SaveKeysView.swift (8739B)
1 // 2 // SaveKeysView.swift 3 // damus 4 // 5 // Created by William Casarin on 2022-05-21. 6 // 7 8 import SwiftUI 9 import Security 10 11 struct SaveKeysView: View { 12 let account: CreateAccountModel 13 let pool: RelayPool = RelayPool(ndb: Ndb()!) 14 @State var loading: Bool = false 15 @State var error: String? = nil 16 17 @State private var credential_handler = CredentialHandler() 18 19 @FocusState var pubkey_focused: Bool 20 @FocusState var privkey_focused: Bool 21 22 let first_contact_event: NdbNote? 23 24 init(account: CreateAccountModel) { 25 self.account = account 26 self.first_contact_event = make_first_contact_event(keypair: account.keypair) 27 } 28 29 var body: some View { 30 ZStack(alignment: .top) { 31 VStack(alignment: .center) { 32 33 Spacer() 34 35 Image("logo-nobg") 36 .resizable() 37 .shadow(color: DamusColors.purple, radius: 2) 38 .frame(width: 56, height: 56, alignment: .center) 39 .padding(.top, 20.0) 40 41 if account.rendered_name.isEmpty { 42 Text("Welcome!", comment: "Text to welcome user.") 43 .font(.title) 44 .fontWeight(.heavy) 45 .foregroundStyle(DamusLogoGradient.gradient) 46 } else { 47 Text("Welcome, \(account.rendered_name)!", comment: "Text to welcome user.") 48 .font(.title) 49 .fontWeight(.heavy) 50 .foregroundStyle(DamusLogoGradient.gradient) 51 } 52 53 Text("Save your login info?", comment: "Ask user if they want to save their account information.") 54 .font(.title) 55 .fontWeight(.heavy) 56 .foregroundColor(DamusColors.neutral6) 57 .padding(.top, 5) 58 59 Text("We'll save your account key, so you won't need to enter it manually next time you log in.", comment: "Reminder to user that they should save their account information.") 60 .font(.system(size: 14)) 61 .foregroundColor(DamusColors.neutral6) 62 .padding(.top, 2) 63 .padding(.bottom, 100) 64 .multilineTextAlignment(.center) 65 66 Spacer() 67 68 if loading { 69 ProgressView() 70 .progressViewStyle(.circular) 71 } else if let err = error { 72 Text("Error: \(err)", comment: "Error message indicating why saving keys failed.") 73 .foregroundColor(.red) 74 75 Button(action: { 76 complete_account_creation(account) 77 }) { 78 HStack { 79 Text("Retry", comment: "Button to retry completing account creation after an error occurred.") 80 .fontWeight(.semibold) 81 } 82 .frame(minWidth: 300, maxWidth: .infinity, maxHeight: 12, alignment: .center) 83 } 84 .buttonStyle(GradientButtonStyle()) 85 .padding(.top, 20) 86 } else { 87 88 Button(action: { 89 save_key(account) 90 complete_account_creation(account) 91 }) { 92 HStack { 93 Text("Save", comment: "Button to save key, complete account creation, and start using the app.") 94 .fontWeight(.semibold) 95 } 96 .frame(minWidth: 300, maxWidth: .infinity, maxHeight: 12, alignment: .center) 97 } 98 .buttonStyle(GradientButtonStyle()) 99 .padding(.top, 20) 100 101 Button(action: { 102 complete_account_creation(account) 103 }) { 104 HStack { 105 Text("Not now", comment: "Button to not save key, complete account creation, and start using the app.") 106 .fontWeight(.semibold) 107 } 108 .frame(minWidth: 300, maxWidth: .infinity, maxHeight: 12, alignment: .center) 109 } 110 .buttonStyle(NeutralButtonStyle(padding: EdgeInsets(top: 15, leading: 15, bottom: 15, trailing: 15), cornerRadius: 12)) 111 .padding(.top, 20) 112 } 113 } 114 .padding(20) 115 } 116 .background(DamusBackground(maxHeight: UIScreen.main.bounds.size.height/2), alignment: .top) 117 .navigationBarBackButtonHidden(true) 118 .navigationBarItems(leading: BackNav()) 119 120 } 121 122 func save_key(_ account: CreateAccountModel) { 123 credential_handler.save_credential(pubkey: account.pubkey, privkey: account.privkey) 124 } 125 126 func complete_account_creation(_ account: CreateAccountModel) { 127 guard let first_contact_event else { 128 error = NSLocalizedString("Could not create your initial contact list event. This is a software bug, please contact Damus support via support@damus.io or through our Nostr account for help.", comment: "Error message to the user indicating that the initial contact list failed to be created.") 129 return 130 } 131 // Save contact list to storage right away so that we don't need to depend on the network to complete this important step 132 self.save_to_storage(first_contact_event: first_contact_event, for: account) 133 134 let bootstrap_relays = load_bootstrap_relays(pubkey: account.pubkey) 135 for relay in bootstrap_relays { 136 add_rw_relay(self.pool, relay) 137 } 138 139 self.pool.register_handler(sub_id: "signup", handler: handle_event) 140 141 self.loading = true 142 143 self.pool.connect() 144 } 145 146 func save_to_storage(first_contact_event: NdbNote, for account: CreateAccountModel) { 147 // Send to NostrDB so that we have a local copy in storage 148 self.pool.send_raw_to_local_ndb(.typical(.event(first_contact_event))) 149 150 // Save the ID to user settings so that we can easily find it later. 151 let settings = UserSettingsStore.globally_load_for(pubkey: account.pubkey) 152 settings.latest_contact_event_id_hex = first_contact_event.id.hex() 153 } 154 155 func handle_event(relay: RelayURL, ev: NostrConnectionEvent) { 156 switch ev { 157 case .ws_event(let wsev): 158 switch wsev { 159 case .connected: 160 let metadata = create_account_to_metadata(account) 161 162 if let keypair = account.keypair.to_full(), 163 let metadata_ev = make_metadata_event(keypair: keypair, metadata: metadata) { 164 self.pool.send(.event(metadata_ev)) 165 } 166 167 if let first_contact_event { 168 self.pool.send(.event(first_contact_event)) 169 } 170 171 do { 172 try save_keypair(pubkey: account.pubkey, privkey: account.privkey) 173 notify(.login(account.keypair)) 174 } catch { 175 self.error = "Failed to save keys" 176 } 177 178 case .error(let err): 179 self.loading = false 180 self.error = String(describing: err) 181 default: 182 break 183 } 184 case .nostr_event(let resp): 185 switch resp { 186 case .notice(let msg): 187 // TODO handle message 188 self.loading = false 189 self.error = msg 190 print(msg) 191 case .event: 192 print("event in signup?") 193 case .eose: 194 break 195 case .ok: 196 break 197 case .auth: 198 break 199 } 200 } 201 } 202 } 203 204 struct SaveKeysView_Previews: PreviewProvider { 205 static var previews: some View { 206 let model = CreateAccountModel(display_name: "William", name: "jb55", about: "I'm me") 207 SaveKeysView(account: model) 208 } 209 } 210 211 func create_account_to_metadata(_ model: CreateAccountModel) -> Profile { 212 return Profile(name: model.name, display_name: model.display_name, about: model.about, picture: model.profile_image?.absoluteString, banner: nil, website: nil, lud06: nil, lud16: nil, nip05: nil, damus_donation: nil) 213 }