secant-ios-wallet/modules/Sources/Features/WalletBalances/WalletBalancesView.swift

213 lines
7.8 KiB
Swift

//
// WalletBalancesView.swift
// Zashi
//
// Created by Lukáš Korba on 04-02-2024
//
import SwiftUI
import ComposableArchitecture
import Generated
import UIComponents
public struct WalletBalancesView: View {
@Environment(\.colorScheme) private var colorScheme
@Perception.Bindable var store: StoreOf<WalletBalances>
let tokenName: String
let couldBeHidden: Bool
let shortened: Bool
public init(
store: StoreOf<WalletBalances>,
tokenName: String,
couldBeHidden: Bool = false,
shortened: Bool = false
) {
self.store = store
self.tokenName = tokenName
self.couldBeHidden = couldBeHidden
self.shortened = shortened
}
public var body: some View {
WithPerceptionTracking {
VStack(spacing: 0) {
balanceContent()
.padding(.top, 40)
.anchorPreference(
key: ExchangeRateFeaturePreferenceKey.self,
value: .bounds
) { $0 }
#if !SECANT_DISTRIB
.accessDebugMenuWithHiddenGesture {
store.send(.debugMenuStartup)
}
#endif
if shortened {
exchangeRate()
}
if store.migratingDatabase {
Text(L10n.Home.migratingDatabases)
.font(.custom(FontFamily.Inter.regular.name, size: 14))
.foregroundColor(Asset.Colors.primary.color)
.padding(.top, 12)
.padding(.bottom, 30)
} else if store.spendability != .everything && !shortened {
Button {
store.send(.availableBalanceTapped)
} label: {
AvailableBalanceView(
balance: store.shieldedBalance,
showIndicator: store.isProcessingZeroAvailableBalance
)
.padding(.top, 12)
.padding(.bottom, 30)
}
} else if !shortened {
Color.clear
.padding(.bottom, 30)
}
}
.foregroundColor(Asset.Colors.primary.color)
.onAppear { store.send(.onAppear) }
.onDisappear { store.send(.onDisappear) }
}
}
@ViewBuilder private func balanceContent() -> some View {
HStack(spacing: 0) {
ZcashSymbol()
.frame(width: 32, height: 32)
.zForegroundColor(Design.Text.primary)
if shortened {
ZatoshiText(store.totalBalance, .abbreviated)
.zFont(.semiBold, size: 48, style: Design.Text.primary)
} else {
ZatoshiRepresentationView(
balance: store.totalBalance,
fontName: FontFamily.Inter.semiBold.name,
mostSignificantFontSize: 48,
leastSignificantFontSize: 20,
format: .expanded
)
}
}
}
private func exchangeRate() -> some View {
Group {
if store.isExchangeRateFeatureOn {
if store.currencyConversion == nil && !store.isExchangeRateStale {
HStack(spacing: 8) {
Text(L10n.General.loading)
.font(.custom(FontFamily.Inter.semiBold.name, size: 14))
.foregroundColor(Asset.Colors.primary.color)
ProgressView()
}
.frame(height: 36)
.padding(.top, 10)
.padding(.vertical, 5)
}
if store.currencyConversion != nil || store.isExchangeRateStale {
Button {
store.send(.exchangeRateRefreshTapped)
} label: {
if store.isExchangeRateStale {
HStack {
Text(L10n.Tooltip.ExchangeRate.title)
.font(.custom(FontFamily.Inter.semiBold.name, size: 14))
.foregroundColor(Asset.Colors.primary.color)
Asset.Assets.infoCircle.image
.zImage(size: 20, color: Asset.Colors.primary.color)
}
.frame(maxWidth: .infinity)
.anchorPreference(
key: ExchangeRateStaleTooltipPreferenceKey.self,
value: .bounds
) { $0 }
} else if store.isExchangeRateRefreshEnabled {
HStack {
Text(store.currencyValue)
.hiddenIfSet()
.font(.custom(FontFamily.Inter.semiBold.name, size: 14))
.foregroundColor(Asset.Colors.primary.color)
if store.isExchangeRateUSDInFlight {
ProgressView()
.scaleEffect(0.7)
.frame(width: 20, height: 20)
} else {
Asset.Assets.refreshCCW.image
.zImage(size: 20, color: Asset.Colors.primary.color)
}
}
.padding(8)
.padding(.horizontal, 6)
.background {
RoundedRectangle(cornerRadius: Design.Radius._lg)
.stroke(Design.Surfaces.strokePrimary.color(colorScheme))
.background {
Design.Surfaces.bgSecondary.color(colorScheme)
.cornerRadius(10)
}
}
} else {
HStack {
Text(store.currencyValue)
.hiddenIfSet()
.font(.custom(FontFamily.Inter.semiBold.name, size: 14))
.foregroundColor(Asset.Colors.primary.color)
if store.isExchangeRateUSDInFlight {
ProgressView()
.scaleEffect(0.7)
.frame(width: 11, height: 14)
} else {
Asset.Assets.refreshCCW.image
.zImage(size: 20, color: Asset.Colors.shade72.color)
}
}
.padding(8)
.padding(.horizontal, 6)
}
}
.frame(height: 36)
.padding(.top, 10)
.padding(.vertical, 5)
}
}
}
}
}
// MARK: - Previews
#Preview {
WalletBalancesView(store: WalletBalances.initial, tokenName: "ZEC")
}
// MARK: - Store
extension WalletBalances {
public static var initial = StoreOf<WalletBalances>(
initialState: .initial
) {
WalletBalances()
}
}
// MARK: - Placeholders
extension WalletBalances.State {
public static let initial = WalletBalances.State(
shieldedBalance: .zero
)
}