Files
koins/apps/ios/Koins/Views/WalletSetupViews.swift

142 lines
4.8 KiB
Swift

import SwiftUI
struct CreateWalletView: View {
@EnvironmentObject private var model: AppModel
@Binding var route: Route
@Binding var seedBox: SeedBox?
@State private var name = ""
@State private var localError: String?
var body: some View {
VStack(alignment: .leading, spacing: 18) {
ScreenHeader(title: "Create wallet",
subtitle: "A new \(model.chain.title) wallet on this device",
onBack: { route = .home })
VStack(alignment: .leading, spacing: 6) {
FieldLabel(text: "Wallet name")
KoinsTextField(placeholder: "My \(model.symbol) wallet", text: $name)
}
if model.chain == .xmr {
Card {
Text("The wallet file is created locally and encrypted on this device. Syncing starts right away and can take a while for a new Monero wallet.")
.font(.system(size: 12))
.foregroundStyle(Theme.muted)
}
}
if let message = localError ?? model.errorMessage {
MessageBanner(message: message, isError: true)
}
PrimaryButton(title: model.isBusy ? "Generating…" : "Generate wallet",
disabled: model.isBusy) {
Task {
localError = nil
model.clearMessages()
do {
let seed = try await model.createWallet(name: name)
seedBox = SeedBox(seed: seed, symbol: model.symbol)
route = .dashboard
} catch {
localError = error.localizedDescription
}
}
}
Spacer(minLength: 0)
}
}
}
struct ImportWalletView: View {
@EnvironmentObject private var model: AppModel
@Binding var route: Route
@State private var name = ""
@State private var secret = ""
@State private var restoreHeight = ""
@State private var localError: String?
@State private var tipError: String?
@State private var isLoadingTip = false
var body: some View {
VStack(alignment: .leading, spacing: 18) {
ScreenHeader(title: "Import wallet",
subtitle: "Restore a \(model.chain.title) wallet from its seed",
onBack: { route = .home })
VStack(alignment: .leading, spacing: 6) {
FieldLabel(text: "Wallet name")
KoinsTextField(placeholder: "My \(model.symbol) wallet", text: $name)
}
VStack(alignment: .leading, spacing: 6) {
FieldLabel(text: model.chain == .xrp ? "Recovery phrase" : "Mnemonic seed")
KoinsTextField(placeholder: model.chain == .xrp ? "12 words, or a family seed (sEd…)" : "25 word seed phrase",
text: $secret,
multiline: model.chain == .xmr)
}
if model.chain == .xmr {
VStack(alignment: .leading, spacing: 6) {
FieldLabel(text: "Restore height (optional)")
KoinsTextField(placeholder: "e.g. 3200000", text: $restoreHeight, keyboard: .numberPad)
}
Card {
VStack(alignment: .leading, spacing: 10) {
Text("Without a height the wallet scans the whole chain — millions of blocks, which takes hours on a phone. Set the block your wallet was first used, or start from the current tip if you only care about new funds.")
.font(.system(size: 12))
.foregroundStyle(Theme.muted)
Button {
Task {
tipError = nil
isLoadingTip = true
defer { isLoadingTip = false }
do {
let tip = try await MoneroService.shared.daemonHeight()
restoreHeight = String(tip)
} catch {
tipError = error.localizedDescription
}
}
} label: {
Text(isLoadingTip ? "Fetching chain tip…" : "Use the current chain tip")
.font(.system(size: 13, weight: .medium))
.foregroundStyle(Theme.accent)
}
.disabled(isLoadingTip)
if let tipError {
Text(tipError)
.font(.system(size: 11))
.foregroundStyle(Theme.danger)
}
}
}
}
if let message = localError ?? model.errorMessage {
MessageBanner(message: message, isError: true)
}
PrimaryButton(title: model.isBusy ? "Importing…" : "Import wallet",
disabled: model.isBusy) {
Task {
localError = nil
model.clearMessages()
do {
let height = UInt64(restoreHeight.trimmingCharacters(in: .whitespaces))
try await model.importWallet(name: name, secret: secret, restoreHeight: height)
route = .dashboard
} catch {
localError = error.localizedDescription
}
}
}
Spacer(minLength: 0)
}
}
}