SettingsView.swift (4864B)
1 import SwiftUI 2 3 struct SettingsView: View { 4 @Environment(NYTAuthService.self) private var nytAuth 5 @Environment(PlayerPreferences.self) private var preferences 6 @Environment(\.dismiss) private var dismiss 7 @Environment(\.resetDatabase) private var resetDatabase 8 9 @AppStorage("debugMode") private var debugMode = false 10 @State private var showingNYTLogin = false 11 @State private var showResetConfirmation = false 12 @State private var externalSource: ExternalSource? 13 @State private var easterEggTaps = 0 14 15 var body: some View { 16 @Bindable var preferences = preferences 17 NavigationStack { 18 Form { 19 Section("External Source") { 20 Picker("Publisher", selection: $externalSource) { 21 Text("None").tag(nil as ExternalSource?) 22 ForEach(ExternalSource.allCases) { source in 23 Text(source.title).tag(source as ExternalSource?) 24 } 25 } 26 .pickerStyle(.menu) 27 28 switch externalSource { 29 case nil: 30 EmptyView() 31 case .newYorkTimes: 32 if nytAuth.isSignedIn { 33 signedInView 34 } else { 35 signInView 36 } 37 } 38 } 39 40 if debugMode { 41 Section("Debugging") { 42 Toggle("Enable iCloud Sync", isOn: $preferences.isICloudSyncEnabled) 43 44 NavigationLink("iCloud Diagnostics") { 45 DiagnosticsView() 46 } 47 NavigationLink("Share Diagnostics") { 48 ShareDiagnosticsView() 49 } 50 NavigationLink("Record Editor") { 51 RecordEditorView() 52 } 53 54 Button("Reset Database", role: .destructive) { 55 showResetConfirmation = true 56 } 57 .alert( 58 "Delete all games?", 59 isPresented: $showResetConfirmation 60 ) { 61 Button("Delete All Games", role: .destructive) { 62 Task { await resetDatabase?() } 63 } 64 Button("Cancel", role: .cancel) {} 65 } message: { 66 Text("This removes every game and clears the sync state on this device. Games on other devices and in iCloud are not affected.") 67 } 68 } 69 } 70 71 Section { 72 NavigationLink("About") { 73 AboutView() 74 } 75 } footer: { 76 Text("Made in Tokyo from natural ones and zeros") 77 .frame(maxWidth: .infinity) 78 .multilineTextAlignment(.center) 79 .padding(.top, 24) 80 .onTapGesture { 81 easterEggTaps += 1 82 if easterEggTaps >= 4 { 83 debugMode.toggle() 84 easterEggTaps = 0 85 } 86 } 87 } 88 } 89 .navigationTitle("Settings") 90 .navigationBarTitleDisplayMode(.inline) 91 .toolbar { 92 ToolbarItem(placement: .confirmationAction) { 93 Button("Done") { dismiss() } 94 } 95 } 96 .sheet(isPresented: $showingNYTLogin) { 97 NYTLoginView() 98 } 99 } 100 } 101 102 // MARK: - Subviews 103 104 private enum ExternalSource: String, CaseIterable, Identifiable { 105 case newYorkTimes 106 107 var id: String { rawValue } 108 109 var title: String { 110 switch self { 111 case .newYorkTimes: "New York Times" 112 } 113 } 114 } 115 116 @ViewBuilder 117 private var signedInView: some View { 118 if let email = nytAuth.signedInEmail { 119 LabeledContent("E-mail", value: email) 120 } else { 121 LabeledContent("Status", value: "Signed in") 122 } 123 Button("Sign Out", role: .destructive) { 124 nytAuth.signOut() 125 } 126 } 127 128 @ViewBuilder 129 private var signInView: some View { 130 if let errorMessage = nytAuth.errorMessage { 131 Text(errorMessage) 132 .foregroundStyle(.red) 133 .font(.caption) 134 } 135 Button("Sign In with NYT") { 136 showingNYTLogin = true 137 } 138 } 139 }