damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

KeySettingsView.swift (4736B)


      1 //
      2 //  KeySettingsView.swift
      3 //  damus
      4 //
      5 //  Created by William Casarin on 2023-04-05.
      6 //
      7 
      8 import SwiftUI
      9 import LocalAuthentication
     10 
     11 struct KeySettingsView: View {
     12     let keypair: Keypair
     13     
     14     @State var privkey: String
     15     @State var privkey_copied: Bool = false
     16     @State var pubkey_copied: Bool = false
     17     @State var show_privkey: Bool = false
     18     @State var has_authenticated_locally: Bool = false
     19     
     20     @Environment(\.dismiss) var dismiss
     21     
     22     init(keypair: Keypair) {
     23         _privkey = State(initialValue: keypair.privkey?.nsec ?? "")
     24         self.keypair = keypair
     25     }
     26     
     27     var ShowSecToggle: some View {
     28         Toggle(NSLocalizedString("Show", comment: "Toggle to show or hide user's secret account login key."), isOn: $show_privkey)
     29             .onChange(of: show_privkey) { newValue in
     30                 if newValue {
     31                     authenticate_locally(has_authenticated_locally) { success in
     32                         self.has_authenticated_locally = success
     33                         self.show_privkey = success
     34                     }
     35                 }
     36             }
     37     }
     38     
     39     // TODO: (jb55) could be more general but not gonna worry about it atm
     40     func CopyButton(is_pk: Bool) -> some View {
     41         return Button(action: {
     42             let copyKey = {
     43                 UIPasteboard.general.string = is_pk ? self.keypair.pubkey.npub : self.privkey
     44                 self.privkey_copied = !is_pk
     45                 self.pubkey_copied = is_pk
     46     
     47                 let generator = UIImpactFeedbackGenerator(style: .light)
     48                 generator.impactOccurred()
     49             }
     50             
     51             if is_pk {
     52                 copyKey()
     53                 return
     54             }
     55             
     56             if has_authenticated_locally {
     57                 copyKey()
     58                 return
     59             }
     60             
     61             authenticate_locally(has_authenticated_locally) { success in
     62                 self.has_authenticated_locally = success
     63                 if success {
     64                     copyKey()
     65                 }
     66             }
     67         }) {
     68             let copied = is_pk ? self.pubkey_copied : self.privkey_copied
     69             Image(copied ? "check-circle" : "copy2")
     70         }
     71     }
     72     
     73     var body: some View {
     74         Form {
     75             Section(NSLocalizedString("Public Account ID", comment: "Section title for the user's public account ID.")) {
     76                 HStack {
     77                     Text(keypair.pubkey.npub)
     78 
     79                     CopyButton(is_pk: true)
     80                 }
     81                 .clipShape(RoundedRectangle(cornerRadius: 5))
     82             }
     83             
     84             if let sec = keypair.privkey?.nsec {
     85                 Section(NSLocalizedString("Secret Account Login Key", comment: "Section title for user's secret account login key.")) {
     86                     HStack {
     87                         if show_privkey == false || !has_authenticated_locally {
     88                             SecureField(NSLocalizedString("Private Key", comment: "Title of the secure field that holds the user's private key."), text: $privkey)
     89                                 .disabled(true)
     90                         } else {
     91                             Text(sec)
     92                                 .clipShape(RoundedRectangle(cornerRadius: 5))
     93                         }
     94                         
     95                         CopyButton(is_pk: false)
     96                     }
     97                     
     98                     ShowSecToggle
     99                 }
    100             }
    101             
    102         }
    103         .navigationTitle(NSLocalizedString("Keys", comment: "Navigation title for managing keys."))
    104         .onReceive(handle_notify(.switched_timeline)) { _ in
    105             dismiss()
    106         }
    107     }
    108 }
    109 
    110 struct KeySettingsView_Previews: PreviewProvider {
    111     static var previews: some View {
    112         let kp = generate_new_keypair()
    113         KeySettingsView(keypair: kp.to_keypair())
    114     }
    115 }
    116 
    117 func authenticate_locally(_ has_authenticated_locally: Bool, completion: @escaping (Bool) -> Void) {
    118     // Need to authenticate only once while ConfigView is presented
    119     guard !has_authenticated_locally else {
    120         completion(true)
    121         return
    122     }
    123     let context = LAContext()
    124     if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) {
    125         context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: NSLocalizedString("Local authentication to access private key", comment: "Face ID usage description shown when trying to access private key")) { success, error in
    126             DispatchQueue.main.async {
    127                 completion(success)
    128             }
    129         }
    130     } else {
    131         // If there's no authentication set up on the device, let the user copy the key without it
    132         completion(true)
    133     }
    134 }
    135