Files
damus/damus/Views/SaveKeysView.swift
Ben Weeks 973e9fe714 Custom iconography added for other areas of the app.
Changelog-Added: Custom iconography added for other areas of the app.
2023-05-29 14:47:05 -07:00

244 lines
9.7 KiB
Swift

//
// SaveKeysView.swift
// damus
//
// Created by William Casarin on 2022-05-21.
//
import SwiftUI
import Security
struct SaveKeysView: View {
let account: CreateAccountModel
let pool: RelayPool = RelayPool()
@State var pub_copied: Bool = false
@State var priv_copied: Bool = false
@State var loading: Bool = false
@State var error: String? = nil
@State private var credential_handler = CredentialHandler()
@FocusState var pubkey_focused: Bool
@FocusState var privkey_focused: Bool
var body: some View {
ZStack(alignment: .top) {
VStack(alignment: .center) {
Text("Welcome, \(account.rendered_name)!", comment: "Text to welcome user.")
.font(.title.bold())
.padding(.bottom, 10)
Text("Before we get started, you'll need to save your account info, otherwise you won't be able to login in the future if you ever uninstall Damus.", comment: "Reminder to user that they should save their account information.")
.padding(.bottom, 10)
Text("Public Key", comment: "Label to indicate that text below is the user's public key used by others to uniquely refer to the user.")
.font(.title2.bold())
.padding(.bottom, 10)
Text("This is your account ID, you can give this to your friends so that they can follow you. Tap to copy.", comment: "Label to describe that a public key is the user's account ID and what they can do with it.")
.padding(.bottom, 10)
SaveKeyView(text: account.pubkey_bech32, textContentType: .username, is_copied: $pub_copied, focus: $pubkey_focused)
.padding(.bottom, 10)
if pub_copied {
Text("Private Key", comment: "Label to indicate that the text below is the user's private key used by only the user themself as a secret to login to access their account.")
.font(.title2.bold())
.padding(.bottom, 10)
Text("This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!", comment: "Label to describe that a private key is the user's secret account key and what they should do with it.")
.padding(.bottom, 10)
SaveKeyView(text: account.privkey_bech32, textContentType: .newPassword, is_copied: $priv_copied, focus: $privkey_focused)
.padding(.bottom, 10)
}
if pub_copied && priv_copied {
if loading {
ProgressView()
.progressViewStyle(.circular)
} else if let err = error {
Text("Error: \(err)", comment: "Error message indicating why saving keys failed.")
.foregroundColor(.red)
Button(action: {
complete_account_creation(account)
}) {
HStack {
Text("Retry", comment: "Button to retry completing account creation after an error occurred.")
.fontWeight(.semibold)
}
.frame(minWidth: 300, maxWidth: .infinity, maxHeight: 12, alignment: .center)
}
.buttonStyle(GradientButtonStyle())
.padding(.top, 20)
} else {
Button(action: {
complete_account_creation(account)
}) {
HStack {
Text("Let's go!", comment: "Button to complete account creation and start using the app.")
.fontWeight(.semibold)
}
.frame(minWidth: 300, maxWidth: .infinity, maxHeight: 12, alignment: .center)
}
.buttonStyle(GradientButtonStyle())
.padding(.top, 20)
}
}
}
.padding(20)
}
.background(
Image("eula-bg")
.resizable()
.blur(radius: 70)
.ignoresSafeArea(),
alignment: .top
)
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: BackNav())
.onAppear {
// Hack to force keyboard to show up for a short moment and then hiding it to register password autofill flow.
pubkey_focused = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
pubkey_focused = false
}
}
}
func complete_account_creation(_ account: CreateAccountModel) {
let bootstrap_relays = load_bootstrap_relays(pubkey: account.pubkey)
for relay in bootstrap_relays {
add_rw_relay(self.pool, relay)
}
self.pool.register_handler(sub_id: "signup", handler: handle_event)
credential_handler.save_credential(pubkey: account.pubkey_bech32, privkey: account.privkey_bech32)
self.loading = true
self.pool.connect()
}
func handle_event(relay: String, ev: NostrConnectionEvent) {
switch ev {
case .ws_event(let wsev):
switch wsev {
case .connected:
let metadata = create_account_to_metadata(account)
let contacts_ev = make_first_contact_event(keypair: account.keypair)
if let keypair = account.keypair.to_full() {
let metadata_ev = make_metadata_event(keypair: keypair, metadata: metadata)
self.pool.send(.event(metadata_ev))
}
if let contacts_ev {
self.pool.send(.event(contacts_ev))
}
do {
try save_keypair(pubkey: account.pubkey, privkey: account.privkey)
notify(.login, account.keypair)
} catch {
self.error = "Failed to save keys"
}
case .error(let err):
self.loading = false
self.error = String(describing: err)
default:
break
}
case .nostr_event(let resp):
switch resp {
case .notice(let msg):
// TODO handle message
self.loading = false
self.error = msg
print(msg)
case .event:
print("event in signup?")
case .eose:
break
case .ok:
break
}
}
}
}
struct SaveKeyView: View {
let text: String
let textContentType: UITextContentType
@Binding var is_copied: Bool
var focus: FocusState<Bool>.Binding
func copy_text() {
UIPasteboard.general.string = text
is_copied = true
}
var body: some View {
HStack {
Spacer()
VStack {
spacerBlock(width: 0, height: 0)
Button(action: copy_text) {
Label("", image: is_copied ? "check-circle.fill" : "copy2")
.foregroundColor(is_copied ? .green : .gray)
.background {
if is_copied {
Circle()
.foregroundColor(.white)
.frame(width: 25, height: 25, alignment: .center)
.padding(.leading, -8)
.padding(.top, 1)
} else {
EmptyView()
}
}
}
}
TextField("", text: .constant(text))
.padding(5)
.background {
RoundedRectangle(cornerRadius: 4.0).opacity(0.1)
}
.textSelection(.enabled)
.font(.callout.monospaced())
.onTapGesture {
copy_text()
// Hack to force keyboard to hide. Showing keyboard on text field is necessary to register password autofill flow but the text itself should not be modified.
DispatchQueue.main.async {
end_editing()
}
}
.textContentType(textContentType)
.deleteDisabled(true)
.focused(focus)
spacerBlock(width: 0, height: 0) /// set a 'width' > 0 here to vary key Text's aspect ratio
}
}
@ViewBuilder private func spacerBlock(width: CGFloat, height: CGFloat) -> some View {
Color.orange.opacity(1)
.frame(width: width, height: height)
}
}
struct SaveKeysView_Previews: PreviewProvider {
static var previews: some View {
let model = CreateAccountModel(real: "William", nick: "jb55", about: "I'm me")
SaveKeysView(account: model)
}
}
func create_account_to_metadata(_ model: CreateAccountModel) -> Profile {
return Profile(name: model.nick_name, display_name: model.real_name, about: model.about, picture: model.profile_image, banner: nil, website: nil, lud06: nil, lud16: nil, nip05: nil, damus_donation: nil)
}