diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index e952a6ad..a50e9eb8 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -640,6 +640,7 @@ D7EDED332B12ACAE0018B19C /* DamusUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EDED322B12ACAE0018B19C /* DamusUserDefaults.swift */; }; D7EDED342B12ACAE0018B19C /* DamusUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EDED322B12ACAE0018B19C /* DamusUserDefaults.swift */; }; D7FB10A72B0C371A00FA8D42 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C2B10272A7B0F5C008AA43E /* Log.swift */; }; + D7FD12262BD345A700CF195B /* FirstAidSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FD12252BD345A700CF195B /* FirstAidSettingsView.swift */; }; D7FF94002AC7AC5300FD969D /* RelayURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FF93FF2AC7AC5200FD969D /* RelayURL.swift */; }; E02429952B7E97740088B16C /* CameraController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E02429942B7E97740088B16C /* CameraController.swift */; }; E02B54182B4DFADA0077FF42 /* Bech32ObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E02B54172B4DFADA0077FF42 /* Bech32ObjectTests.swift */; }; @@ -1434,6 +1435,7 @@ D7EDED202B117DCA0018B19C /* SequenceUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SequenceUtils.swift; sourceTree = ""; }; D7EDED2D2B128E8A0018B19C /* CollectionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionExtension.swift; sourceTree = ""; }; D7EDED322B12ACAE0018B19C /* DamusUserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusUserDefaults.swift; sourceTree = ""; }; + D7FD12252BD345A700CF195B /* FirstAidSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstAidSettingsView.swift; sourceTree = ""; }; D7FF93FF2AC7AC5200FD969D /* RelayURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayURL.swift; sourceTree = ""; }; E02429942B7E97740088B16C /* CameraController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraController.swift; sourceTree = ""; }; E02B54172B4DFADA0077FF42 /* Bech32ObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bech32ObjectTests.swift; sourceTree = ""; }; @@ -1725,6 +1727,7 @@ 4C1A9A2629DDE31900516EAC /* TranslationSettingsView.swift */, E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */, 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */, + D7FD12252BD345A700CF195B /* FirstAidSettingsView.swift */, ); path = Settings; sourceTree = ""; @@ -3110,6 +3113,7 @@ D7EDED1E2B11797D0018B19C /* LongformEvent.swift in Sources */, 504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */, 3A8CC6CC2A2CFEF900940F5F /* StringUtil.swift in Sources */, + D7FD12262BD345A700CF195B /* FirstAidSettingsView.swift in Sources */, D7870BC12AC4750B0080BA88 /* MentionView.swift in Sources */, 4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */, 4C363AA228296A7E006E126D /* SearchView.swift in Sources */, diff --git a/damus/Util/Router.swift b/damus/Util/Router.swift index 0a341d9b..b737446b 100644 --- a/damus/Util/Router.swift +++ b/damus/Util/Router.swift @@ -30,6 +30,7 @@ enum Route: Hashable { case ReactionsSettings(settings: UserSettingsStore) case SearchSettings(settings: UserSettingsStore) case DeveloperSettings(settings: UserSettingsStore) + case FirstAidSettings(settings: UserSettingsStore) case Thread(thread: ThreadModel) case Reposts(reposts: EventsModel) case QuoteReposts(quotes: EventsModel) @@ -89,6 +90,8 @@ enum Route: Hashable { SearchSettingsView(settings: settings) case .DeveloperSettings(let settings): DeveloperSettingsView(settings: settings) + case .FirstAidSettings(settings: let settings): + FirstAidSettingsView(damus_state: damusState, settings: settings) case .Thread(let thread): ThreadView(state: damusState, thread: thread) case .Reposts(let reposts): @@ -175,6 +178,8 @@ enum Route: Hashable { hasher.combine("searchSettings") case .DeveloperSettings: hasher.combine("developerSettings") + case .FirstAidSettings: + hasher.combine("firstAidSettings") case .Thread(let threadModel): hasher.combine("thread") hasher.combine(threadModel.event.id) diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift index 6ffaf066..c6e331c3 100644 --- a/damus/Views/ConfigView.swift +++ b/damus/Views/ConfigView.swift @@ -67,6 +67,10 @@ struct ConfigView: View { NavigationLink(value: Route.DeveloperSettings(settings: settings)) { IconLabel(NSLocalizedString("Developer", comment: "Section header for developer settings"), img_name: "magic-stick2.fill", color: DamusColors.adaptableBlack) } + + NavigationLink(value: Route.FirstAidSettings(settings: settings)) { + IconLabel(NSLocalizedString("First Aid", comment: "Section header for first aid tools and settings"), img_name: "help2", color: .red) + } } Section(NSLocalizedString("Sign Out", comment: "Section title for signing out")) { diff --git a/damus/Views/Settings/FirstAidSettingsView.swift b/damus/Views/Settings/FirstAidSettingsView.swift new file mode 100644 index 00000000..2be6d0d2 --- /dev/null +++ b/damus/Views/Settings/FirstAidSettingsView.swift @@ -0,0 +1,84 @@ +// +// FirstAidSettingsView.swift +// damus +// +// Created by Daniel D’Aquino on 2024-04-19. +// + +import SwiftUI + +struct FirstAidSettingsView: View { + let damus_state: DamusState + @ObservedObject var settings: UserSettingsStore + @State var reset_contact_list_state: ContactListResetState = .not_started + + enum ContactListResetState: Equatable { + case not_started + case confirming_with_user + case error(String) + case in_progress + case completed + } + + + var body: some View { + Form { + if damus_state.contacts.event == nil { + Section( + header: Text(NSLocalizedString("Contact list (Follows + Relay list)", comment: "Section title for Contact list first aid tools")), + footer: Text(NSLocalizedString("No contact list was found. You might experience issues using the app. If you suspect you have permanently lost your contact list (or if you never had one), you can fix this by resetting it", comment: "Section footer for Contact list first aid tools")) + ) { + Button(action: { + reset_contact_list_state = .confirming_with_user + }, label: { + HStack(spacing: 6) { + switch reset_contact_list_state { + case .not_started, .error: + Label(NSLocalizedString("Reset contact list", comment: "Button to reset contact list."), image: "broom") + .frame(maxWidth: .infinity, alignment: .leading) + .foregroundColor(.red) + case .confirming_with_user, .in_progress: + ProgressView() + Text(NSLocalizedString("In progress…", comment: "Loading message indicating that a contact list reset operation is in progress.")) + case .completed: + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.green) + Text(NSLocalizedString("Contact list has been reset", comment: "Message indicating that the contact list was successfully reset.")) + } + } + }) + .disabled(reset_contact_list_state == .in_progress || reset_contact_list_state == .completed) + + if case let .error(error_message) = reset_contact_list_state { + Text(error_message) + .foregroundStyle(.red) + } + } + .alert(NSLocalizedString("WARNING:\n\nThis will reset your contact list, including the list of everyone you follow and the list of all relays you usually connect to. ONLY PROCEED IF YOU ARE SURE YOU HAVE LOST YOUR CONTACT LIST BEYOND RECOVERABILITY.", comment: "Alert for resetting the user's contact list."), + isPresented: Binding(get: { reset_contact_list_state == .confirming_with_user }, set: { _ in return }) + ) { + Button(NSLocalizedString("Cancel", comment: "Cancel resetting the contact list."), role: .cancel) { + reset_contact_list_state = .not_started + } + Button(NSLocalizedString("Continue", comment: "Continue with resetting the contact list.")) { + guard let new_contact_list_event = make_first_contact_event(keypair: damus_state.keypair) else { + reset_contact_list_state = .error(NSLocalizedString("An unexpected error happened while trying to create the new contact list. Please contact support.", comment: "Error message for a failed contact list reset operation")) + return + } + damus_state.pool.send(.event(new_contact_list_event)) + reset_contact_list_state = .completed + } + } + } + + if damus_state.contacts.event != nil { + Text(NSLocalizedString("We did not detect any issues that we can automatically fix for you. If you are having issues, please contact Damus support", comment: "Message indicating that no First Aid actions are available.")) + } + } + .navigationTitle(NSLocalizedString("First Aid", comment: "Navigation title for first aid settings and tools")) + } +} + +#Preview { + FirstAidSettingsView(damus_state: test_damus_state, settings: test_damus_state.settings) +}