NewGameSheet.swift (3527B)
1 import SwiftUI 2 3 struct NewGameSheet: View { 4 let store: GameStore 5 6 @Environment(\.dismiss) private var dismiss 7 @Environment(NYTAuthService.self) private var nytAuth 8 @AppStorage("lastPuzzleSource") private var storedSource: PuzzleSource = .bundled 9 @AppStorage("debugMode") private var debugMode = false 10 @State private var selection: PuzzleSource = .bundled 11 @State private var duplicateSource: String? 12 @State private var createError: String? 13 14 private var availableSources: [PuzzleSource] { 15 var sources: [PuzzleSource] = [.bundled] 16 if debugMode { 17 sources.append(.debug) 18 } 19 sources.append(.imported) 20 if nytAuth.isSignedIn { 21 sources.append(.nyt) 22 } 23 return sources 24 } 25 26 var body: some View { 27 NavigationStack { 28 VStack(spacing: 0) { 29 Picker("Source", selection: $selection) { 30 ForEach(availableSources) { source in 31 Text(source.title).tag(source) 32 } 33 } 34 .pickerStyle(.segmented) 35 .padding() 36 37 Group { 38 switch selection { 39 case .bundled: 40 BundledBrowseView(onSelected: handleSelected) 41 case .debug: 42 DebugBrowseView(onSelected: handleSelected) 43 case .imported: 44 ImportedBrowseView(onSelected: handleSelected) 45 case .nyt: 46 NYTBrowseView(onSelected: handleSelected) 47 } 48 } 49 } 50 .navigationTitle("New Puzzle") 51 .navigationBarTitleDisplayMode(.inline) 52 .toolbar { 53 ToolbarItem(placement: .cancellationAction) { 54 Button("Cancel") { dismiss() } 55 } 56 } 57 } 58 .onAppear { 59 selection = availableSources.contains(storedSource) ? storedSource : .bundled 60 } 61 .onChange(of: selection) { _, newValue in 62 storedSource = newValue 63 } 64 .alert( 65 "Puzzle Already in Library", 66 isPresented: .init( 67 get: { duplicateSource != nil }, 68 set: { if !$0 { duplicateSource = nil } } 69 ), 70 presenting: duplicateSource 71 ) { source in 72 Button("Create Copy") { 73 create(from: source) 74 } 75 Button("Cancel", role: .cancel) {} 76 } message: { _ in 77 Text("You already have a game for this puzzle. Cancel and resume it from the library, or create a new copy.") 78 } 79 .alert( 80 "Couldn't Create Game", 81 isPresented: .init( 82 get: { createError != nil }, 83 set: { if !$0 { createError = nil } } 84 ), 85 presenting: createError 86 ) { _ in 87 Button("OK", role: .cancel) {} 88 } message: { message in 89 Text(message) 90 } 91 } 92 93 private func handleSelected(_ source: String) { 94 if store.findGameID(matching: source) != nil { 95 duplicateSource = source 96 } else { 97 create(from: source) 98 } 99 } 100 101 private func create(from source: String) { 102 do { 103 _ = try store.createGame(from: source) 104 dismiss() 105 } catch { 106 createError = error.localizedDescription 107 } 108 } 109 }