Show each address QR in a bottom drawer
This commit is contained in:
@@ -6,7 +6,7 @@ struct AccountsView: View {
|
|||||||
|
|
||||||
@State private var error: String?
|
@State private var error: String?
|
||||||
@State private var busy = false
|
@State private var busy = false
|
||||||
@State private var expandedAddress: String?
|
@State private var qrAddress: AddressQR?
|
||||||
@State private var showAccountPrompt = false
|
@State private var showAccountPrompt = false
|
||||||
@State private var showSubaddressPrompt = false
|
@State private var showSubaddressPrompt = false
|
||||||
@State private var pendingLabel = ""
|
@State private var pendingLabel = ""
|
||||||
@@ -37,6 +37,9 @@ struct AccountsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.task(id: model.moneroReady) { await model.loadAccounts() }
|
.task(id: model.moneroReady) { await model.loadAccounts() }
|
||||||
|
.sheet(item: $qrAddress) { item in
|
||||||
|
AddressQRSheet(title: item.title, address: item.address) { qrAddress = nil }
|
||||||
|
}
|
||||||
.alert("Account name", isPresented: $showAccountPrompt) {
|
.alert("Account name", isPresented: $showAccountPrompt) {
|
||||||
TextField("e.g. Savings", text: $pendingLabel)
|
TextField("e.g. Savings", text: $pendingLabel)
|
||||||
Button("Create") { createAccount() }
|
Button("Create") { createAccount() }
|
||||||
@@ -64,16 +67,14 @@ struct AccountsView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
||||||
addressRow(id: "\(account.index)/primary",
|
addressRow(title: "Primary address",
|
||||||
title: "Primary address",
|
|
||||||
address: account.address,
|
address: account.address,
|
||||||
isUsed: true)
|
isUsed: true)
|
||||||
|
|
||||||
if !account.subaddresses.isEmpty {
|
if !account.subaddresses.isEmpty {
|
||||||
Divider().overlay(Theme.border)
|
Divider().overlay(Theme.border)
|
||||||
ForEach(account.subaddresses.filter { $0.index != 0 }) { subaddress in
|
ForEach(account.subaddresses.filter { $0.index != 0 }) { subaddress in
|
||||||
addressRow(id: subaddress.id,
|
addressRow(title: subaddress.label.isEmpty ? "Address \(subaddress.index)" : subaddress.label,
|
||||||
title: subaddress.label.isEmpty ? "Address \(subaddress.index)" : subaddress.label,
|
|
||||||
address: subaddress.address,
|
address: subaddress.address,
|
||||||
isUsed: subaddress.isUsed,
|
isUsed: subaddress.isUsed,
|
||||||
balance: subaddress.balance)
|
balance: subaddress.balance)
|
||||||
@@ -89,8 +90,10 @@ struct AccountsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addressRow(id: String, title: String, address: String, isUsed: Bool, balance: String? = nil) -> some View {
|
private func addressRow(title: String, address: String, isUsed: Bool, balance: String? = nil) -> some View {
|
||||||
VStack(spacing: 10) {
|
Button {
|
||||||
|
qrAddress = AddressQR(title: title, address: address)
|
||||||
|
} label: {
|
||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
VStack(alignment: .leading, spacing: 2) {
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
Text(title)
|
Text(title)
|
||||||
@@ -108,31 +111,17 @@ struct AccountsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(minLength: 6)
|
Spacer(minLength: 6)
|
||||||
Button {
|
Image(systemName: "qrcode")
|
||||||
expandedAddress = expandedAddress == id ? nil : id
|
.font(.system(size: 14, weight: .semibold))
|
||||||
} label: {
|
.foregroundStyle(Theme.accent)
|
||||||
Image(systemName: expandedAddress == id ? "xmark" : "qrcode")
|
.frame(width: 32, height: 32)
|
||||||
.font(.system(size: 14, weight: .semibold))
|
.background(Theme.surfaceAlt)
|
||||||
.foregroundStyle(Theme.accent)
|
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
|
||||||
.frame(width: 32, height: 32)
|
|
||||||
.background(Theme.surfaceAlt)
|
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if expandedAddress == id {
|
|
||||||
VStack(spacing: 10) {
|
|
||||||
QRCodeView(text: address)
|
|
||||||
.frame(maxWidth: 180)
|
|
||||||
HStack(spacing: 8) {
|
|
||||||
SecondaryButton(title: "Copy") {
|
|
||||||
UIPasteboard.general.string = address
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
}
|
}
|
||||||
|
.contentShape(Rectangle())
|
||||||
}
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.disabled(address.isEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createAccount() {
|
private func createAccount() {
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct AddressQR: Identifiable {
|
||||||
|
let id = UUID()
|
||||||
|
let title: String
|
||||||
|
let address: String
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Drawer that shows one address as a scannable QR, presented from the bottom
|
||||||
|
/// like the transaction detail sheet.
|
||||||
|
struct AddressQRSheet: View {
|
||||||
|
var title: String
|
||||||
|
var address: String
|
||||||
|
var onDone: () -> Void
|
||||||
|
|
||||||
|
@State private var copied = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
Theme.background.ignoresSafeArea()
|
||||||
|
VStack(spacing: 16) {
|
||||||
|
Text(title)
|
||||||
|
.font(.system(size: 16, weight: .semibold))
|
||||||
|
.foregroundStyle(Theme.text)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
|
QRCodeView(text: address)
|
||||||
|
.frame(maxWidth: 220)
|
||||||
|
|
||||||
|
Text(address)
|
||||||
|
.font(.system(size: 12, design: .monospaced))
|
||||||
|
.foregroundStyle(Theme.muted)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.lineLimit(4)
|
||||||
|
.truncationMode(.middle)
|
||||||
|
.textSelection(.enabled)
|
||||||
|
|
||||||
|
SecondaryButton(title: copied ? "Copied" : "Copy address") {
|
||||||
|
UIPasteboard.general.string = address
|
||||||
|
copied = true
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { copied = false }
|
||||||
|
}
|
||||||
|
|
||||||
|
PrimaryButton(title: "Done", action: onDone)
|
||||||
|
}
|
||||||
|
.padding(22)
|
||||||
|
.frame(maxHeight: .infinity, alignment: .top)
|
||||||
|
}
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
|
.presentationDetents([.medium, .large])
|
||||||
|
.presentationDragIndicator(.visible)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user