Replaced the transaction screen's balance card with a hero header featuring a wallet picker dropdown overlay that supports switching wallets, adding new wallets, and editing/deleting wallets — moving all wallet management out of Settings. Added wallet identicons (unique color grids per wallet) to the picker and header, and added a "Wallet Info" option to the transaction screen's menu. Improved the Electrum connection status to show "Connected" as soon as the chain tip is fetched rather than waiting for a full sync to complete. Changed the default address gap limit from 50 to20, and made various UX improvements including larger tap targets, consistent Done button styling, and auto-focus on wallet rename.

This commit is contained in:
Nick Klockenga 2026-03-29 22:29:43 -04:00
parent b7b0e7da3c
commit 7642fe8190
No known key found for this signature in database
GPG Key ID: D32B8BF28121ADF6
14 changed files with 351 additions and 161 deletions

View File

@ -419,7 +419,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 21;
CURRENT_PROJECT_VERSION = 22;
DEVELOPMENT_ASSET_PATHS = "\"hellbender/Preview Content\"";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@ -452,7 +452,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 21;
CURRENT_PROJECT_VERSION = 22;
DEVELOPMENT_ASSET_PATHS = "\"hellbender/Preview Content\"";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;

View File

@ -31,7 +31,7 @@ final class WalletProfile {
internalDescriptor: String,
network: BitcoinNetwork = .testnet4,
isActive: Bool = false,
addressGapLimit: Int = 50,
addressGapLimit: Int = 20,
electrumHost: String = "",
electrumPort: Int = 0,
electrumSSL: Int = 0,

View File

@ -316,6 +316,18 @@ final class BitcoinService {
try await Task.detached { try client.ping() }.value
addToLog("Server ping OK")
// Fetch chain tip early to confirm server returns real data
if let header = await Task.detached(operation: { [client] in try? client.blockHeadersSubscribe() }).value {
guard currentProfile?.id == syncProfileId else {
addToLog("Sync cancelled: wallet switched during chain tip fetch")
return
}
chainTipHeight = UInt32(header.height)
electrumVerified = true
electrumConnectionError = nil
addToLog("Chain tip height: \(chainTipHeight)")
}
if needsFullScan {
let gapLimit = currentProfile?.addressGapLimit ?? Constants.maxAddressGap
addToLog("Starting full scan (gapLimit: \(gapLimit))")
@ -405,17 +417,6 @@ final class BitcoinService {
_ = try wallet.persist(persister: syncPersister)
}
// Update chain tip height for confirmation count calculation
addToLog("Fetching chain tip height")
if let header = await Task.detached(operation: { [client] in try? client.blockHeadersSubscribe() }).value {
guard currentProfile?.id == syncProfileId else {
addToLog("Sync cancelled: wallet switched during chain tip fetch")
return
}
chainTipHeight = UInt32(header.height)
addToLog("Chain tip height: \(chainTipHeight)")
}
// Verify wallet identity one more time after final await
guard currentProfile?.id == syncProfileId else {
addToLog("Sync completed but wallet switched — discarding results")
@ -427,8 +428,6 @@ final class BitcoinService {
let now = Date()
lastSyncDate = now
electrumVerified = true
electrumConnectionError = nil
setSyncState(.synced(now), for: syncProfileId)
addToLog("Sync completed successfully")
} catch {

View File

@ -51,7 +51,7 @@ enum Constants {
static let maxCosigners = 10
static let minCosigners = 1
static let maxAddressGap = 50
static let maxAddressGap = 20
static func derivationPath(for network: BitcoinNetwork) -> String {
"m/48'/\(network.coinType)'/0'/2'"

View File

@ -78,7 +78,7 @@ final class SetupWizardViewModel {
// Advanced settings
var blockExplorerHost: String = ""
var addressGapLimit: String = "50"
var addressGapLimit: String = "20"
// State
var errorMessage: String?

View File

@ -20,6 +20,12 @@ final class TransactionListViewModel {
bitcoinService.syncState
}
func clearState() {
transactions = []
balance = 0
isLoading = true
}
func loadActiveWallet(from wallets: [WalletProfile]) {
guard let active = wallets.first(where: { $0.isActive }) else { return }
expectedProfileId = active.id

View File

@ -1,3 +1,4 @@
import CryptoKit
import SwiftUI
// MARK: - Theme Data
@ -10,6 +11,7 @@ struct HBTheme {
var textPrimary: Color
var textSecondary: Color
var accent: Color
var heroBackground: Color
var success: Color
var error: Color
var colorScheme: ColorScheme? = .dark
@ -22,6 +24,7 @@ struct HBTheme {
textPrimary: Color(.label),
textSecondary: Color(.secondaryLabel),
accent: Color(red: 0.969, green: 0.576, blue: 0.102),
heroBackground: Color(.tertiarySystemBackground),
success: Color(.systemGreen),
error: Color(.systemRed),
colorScheme: nil
@ -35,6 +38,7 @@ struct HBTheme {
textPrimary: Color(red: 0.910, green: 0.902, blue: 0.890),
textSecondary: Color(red: 0.420, green: 0.420, blue: 0.463),
accent: Color(red: 0.969, green: 0.576, blue: 0.102),
heroBackground: Color(red: 0.110, green: 0.110, blue: 0.141),
success: Color(red: 0.176, green: 0.545, blue: 0.341),
error: Color(red: 0.851, green: 0.267, blue: 0.267),
colorScheme: .dark
@ -48,6 +52,7 @@ struct HBTheme {
textPrimary: Color(red: 0.110, green: 0.110, blue: 0.118),
textSecondary: Color(red: 0.557, green: 0.557, blue: 0.576),
accent: Color(red: 0.969, green: 0.576, blue: 0.102),
heroBackground: Color(red: 0.930, green: 0.930, blue: 0.945),
success: Color(red: 0.204, green: 0.780, blue: 0.349),
error: Color(red: 1.000, green: 0.231, blue: 0.188),
colorScheme: .light
@ -122,6 +127,11 @@ extension Color {
ThemeManager.shared.theme.border
}
/// Hero
static var hbHeroBackground: Color {
ThemeManager.shared.theme.heroBackground
}
/// Accents
static var hbBitcoinOrange: Color {
ThemeManager.shared.theme.accent
@ -261,6 +271,54 @@ struct NetworkBadge: View {
}
}
// MARK: - Wallet Identicon
struct WalletIdenticon: View {
let id: UUID
private let gridSize = 4
private let palette: [Color] = [
Color.hbBitcoinOrange,
Color.hbSteelBlue,
Color.hbSuccess,
Color(red: 0.6, green: 0.4, blue: 0.8),
Color(red: 0.9, green: 0.5, blue: 0.3),
Color(red: 0.3, green: 0.7, blue: 0.7),
]
private var hashBytes: [UInt8] {
let data = withUnsafeBytes(of: id.uuid) { Data($0) }
return Array(SHA256.hash(data: data))
}
var body: some View {
let bytes = hashBytes
Canvas { context, size in
let cellW = size.width / CGFloat(gridSize)
let cellH = size.height / CGFloat(gridSize)
for row in 0 ..< gridSize {
for col in 0 ..< gridSize {
let byteIndex = (row * gridSize + col) % bytes.count
let byte = bytes[byteIndex]
let colorIndex = Int(byte) % palette.count
let brightness = Double(bytes[(byteIndex + 1) % bytes.count]) / 255.0
let opacity = 0.5 + brightness * 0.5
let rect = CGRect(
x: CGFloat(col) * cellW,
y: CGFloat(row) * cellH,
width: cellW,
height: cellH
)
context.fill(Path(rect), with: .color(palette[colorIndex].opacity(opacity)))
}
}
}
.clipShape(RoundedRectangle(cornerRadius: 6))
}
}
// MARK: - Sync Status Dot
struct SyncStatusDot: View {

View File

@ -32,9 +32,9 @@ struct ConnectionStatusView: View {
.navigationTitle("Connection Status")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
ToolbarItem(placement: .confirmationAction) {
Button("Done") { dismiss() }
.foregroundStyle(Color.hbTextSecondary)
.foregroundStyle(Color.hbBitcoinOrange)
}
}
}

View File

@ -7,10 +7,6 @@ private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "hellbend
struct SettingsView: View {
@Environment(\.modelContext) private var modelContext
@Query private var wallets: [WalletProfile]
@State private var viewModel = WalletManagerViewModel()
@State private var walletToDelete: WalletProfile?
@State private var showAddWallet = false
@State private var showLogExport = false
var body: some View {
@ -25,65 +21,6 @@ struct SettingsView: View {
.padding(.bottom, 4)
List {
// Wallets
Section("Wallets") {
ForEach(wallets) { wallet in
HStack(spacing: 12) {
Button {
if !wallet.isActive {
viewModel.setActiveWallet(wallet, allWallets: wallets, modelContext: modelContext)
}
} label: {
Image(systemName: wallet.isActive ? "checkmark.circle.fill" : "circle")
.font(.system(size: 22))
.foregroundStyle(wallet.isActive ? Color.hbBitcoinOrange : Color.hbTextSecondary)
}
.buttonStyle(.plain)
NavigationLink(destination: WalletInfoView(wallet: wallet)) {
HStack(spacing: 8) {
VStack(alignment: .leading, spacing: 4) {
Text(wallet.name)
.font(.hbHeadline)
.foregroundStyle(Color.hbTextPrimary)
HStack(spacing: 8) {
Text(wallet.multisigDescription)
.font(.hbMono(12))
.foregroundStyle(Color.hbTextSecondary)
NetworkBadge(network: wallet.bitcoinNetwork)
}
}
Spacer()
}
}
}
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
walletToDelete = wallet
} label: {
Label("Delete", systemImage: "trash")
}
}
.listRowBackground(Color.hbSurface)
}
if wallets.isEmpty {
Text("No wallets configured")
.foregroundStyle(Color.hbTextSecondary)
.listRowBackground(Color.hbSurface)
}
Button(action: { showAddWallet = true }) {
Label("Add Wallet", systemImage: "plus.circle")
.font(.hbBody(15))
.foregroundStyle(Color.hbBitcoinOrange)
}
.listRowBackground(Color.hbSurface)
}
// Security
AppLockSettingsSection()
@ -125,24 +62,6 @@ struct SettingsView: View {
}
.background(Color.hbBackground)
.navigationTitle("")
.sheet(isPresented: $showAddWallet) {
SetupWizardView(canDismiss: true)
.interactiveDismissDisabled()
}
.alert("Delete Wallet?", isPresented: .init(
get: { walletToDelete != nil },
set: { if !$0 { walletToDelete = nil } }
)) {
Button("Delete", role: .destructive) {
if let wallet = walletToDelete {
viewModel.deleteWallet(wallet, modelContext: modelContext)
}
walletToDelete = nil
}
Button("Cancel", role: .cancel) { walletToDelete = nil }
} message: {
Text("This will permanently delete \"\(walletToDelete?.name ?? "")\" and all its data.")
}
.sheet(isPresented: $showLogExport) {
LogExportSheet()
}

View File

@ -16,6 +16,7 @@ struct WalletInfoView: View {
@State private var showEditCosigners = false
@State private var isEditingName = false
@State private var editedName: String = ""
@FocusState private var nameFieldFocused: Bool
@State private var electrumHostText: String = ""
@State private var electrumPortText: String = ""
@State private var isTestingConnection = false
@ -54,7 +55,9 @@ struct WalletInfoView: View {
.foregroundStyle(Color.hbTextPrimary)
.multilineTextAlignment(.trailing)
.frame(maxWidth: 180)
.focused($nameFieldFocused)
.onSubmit { saveName() }
.onAppear { nameFieldFocused = true }
Button(action: saveName) {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(Color.hbSuccess)

View File

@ -10,6 +10,13 @@ struct TransactionListView: View {
@Query private var walletLabels: [WalletLabel]
@Query private var frozenUTXOs: [FrozenUTXO]
@State private var viewModel = TransactionListViewModel()
@State private var walletManager = WalletManagerViewModel()
@State private var showWalletPicker = false
@State private var walletPickerEditMode = false
@State private var showAddWallet = false
@State private var showWalletInfo = false
@State private var walletToEdit: WalletProfile?
@State private var walletToDelete: WalletProfile?
@State private var showConnectionStatus = false
@State private var showDashboard = false
@State private var showImportFilePicker = false
@ -33,6 +40,10 @@ struct TransactionListView: View {
FiatPriceService.shared
}
private var activeWalletName: String {
wallets.first(where: { $0.isActive })?.name ?? viewModel.walletName
}
private var isPrivate: Bool {
wallets.first(where: { $0.isActive })?.privacyMode ?? false
}
@ -188,25 +199,17 @@ struct TransactionListView: View {
var body: some View {
NavigationStack {
VStack(spacing: 0) {
// Wallet status header
VStack(spacing: 8) {
// Wallet hero header
VStack(spacing: 12) {
// Wallet selector + menu row
HStack {
Button(action: { showConnectionStatus = true }) {
SyncStatusDot(state: viewModel.syncState)
}
Text(viewModel.walletName)
.font(.hbHeadline)
.foregroundStyle(Color.hbTextPrimary)
NetworkBadge(network: viewModel.network)
Spacer()
Menu {
Button(action: { showDashboard = true }) {
Label("Dashboard", systemImage: "chart.bar.xaxis")
}
Button(action: { showWalletInfo = true }) {
Label("Wallet Info", systemImage: "info.circle")
}
Menu {
Button(action: { showImportFilePicker = true }) {
Label("Labels File Import", systemImage: "square.and.arrow.down")
@ -222,61 +225,79 @@ struct TransactionListView: View {
Label("Wallet Labels", systemImage: "tag")
}
} label: {
Image(systemName: "ellipsis.circle")
Image(systemName: "ellipsis")
.font(.system(size: 20))
.foregroundStyle(Color.hbTextSecondary)
}
}
HStack(alignment: .center) {
VStack(alignment: .leading, spacing: 2) {
if isPrivate {
Text(Constants.privacyText())
.font(.hbAmountLarge)
.foregroundStyle(Color.hbTextPrimary)
} else if fiatEnabled, fiatPrimary, let fiatStr = fiatService.formattedSatsToFiat(viewModel.balance) {
Text(fiatStr)
.font(.hbAmountLarge)
.foregroundStyle(Color.hbTextPrimary)
Text(viewModel.balance.formattedSats)
.font(.hbBody(14))
.foregroundStyle(Color.hbTextSecondary)
} else {
Text(viewModel.balance.formattedSats)
.font(.hbAmountLarge)
.foregroundStyle(Color.hbTextPrimary)
if fiatEnabled, let fiatStr = fiatService.formattedSatsToFiat(viewModel.balance) {
Text(fiatStr)
.font(.hbBody(14))
.foregroundStyle(Color.hbTextSecondary)
}
}
}
.onLongPressGesture {
togglePrivacyMode()
}
.onTapGesture(count: 2) {
if fiatEnabled { fiatPrimary.toggle() }
.frame(width: 44, height: 44)
.contentShape(Rectangle())
}
Spacer()
if fiatEnabled {
Button(action: { fiatPrimary.toggle() }) {
Image(systemName: "arrow.up.arrow.down")
.font(.system(size: 14, weight: .medium))
// Wallet picker
Button(action: { showWalletPicker.toggle() }) {
HStack(spacing: 8) {
if let walletID {
WalletIdenticon(id: walletID)
.frame(width: 24, height: 24)
}
Text(activeWalletName)
.font(.hbHeadline)
.foregroundStyle(Color.hbTextPrimary)
Image(systemName: showWalletPicker ? "chevron.up" : "chevron.down")
.font(.system(size: 12, weight: .semibold))
.foregroundStyle(Color.hbTextSecondary)
.padding(8)
.background(Color.hbSurfaceElevated)
.clipShape(Circle())
}
.padding(.horizontal, 12)
.padding(.vertical, 8)
.overlay(
RoundedRectangle(cornerRadius: 22)
.strokeBorder(Color.hbBorder, lineWidth: 1)
)
}
Spacer()
// Connection status
Button(action: { showConnectionStatus = true }) {
SyncStatusDot(state: viewModel.syncState)
}
}
// Balance
VStack(spacing: 2) {
if isPrivate {
Text(Constants.privacyText())
.font(.hbAmountLarge)
.foregroundStyle(Color.hbTextPrimary)
} else if fiatEnabled, fiatPrimary, let fiatStr = fiatService.formattedSatsToFiat(viewModel.balance) {
Text(fiatStr)
.font(.hbAmountLarge)
.foregroundStyle(Color.hbTextPrimary)
Text(viewModel.balance.formattedSats)
.font(.hbBody(14))
.foregroundStyle(Color.hbTextSecondary)
} else {
Text(viewModel.balance.formattedSats)
.font(.hbAmountLarge)
.foregroundStyle(Color.hbTextPrimary)
if fiatEnabled, let fiatStr = fiatService.formattedSatsToFiat(viewModel.balance) {
Text(fiatStr)
.font(.hbBody(14))
.foregroundStyle(Color.hbTextSecondary)
}
}
}
.onLongPressGesture {
togglePrivacyMode()
}
.onTapGesture(count: 2) {
if fiatEnabled { fiatPrimary.toggle() }
}
// Info row
HStack {
Text(viewModel.multisigDescription + " multisig")
.font(.hbLabel())
.foregroundStyle(Color.hbTextSecondary)
NetworkBadge(network: viewModel.network)
Spacer()
@ -294,13 +315,29 @@ struct TransactionListView: View {
}
}
}
.hbCard()
.padding(16)
.background(Color.hbHeroBackground)
// Transactions section header
HStack {
Text("Transactions")
.font(.hbHeadline)
.foregroundStyle(Color.hbTextPrimary)
Spacer()
}
.padding(.horizontal, 16)
.padding(.top, 12)
.padding(.bottom, 4)
transactionContent
}
.background(Color.hbBackground)
.overlay { walletPickerOverlay }
.navigationTitle("")
.onDisappear {
walletPickerEditMode = false
showWalletPicker = false
}
.refreshable {
await viewModel.refresh()
}
@ -310,6 +347,33 @@ struct TransactionListView: View {
.sheet(isPresented: $showDashboard) {
WalletDashboardView()
}
.sheet(isPresented: $showAddWallet, onDismiss: {
if let active = wallets.first(where: { $0.isActive }),
bitcoinService.currentProfile?.id != active.id
{
viewModel.clearState()
bitcoinService.unloadWallet()
viewModel.loadActiveWallet(from: wallets)
}
}) {
SetupWizardView(canDismiss: true)
.interactiveDismissDisabled()
}
.sheet(isPresented: $showWalletInfo, onDismiss: {
walletToEdit = nil
}) {
if let wallet = walletToEdit ?? wallets.first(where: { $0.isActive }) {
NavigationStack {
WalletInfoView(wallet: wallet)
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("Done") { showWalletInfo = false }
.foregroundStyle(Color.hbBitcoinOrange)
}
}
}
}
}
.fileImporter(
isPresented: $showImportFilePicker,
allowedContentTypes: [UTType(filenameExtension: "jsonl") ?? .plainText],
@ -317,6 +381,20 @@ struct TransactionListView: View {
) { result in
importLabelsFromFile(result: result)
}
.alert("Delete Wallet?", isPresented: .init(
get: { walletToDelete != nil },
set: { if !$0 { walletToDelete = nil } }
)) {
Button("Delete", role: .destructive) {
if let wallet = walletToDelete {
walletManager.deleteWallet(wallet, modelContext: modelContext)
}
walletToDelete = nil
}
Button("Cancel", role: .cancel) { walletToDelete = nil }
} message: {
Text("Are you sure you want to delete \"\(walletToDelete?.name ?? "")\"? This cannot be undone. You can re-import using your output descriptor.")
}
.alert("Import Labels", isPresented: $showImportResult) {
Button("OK", role: .cancel) {}
} message: {
@ -393,6 +471,8 @@ struct TransactionListView: View {
Task { await fiatService.fetchRatesIfNeeded() }
}
} else {
walletPickerEditMode = false
showWalletPicker = false
bitcoinService.stopAutoSync()
}
}
@ -425,6 +505,129 @@ struct TransactionListView: View {
generator.impactOccurred()
}
@ViewBuilder
private var walletPickerOverlay: some View {
if showWalletPicker {
Color.black.opacity(0.35)
.ignoresSafeArea()
.onTapGesture {
walletPickerEditMode = false
showWalletPicker = false
}
VStack {
VStack(spacing: 0) {
// Header
HStack {
Button(action: {
walletPickerEditMode.toggle()
}) {
Text(walletPickerEditMode ? "Done" : "Edit")
.font(.hbBody(15))
.foregroundStyle(walletPickerEditMode ? Color.hbSuccess : Color.hbTextSecondary)
}
Spacer()
Text("Wallets")
.font(.hbHeadline)
.foregroundStyle(Color.hbTextPrimary)
Spacer()
Button(action: {
walletPickerEditMode = false
showWalletPicker = false
showAddWallet = true
}) {
Text("Add")
.font(.hbBody(15))
.foregroundStyle(Color.hbBitcoinOrange)
}
}
.padding(.horizontal, 14)
.padding(.vertical, 12)
Divider()
.background(Color.hbBorder)
ForEach(Array(wallets.enumerated()), id: \.element.id) { index, wallet in
HStack(spacing: 12) {
if walletPickerEditMode {
Image(systemName: "minus.circle.fill")
.font(.system(size: 20))
.foregroundStyle(Color.hbError)
.onTapGesture {
walletToDelete = wallet
}
}
WalletIdenticon(id: wallet.id)
.frame(width: 32, height: 32)
.overlay(
RoundedRectangle(cornerRadius: 6)
.strokeBorder(Color.hbBitcoinOrange, lineWidth: wallet.isActive ? 2 : 0)
)
VStack(spacing: 4) {
NetworkBadge(network: wallet.bitcoinNetwork)
Text(wallet.multisigDescription)
.font(.hbLabel())
.foregroundStyle(Color.hbTextSecondary)
}
.fixedSize()
Text(wallet.name)
.font(.hbBody())
.foregroundStyle(Color.hbTextPrimary)
Spacer()
if walletPickerEditMode {
Image(systemName: "chevron.right")
.font(.system(size: 14, weight: .semibold))
.foregroundStyle(Color.hbTextSecondary)
}
}
.padding(.horizontal, 14)
.padding(.vertical, 15)
.background(wallet.isActive ? Color.hbBitcoinOrange.opacity(0.08) : Color.clear)
.contentShape(Rectangle())
.onTapGesture {
if walletPickerEditMode {
walletToEdit = wallet
walletPickerEditMode = false
showWalletPicker = false
showWalletInfo = true
} else {
guard !wallet.isActive else {
showWalletPicker = false
return
}
viewModel.clearState()
walletManager.setActiveWallet(wallet, allWallets: wallets, modelContext: modelContext)
showWalletPicker = false
}
}
.onLongPressGesture {
walletPickerEditMode = true
}
if index < wallets.count - 1 {
Divider()
.background(Color.hbBorder)
}
}
}
.background(Color.hbSurfaceElevated)
.clipShape(RoundedRectangle(cornerRadius: 12))
.overlay(
RoundedRectangle(cornerRadius: 12)
.strokeBorder(Color.hbBorder, lineWidth: 0.5)
)
.shadow(color: .black.opacity(0.25), radius: 12, y: 4)
.padding(.horizontal, 35)
.padding(.top, 60)
Spacer()
}
.transition(.opacity)
.animation(.easeInOut(duration: 0.15), value: showWalletPicker)
}
}
@ViewBuilder
private var transactionContent: some View {
if viewModel.transactions.isEmpty, viewModel.isLoading || viewModel.syncState.isSyncing {

View File

@ -52,6 +52,8 @@ struct SetupWizardView: View {
Image(systemName: "xmark")
.font(.system(size: 14, weight: .semibold))
.foregroundStyle(Color.hbTextSecondary)
.frame(width: 44, height: 44)
.contentShape(Rectangle())
}
}
}

View File

@ -21,7 +21,7 @@ struct WalletNameView: View {
.font(.hbLabel())
.foregroundStyle(Color.hbTextSecondary)
TextField("My Multisig Wallet", text: $viewModel.walletName)
TextField("My Wallet", text: $viewModel.walletName)
.font(.hbBody(18))
.padding(14)
.background(Color.hbSurfaceElevated)

View File

@ -67,7 +67,7 @@ final class hellbenderUITests: XCTestCase {
XCTAssertTrue(nameTitle.waitForExistence(timeout: 3), "Wallet name screen should appear")
// Type a wallet name into the text field
let nameField = app.textFields["My Multisig Wallet"]
let nameField = app.textFields["My Wallet"]
XCTAssertTrue(nameField.waitForExistence(timeout: 3), "Wallet name text field should exist")
nameField.tap()
nameField.typeText("UI Test Wallet")