142 lines
4.8 KiB
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)
|
|
}
|
|
}
|
|
} |