ConfigView.swift (11250B)
1 // 2 // ConfigView.swift 3 // damus 4 // 5 // Created by William Casarin on 2022-06-09. 6 // 7 import AVFoundation 8 import Kingfisher 9 import SwiftUI 10 import LocalAuthentication 11 import Combine 12 13 struct ConfigView: View { 14 let state: DamusState 15 @Environment(\.colorScheme) var colorScheme 16 @Environment(\.dismiss) var dismiss 17 @State var confirm_logout: Bool = false 18 @State var delete_account_warning: Bool = false 19 @State var confirm_delete_account: Bool = false 20 @State var delete_text: String = "" 21 @State private var searchText: String = "" 22 23 @ObservedObject var settings: UserSettingsStore 24 25 // String constants 26 private let DELETE_KEYWORD = "DELETE" 27 private let keysTitle = NSLocalizedString("Keys", comment: "Settings section for managing keys") 28 private let appearanceTitle = NSLocalizedString("Appearance and filters", comment: "Section header for text, appearance, and content filter settings") 29 private let searchUniverseTitle = NSLocalizedString("Search / Universe", comment: "Section header for search/universe settings") 30 private let notificationsTitle = NSLocalizedString("Notifications", comment: "Section header for Damus notifications") 31 private let zapsTitle = NSLocalizedString("Zaps", comment: "Section header for zap settings") 32 private let translationTitle = NSLocalizedString("Translation", comment: "Section header for text and appearance settings") 33 private let reactionsTitle = NSLocalizedString("Reactions", comment: "Section header for reactions settings") 34 private let developerTitle = NSLocalizedString("Developer", comment: "Section header for developer settings") 35 private let firstAidTitle = NSLocalizedString("First Aid", comment: "Section header for first aid tools and settings") 36 private let signOutTitle = NSLocalizedString("Sign out", comment: "Sidebar menu label to sign out of the account.") 37 private let deleteAccountTitle = NSLocalizedString("Delete Account", comment: "Button to delete the user's account.") 38 private let versionTitle = NSLocalizedString("Version", comment: "Section title for displaying the version number of the Damus app.") 39 private let copyString = NSLocalizedString("Copy", comment: "Context menu option for copying the version of damus.") 40 41 init(state: DamusState) { 42 self.state = state 43 _settings = ObservedObject(initialValue: state.settings) 44 } 45 46 func textColor() -> Color { 47 colorScheme == .light ? DamusColors.black : DamusColors.white 48 } 49 50 func showSettingsButton(title : String)->Bool{ 51 return searchText.isEmpty || title.lowercased().contains(searchText.lowercased()) 52 } 53 54 var body: some View { 55 ZStack(alignment: .leading) { 56 Form { 57 Section { 58 // Keys 59 if showSettingsButton(title: keysTitle){ 60 NavigationLink(value:Route.KeySettings(keypair: state.keypair)){ 61 IconLabel(keysTitle,img_name:"Key",color:.purple) 62 } 63 } 64 // Appearance and filters 65 if showSettingsButton(title: appearanceTitle){ 66 NavigationLink(value:Route.AppearanceSettings(settings: settings)){ 67 IconLabel(appearanceTitle,img_name:"eye",color:.red) 68 } 69 } 70 // Search/Universe 71 if showSettingsButton(title: searchUniverseTitle){ 72 NavigationLink(value: Route.SearchSettings(settings: settings)){ 73 IconLabel(searchUniverseTitle,img_name:"search",color:.red) 74 } 75 } 76 77 //Notifications 78 if showSettingsButton(title: notificationsTitle){ 79 NavigationLink(value: Route.NotificationSettings(settings: settings)){ 80 IconLabel(notificationsTitle,img_name:"notification-bell-on",color:.blue) 81 } 82 } 83 //Zaps 84 if showSettingsButton(title: zapsTitle){ 85 NavigationLink(value: Route.ZapSettings(settings: settings)){ 86 IconLabel(zapsTitle,img_name:"zap.fill",color:.orange) 87 } 88 } 89 //Translation 90 if showSettingsButton(title: translationTitle){ 91 NavigationLink(value: Route.TranslationSettings(settings: settings)){ 92 IconLabel(translationTitle,img_name:"globe",color:.green) 93 } 94 } 95 //Reactions 96 if showSettingsButton(title: reactionsTitle){ 97 NavigationLink(value: Route.ReactionsSettings(settings: settings)){ 98 IconLabel(reactionsTitle,img_name:"shaka.fill",color:.purple) 99 } 100 } 101 //Developer 102 if showSettingsButton(title: developerTitle){ 103 NavigationLink(value: Route.DeveloperSettings(settings: settings)){ 104 IconLabel(developerTitle,img_name:"magic-stick2.fill",color:DamusColors.adaptableBlack) 105 } 106 } 107 //First Aid 108 if showSettingsButton(title: firstAidTitle){ 109 NavigationLink(value: Route.FirstAidSettings(settings: settings)){ 110 IconLabel(firstAidTitle,img_name:"help2",color: .red) 111 } 112 } 113 } 114 //Sign out Section 115 if showSettingsButton(title: signOutTitle){ 116 Section(signOutTitle){ 117 Button(action: { 118 if state.keypair.privkey == nil { 119 logout(state) 120 } else { 121 confirm_logout = true 122 } 123 }, label: { 124 Label(signOutTitle, image: "logout") 125 .foregroundColor(textColor()) 126 .frame(maxWidth: .infinity, alignment: .leading) 127 }) 128 } 129 } 130 // Delete Account 131 if showSettingsButton(title: deleteAccountTitle){ 132 if state.is_privkey_user { 133 Section(header: Text("Permanently Delete Account", comment: "Section title for deleting the user")) { 134 Button(action: { 135 delete_account_warning = true 136 }, label: { 137 Label(deleteAccountTitle, image: "delete") 138 .frame(maxWidth: .infinity, alignment: .leading) 139 .foregroundColor(.red) 140 }) 141 } 142 } 143 } 144 // Version info 145 if showSettingsButton(title: versionTitle) { 146 Section( 147 header: Text(versionTitle), 148 footer: Text("").padding(.bottom, tabHeight + getSafeAreaBottom()) 149 ) { 150 Text(verbatim: VersionInfo.version) 151 .contextMenu { 152 Button { 153 UIPasteboard.general.string = VersionInfo.version 154 } label: { 155 Label(copyString, image: "copy2") 156 } 157 } 158 } 159 } 160 } 161 } 162 .navigationTitle(NSLocalizedString("Settings", comment: "Navigation title for Settings view.")) 163 .navigationBarTitleDisplayMode(.large) 164 .searchable(text: $searchText, prompt: NSLocalizedString("Search within settings", comment: "Text to prompt the user to search settings.")) 165 .alert(NSLocalizedString("WARNING:\n\nTHIS WILL SIGN AN EVENT THAT DELETES THIS ACCOUNT.\n\nYOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.\n\n ARE YOU SURE YOU WANT TO CONTINUE?", comment: "Alert for deleting the users account."), isPresented: $delete_account_warning) { 166 167 Button(NSLocalizedString("Cancel", comment: "Cancel deleting the user."), role: .cancel) { 168 delete_account_warning = false 169 } 170 Button(NSLocalizedString("Continue", comment: "Continue with deleting the user.")) { 171 confirm_delete_account = true 172 } 173 } 174 .alert(NSLocalizedString("Permanently Delete Account", comment: "Alert for deleting the users account."), isPresented: $confirm_delete_account) { 175 TextField(String(format: NSLocalizedString("Type %@ to delete", comment: "Text field prompt asking user to type DELETE in all caps to confirm that they want to proceed with deleting their account."), DELETE_KEYWORD), text: $delete_text) 176 Button(NSLocalizedString("Cancel", comment: "Cancel deleting the user."), role: .cancel) { 177 confirm_delete_account = false 178 } 179 Button(NSLocalizedString("Delete", comment: "Button for deleting the users account."), role: .destructive) { 180 guard let keypair = state.keypair.to_full(), 181 delete_text == DELETE_KEYWORD, 182 let ev = created_deleted_account_profile(keypair: keypair) else { 183 return 184 } 185 state.nostrNetwork.postbox.send(ev) 186 logout(state) 187 } 188 } 189 .alert(NSLocalizedString("Logout", comment: "Alert for logging out the user."), isPresented: $confirm_logout) { 190 Button(NSLocalizedString("Cancel", comment: "Cancel out of logging out the user."), role: .cancel) { 191 confirm_logout = false 192 } 193 Button(NSLocalizedString("Logout", comment: "Button for logging out the user."), role: .destructive) { 194 logout(state) 195 } 196 } message: { 197 Text("Make sure your nsec account key is saved before you logout or you will lose access to this account", comment: "Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out.") 198 } 199 .onReceive(handle_notify(.switched_timeline)) { _ in 200 dismiss() 201 } 202 } 203 } 204 205 struct ConfigView_Previews: PreviewProvider { 206 static var previews: some View { 207 NavigationView { 208 ConfigView(state: test_damus_state) 209 } 210 } 211 } 212 213 214 func handle_string_amount(new_value: String) -> Int? { 215 let filtered = new_value.filter { 216 $0.isNumber 217 } 218 219 if filtered == "" { 220 return nil 221 } 222 223 guard let amt = NumberFormatter().number(from: filtered) as? Int else { 224 return nil 225 } 226 227 return amt 228 }