@@ -10,13 +10,17 @@ import BitcoinUI
10
10
import SwiftUI
11
11
12
12
struct WalletView : View {
13
+ @AppStorage ( " balanceDisplayFormat " ) private var balanceFormat : BalanceDisplayFormat =
14
+ . bitcoinSats
13
15
@Bindable var viewModel : WalletViewModel
14
16
@Binding var sendNavigationPath : NavigationPath
17
+ @State private var balanceTextPulsingOpacity : Double = 0.7
15
18
@State private var isFirstAppear = true
16
19
@State private var newTransactionSent = false
17
20
@State private var showAllTransactions = false
18
21
@State private var showReceiveView = false
19
22
@State private var showSettingsView = false
23
+ @State private var showingFormatMenu = false
20
24
21
25
var body : some View {
22
26
@@ -27,56 +31,41 @@ struct WalletView: View {
27
31
VStack ( spacing: 20 ) {
28
32
29
33
VStack ( spacing: 10 ) {
30
- withAnimation {
31
- HStack ( spacing: 15 ) {
32
- Image ( systemName: " bitcoinsign " )
33
- . foregroundStyle ( . secondary)
34
- . font ( . title)
35
- . fontWeight ( . thin)
36
- Text ( viewModel. balanceTotal. formattedSatoshis ( ) )
37
- . contentTransition ( . numericText( ) )
38
- . fontWeight ( . semibold)
39
- . fontDesign ( . rounded)
40
- Text ( " sats " )
41
- . foregroundStyle ( . secondary)
42
- . fontWeight ( . thin)
43
- }
44
- . font ( . largeTitle)
45
- . lineLimit ( 1 )
46
- . minimumScaleFactor ( 0.5 )
34
+ HStack ( spacing: 15 ) {
35
+ currencySymbol
36
+ balanceText
37
+ unitText
47
38
}
48
- . accessibilityLabel ( " Bitcoin Balance " )
49
- . accessibilityValue ( " \( viewModel. balanceTotal. formattedSatoshis ( ) ) sats " )
50
- HStack {
51
- if viewModel. walletSyncState == . syncing {
52
- Image ( systemName: " chart.bar.fill " )
53
- . symbolEffect ( . variableColor. cumulative)
54
- . transition ( . symbolEffect( . appear) )
39
+ . font ( . largeTitle)
40
+ . lineLimit ( 1 )
41
+ . minimumScaleFactor ( 0.5 )
42
+ }
43
+ . accessibilityLabel ( " Bitcoin Balance " )
44
+ . accessibilityValue ( formattedBalance)
45
+ . onTapGesture {
46
+ withAnimation ( . spring( response: 0.3 , dampingFraction: 0.7 ) ) {
47
+ balanceFormat =
48
+ BalanceDisplayFormat . allCases [
49
+ ( balanceFormat. index + 1 ) % BalanceDisplayFormat. allCases. count
50
+ ]
51
+ }
52
+ }
53
+ . swipeGesture { direction in
54
+ withAnimation ( . spring( response: 0.3 , dampingFraction: 0.7 ) ) {
55
+ switch direction {
56
+ case . left:
57
+ balanceFormat =
58
+ BalanceDisplayFormat . allCases [
59
+ ( balanceFormat. index + 1 ) % BalanceDisplayFormat. allCases. count
60
+ ]
61
+ case . right:
62
+ balanceFormat =
63
+ BalanceDisplayFormat . allCases [
64
+ ( balanceFormat. index - 1 + BalanceDisplayFormat. allCases. count)
65
+ % BalanceDisplayFormat. allCases. count
66
+ ]
55
67
}
56
- Text (
57
- viewModel. satsPrice > 0 || viewModel. walletSyncState == . synced
58
- ? viewModel. satsPrice. formatted ( . currency( code: " USD " ) ) : " "
59
- )
60
- . fontDesign ( . rounded)
61
- . foregroundStyle (
62
- viewModel. walletSyncState == . synced ? . secondary : . tertiary
63
- )
64
- . opacity (
65
- viewModel. walletSyncState == . syncing && viewModel. satsPrice == 0
66
- ? 0.7 : 1
67
- )
68
- . contentTransition ( . numericText( ) )
69
- . animation (
70
- . spring( response: 0.5 , dampingFraction: 0.6 , blendDuration: 0.5 ) ,
71
- value: viewModel. satsPrice
72
- )
73
68
}
74
- . foregroundStyle ( . secondary)
75
- . font ( . subheadline)
76
- . animation (
77
- . spring( response: 0.5 , dampingFraction: 0.6 , blendDuration: 0.5 ) ,
78
- value: viewModel. walletSyncState
79
- )
80
69
}
81
70
. padding ( . vertical, 35.0 )
82
71
@@ -300,6 +289,75 @@ struct WalletView: View {
300
289
301
290
}
302
291
292
+ extension WalletView {
293
+
294
+ @MainActor
295
+ var formattedBalance : String {
296
+ switch balanceFormat {
297
+ case . sats:
298
+ return viewModel. balanceTotal. formatted ( . number)
299
+ case . bitcoinSats:
300
+ return viewModel. balanceTotal. formattedSatoshis ( )
301
+ case . bitcoin:
302
+ return String ( format: " %.8f " , Double ( viewModel. balanceTotal) / 100_000_000 )
303
+ case . fiat:
304
+ return String ( format: " %.2f " , viewModel. satsPrice)
305
+ }
306
+ }
307
+
308
+ private var currencySymbol : some View {
309
+ Image ( systemName: balanceFormat == . fiat ? " dollarsign " : " bitcoinsign " )
310
+ . foregroundStyle ( . secondary)
311
+ . font ( . title)
312
+ . fontWeight ( . thin)
313
+ . transition (
314
+ . asymmetric(
315
+ insertion: . move( edge: . leading) . combined ( with: . opacity) ,
316
+ removal: . move( edge: . trailing) . combined ( with: . opacity)
317
+ )
318
+ )
319
+ . opacity ( balanceFormat == . sats ? 0 : 1 )
320
+ . id ( " symbol- \( balanceFormat) " )
321
+ . animation ( . spring( response: 0.3 , dampingFraction: 0.7 ) , value: balanceFormat)
322
+ }
323
+
324
+ @MainActor
325
+ var balanceText : some View {
326
+ Text ( balanceFormat == . fiat && viewModel. satsPrice == 0 ? " 00.00 " : formattedBalance)
327
+ . contentTransition ( . numericText( countsDown: true ) )
328
+ . fontWeight ( . semibold)
329
+ . fontDesign ( . rounded)
330
+ . foregroundStyle (
331
+ balanceFormat == . fiat && viewModel. satsPrice == 0 ? . secondary : . primary
332
+ )
333
+ . opacity (
334
+ balanceFormat == . fiat && viewModel. satsPrice == 0 ? balanceTextPulsingOpacity : 1
335
+ )
336
+ . animation ( . spring( response: 0.4 , dampingFraction: 0.8 ) , value: balanceFormat)
337
+ . animation ( . easeInOut, value: viewModel. satsPrice)
338
+ . onAppear {
339
+ withAnimation ( . easeInOut( duration: 1.5 ) . repeatForever ( autoreverses: true ) ) {
340
+ balanceTextPulsingOpacity = 0.3
341
+ }
342
+ }
343
+ }
344
+
345
+ private var unitText : some View {
346
+ Text ( balanceFormat. displayText)
347
+ . foregroundStyle ( . secondary)
348
+ . fontWeight ( . thin)
349
+ . transition (
350
+ . asymmetric(
351
+ insertion: . move( edge: . trailing) . combined ( with: . opacity) ,
352
+ removal: . move( edge: . leading) . combined ( with: . opacity)
353
+ )
354
+ )
355
+ . id ( " format- \( balanceFormat) " )
356
+ . animation ( . spring( response: 0.3 , dampingFraction: 0.7 ) , value: balanceFormat)
357
+ }
358
+
359
+ }
360
+
303
361
#if DEBUG
304
362
#Preview( " WalletView - en " ) {
305
363
WalletView (
0 commit comments