[#1477] Restore wallet screen logic
[#1477] Update Restore Wallet flow - Everything has been redesigned - Estimation of Birthday height logic implemented
This commit is contained in:
parent
ab3226aced
commit
aba41ab0ee
|
@ -6,6 +6,9 @@ directly impact users rather than highlighting other crucial architectural updat
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
- Restore wallet flow has been fully redesigned. New option to estimate Birthday of the wallet based on a date has been implemented.
|
||||
|
||||
## 1.5.3 build 1 (2025-04-14)
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -84,6 +84,7 @@ let package = Package(
|
|||
.library(name: "Utils", targets: ["Utils"]),
|
||||
.library(name: "Vendors", targets: ["Vendors"]),
|
||||
.library(name: "WalletBalances", targets: ["WalletBalances"]),
|
||||
.library(name: "WalletBirthday", targets: ["WalletBirthday"]),
|
||||
.library(name: "WalletConfigProvider", targets: ["WalletConfigProvider"]),
|
||||
.library(name: "WalletStorage", targets: ["WalletStorage"]),
|
||||
.library(name: "Welcome", targets: ["Welcome"]),
|
||||
|
@ -97,8 +98,9 @@ let package = Package(
|
|||
.package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.5.6"),
|
||||
.package(url: "https://github.com/pointfreeco/swift-url-routing", from: "0.6.2"),
|
||||
.package(url: "https://github.com/zcash-hackworks/MnemonicSwift", from: "2.2.5"),
|
||||
.package(url: "https://github.com/Electric-Coin-Company/zcash-swift-wallet-sdk", from: "2.2.11"),
|
||||
.package(url: "https://github.com/flexa/flexa-ios.git", from: "1.0.9"),
|
||||
// .package(url: "https://github.com/Electric-Coin-Company/zcash-swift-wallet-sdk", from: "2.2.10"),
|
||||
.package(url: "https://github.com/LukasKorba/ZcashLightClientKit", branch: "1537-Birthday-estimate-based-on-a-date"),
|
||||
.package(url: "https://github.com/flexa/flexa-ios.git", exact: "1.0.9"),
|
||||
.package(url: "https://github.com/pacu/zcash-swift-payment-uri", from: "0.1.0-beta.10"),
|
||||
.package(url: "https://github.com/airbnb/lottie-spm.git", from: "4.5.1"),
|
||||
.package(url: "https://github.com/KeystoneHQ/keystone-sdk-ios/", from: "0.0.1")
|
||||
|
@ -126,7 +128,7 @@ let package = Package(
|
|||
"UIComponents",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk"),
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit"),
|
||||
.product(name: "KeystoneSDK", package: "keystone-sdk-ios")
|
||||
],
|
||||
path: "Sources/Features/AddKeystoneHWWallet"
|
||||
|
@ -142,7 +144,7 @@ let package = Package(
|
|||
"Scan",
|
||||
"UIComponents",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/AddressBook"
|
||||
),
|
||||
|
@ -155,7 +157,7 @@ let package = Package(
|
|||
"Utils",
|
||||
"WalletStorage",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Dependencies/AddressBookClient"
|
||||
),
|
||||
|
@ -168,7 +170,7 @@ let package = Package(
|
|||
"UIComponents",
|
||||
"Utils",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/AddressDetails"
|
||||
),
|
||||
|
@ -211,7 +213,7 @@ let package = Package(
|
|||
"WalletStorage",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/BalanceBreakdown"
|
||||
),
|
||||
|
@ -221,7 +223,7 @@ let package = Package(
|
|||
"Generated",
|
||||
"Utils",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Dependencies/BalanceFormatter"
|
||||
),
|
||||
|
@ -238,10 +240,13 @@ let package = Package(
|
|||
"AddressBook",
|
||||
"AudioServices",
|
||||
"Generated",
|
||||
"MnemonicSwift",
|
||||
"Models",
|
||||
"NumberFormatter",
|
||||
"PartialProposalError",
|
||||
"Pasteboard",
|
||||
"RequestZec",
|
||||
"RestoreInfo",
|
||||
"Scan",
|
||||
"SDKSynchronizer",
|
||||
"SendConfirmation",
|
||||
|
@ -250,10 +255,12 @@ let package = Package(
|
|||
"TransactionsManager",
|
||||
"UIComponents",
|
||||
"Utils",
|
||||
"WalletBirthday",
|
||||
"WalletStorage",
|
||||
"ZcashSDKEnvironment",
|
||||
"ZecKeyboard",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/CoordFlows"
|
||||
),
|
||||
|
@ -276,7 +283,7 @@ let package = Package(
|
|||
"FileManager",
|
||||
"Utils",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Dependencies/DatabaseFiles"
|
||||
),
|
||||
|
@ -293,7 +300,7 @@ let package = Package(
|
|||
"DerivationTool",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "URLRouting", package: "swift-url-routing"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Dependencies/Deeplink"
|
||||
),
|
||||
|
@ -314,7 +321,7 @@ let package = Package(
|
|||
"UIComponents",
|
||||
"Utils",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/DeleteWallet"
|
||||
),
|
||||
|
@ -323,7 +330,7 @@ let package = Package(
|
|||
dependencies: [
|
||||
"Utils",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Dependencies/DerivationTool"
|
||||
),
|
||||
|
@ -341,7 +348,7 @@ let package = Package(
|
|||
"UserPreferencesStorage",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Dependencies/ExchangeRate"
|
||||
),
|
||||
|
@ -352,7 +359,7 @@ let package = Package(
|
|||
"LogsHandler",
|
||||
"Utils",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/ExportLogs"
|
||||
),
|
||||
|
@ -389,7 +396,7 @@ let package = Package(
|
|||
"PartnerKeys",
|
||||
"UserDefaults",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk"),
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit"),
|
||||
.product(name: "Flexa", package: "flexa-ios")
|
||||
],
|
||||
path: "Sources/Dependencies/FlexaHandler"
|
||||
|
@ -417,7 +424,7 @@ let package = Package(
|
|||
"WalletBalances",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/Home"
|
||||
),
|
||||
|
@ -432,7 +439,7 @@ let package = Package(
|
|||
"WalletStorage",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/ImportWallet"
|
||||
),
|
||||
|
@ -508,7 +515,7 @@ let package = Package(
|
|||
"SecurityWarning",
|
||||
"UIComponents",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/OnboardingFlow"
|
||||
),
|
||||
|
@ -580,7 +587,7 @@ let package = Package(
|
|||
"Utils",
|
||||
"ZecKeyboard",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/Receive"
|
||||
),
|
||||
|
@ -595,7 +602,7 @@ let package = Package(
|
|||
"Utils",
|
||||
"WalletStorage",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/RecoveryPhraseDisplay"
|
||||
),
|
||||
|
@ -616,7 +623,7 @@ let package = Package(
|
|||
"Utils",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk"),
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit"),
|
||||
.product(name: "ZcashPaymentURI", package: "zcash-swift-payment-uri")
|
||||
],
|
||||
path: "Sources/Features/RequestZec"
|
||||
|
@ -699,7 +706,7 @@ let package = Package(
|
|||
"ZcashSDKEnvironment",
|
||||
"ZecKeyboard",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/Root"
|
||||
),
|
||||
|
@ -716,7 +723,7 @@ let package = Package(
|
|||
"ZcashSDKEnvironment",
|
||||
.product(name: "ZcashPaymentURI", package: "zcash-swift-payment-uri"),
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk"),
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit"),
|
||||
.product(name: "KeystoneSDK", package: "keystone-sdk-ios")
|
||||
],
|
||||
path: "Sources/Features/Scan"
|
||||
|
@ -728,7 +735,7 @@ let package = Package(
|
|||
"Models",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk"),
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit"),
|
||||
.product(name: "KeystoneSDK", package: "keystone-sdk-ios")
|
||||
],
|
||||
path: "Sources/Dependencies/SDKSynchronizer"
|
||||
|
@ -784,7 +791,7 @@ let package = Package(
|
|||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "Lottie", package: "lottie-spm"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk"),
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit"),
|
||||
.product(name: "KeystoneSDK", package: "keystone-sdk-ios")
|
||||
],
|
||||
path: "Sources/Features/SendConfirmation"
|
||||
|
@ -817,7 +824,7 @@ let package = Package(
|
|||
"WalletBalances",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/SendForm"
|
||||
),
|
||||
|
@ -830,7 +837,7 @@ let package = Package(
|
|||
"UserPreferencesStorage",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/ServerSetup"
|
||||
),
|
||||
|
@ -859,7 +866,7 @@ let package = Package(
|
|||
"UIComponents",
|
||||
"WhatsNew",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk"),
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit"),
|
||||
.product(name: "Flexa", package: "flexa-ios")
|
||||
],
|
||||
path: "Sources/Features/Settings"
|
||||
|
@ -917,7 +924,7 @@ let package = Package(
|
|||
"Utils",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/TransactionDetails"
|
||||
),
|
||||
|
@ -937,7 +944,7 @@ let package = Package(
|
|||
"Utils",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/TransactionList"
|
||||
),
|
||||
|
@ -958,7 +965,7 @@ let package = Package(
|
|||
"Utils",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/TransactionsManager"
|
||||
),
|
||||
|
@ -985,7 +992,7 @@ let package = Package(
|
|||
"Models",
|
||||
.product(name: "ZcashPaymentURI", package: "zcash-swift-payment-uri"),
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Dependencies/URIParser"
|
||||
),
|
||||
|
@ -1005,7 +1012,7 @@ let package = Package(
|
|||
"Utils",
|
||||
"WalletStorage",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Dependencies/UserMetadataProvider"
|
||||
),
|
||||
|
@ -1014,14 +1021,14 @@ let package = Package(
|
|||
dependencies: [
|
||||
"UserDefaults",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Dependencies/UserPreferencesStorage"
|
||||
),
|
||||
.target(
|
||||
name: "Utils",
|
||||
dependencies: [
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk"),
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit"),
|
||||
.product(name: "CasePaths", package: "swift-case-paths"),
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture")
|
||||
],
|
||||
|
@ -1046,10 +1053,24 @@ let package = Package(
|
|||
"Utils",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/WalletBalances"
|
||||
),
|
||||
.target(
|
||||
name: "WalletBirthday",
|
||||
dependencies: [
|
||||
"Generated",
|
||||
"Models",
|
||||
"SDKSynchronizer",
|
||||
"UIComponents",
|
||||
"Utils",
|
||||
"ZcashSDKEnvironment",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/WalletBirthday"
|
||||
),
|
||||
.target(
|
||||
name: "WalletConfigProvider",
|
||||
dependencies: [
|
||||
|
@ -1067,7 +1088,7 @@ let package = Package(
|
|||
"MnemonicClient",
|
||||
"Models",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Dependencies/WalletStorage"
|
||||
),
|
||||
|
@ -1105,7 +1126,7 @@ let package = Package(
|
|||
"Generated",
|
||||
"UserDefaults",
|
||||
"UserPreferencesStorage",
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk"),
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit"),
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture")
|
||||
],
|
||||
path: "Sources/Dependencies/ZcashSDKEnvironment"
|
||||
|
@ -1117,7 +1138,7 @@ let package = Package(
|
|||
"Models",
|
||||
"UIComponents",
|
||||
"Utils",
|
||||
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk"),
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit"),
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture")
|
||||
],
|
||||
path: "Sources/Features/ZecKeyboard"
|
||||
|
|
|
@ -69,6 +69,8 @@ public struct SDKSynchronizerClient {
|
|||
|
||||
public var walletAccounts: () async throws -> [WalletAccount] = { [] }
|
||||
|
||||
public var estimateBirthdayHeight: (Date) -> BlockHeight = { _ in BlockHeight(0) }
|
||||
|
||||
// PCZT
|
||||
public var createPCZTFromProposal: (AccountUUID, Proposal) async throws -> Pczt
|
||||
public var addProofsToPCZT: (Pczt) async throws -> Pczt
|
||||
|
|
|
@ -206,6 +206,9 @@ extension SDKSynchronizerClient: DependencyKey {
|
|||
|
||||
return sortedWalletAccounts
|
||||
},
|
||||
estimateBirthdayHeight: { date in
|
||||
synchronizer.estimateBirthdayHeight(for: date)
|
||||
},
|
||||
createPCZTFromProposal: { accountUUID, proposal in
|
||||
try await synchronizer.createPCZTFromProposal(accountUUID: accountUUID, proposal: proposal)
|
||||
},
|
||||
|
|
|
@ -42,6 +42,7 @@ extension SDKSynchronizerClient: TestDependencyKey {
|
|||
refreshExchangeRateUSD: unimplemented("\(Self.self).refreshExchangeRateUSD", placeholder: {}()),
|
||||
evaluateBestOf: { _, _, _, _, _, _ in fatalError("evaluateBestOf not implemented") },
|
||||
walletAccounts: unimplemented("\(Self.self).walletAccounts", placeholder: []),
|
||||
estimateBirthdayHeight: unimplemented("\(Self.self).estimateBirthdayHeight", placeholder: BlockHeight(0)),
|
||||
createPCZTFromProposal: unimplemented("\(Self.self).createPCZTFromProposal", placeholder: Pczt()),
|
||||
addProofsToPCZT: unimplemented("\(Self.self).addProofsToPCZT", placeholder: Pczt()),
|
||||
createTransactionFromPCZT: unimplemented("\(Self.self).createTransactionFromPCZT", placeholder: .success(txIds: [])),
|
||||
|
@ -80,6 +81,7 @@ extension SDKSynchronizerClient {
|
|||
refreshExchangeRateUSD: { },
|
||||
evaluateBestOf: { _, _, _, _, _, _ in [] },
|
||||
walletAccounts: { [] },
|
||||
estimateBirthdayHeight: { _ in BlockHeight(0) },
|
||||
createPCZTFromProposal: { _, _ in Pczt() },
|
||||
addProofsToPCZT: { _ in Pczt() },
|
||||
createTransactionFromPCZT: { _, _ in .success(txIds: []) },
|
||||
|
@ -189,6 +191,7 @@ extension SDKSynchronizerClient {
|
|||
refreshExchangeRateUSD: @escaping () -> Void = { },
|
||||
evaluateBestOf: @escaping ([LightWalletEndpoint], Double, Double, UInt64, Int, NetworkType) async -> [LightWalletEndpoint] = { _, _, _, _, _, _ in [] },
|
||||
walletAccounts: @escaping () async throws -> [WalletAccount] = { [] },
|
||||
estimateBirthdayHeight: @escaping (Date) -> BlockHeight = { _ in BlockHeight(0) },
|
||||
createPCZTFromProposal: @escaping (AccountUUID, Proposal) async throws -> Pczt = { _, _ in Pczt() },
|
||||
addProofsToPCZT: @escaping (Data) async throws -> Pczt = { _ in Pczt() },
|
||||
createTransactionFromPCZT: @escaping (Pczt, Pczt) async throws -> CreateProposedTransactionsResult = { _, _ in .success(txIds: []) },
|
||||
|
@ -224,6 +227,7 @@ extension SDKSynchronizerClient {
|
|||
refreshExchangeRateUSD: refreshExchangeRateUSD,
|
||||
evaluateBestOf: evaluateBestOf,
|
||||
walletAccounts: walletAccounts,
|
||||
estimateBirthdayHeight: estimateBirthdayHeight,
|
||||
createPCZTFromProposal: createPCZTFromProposal,
|
||||
addProofsToPCZT: addProofsToPCZT,
|
||||
createTransactionFromPCZT: createTransactionFromPCZT,
|
||||
|
|
|
@ -61,7 +61,7 @@ public struct AboutView: View {
|
|||
.onAppear { store.send(.onAppear) }
|
||||
.zashiBack()
|
||||
.screenTitle(L10n.Settings.about)
|
||||
.walletStatusPanel(background: .transparent)
|
||||
//..walletstatusPanel(background: .transparent)
|
||||
}
|
||||
//.navigationBarTitleDisplayMode(.inline)
|
||||
.screenHorizontalPadding()
|
||||
|
|
|
@ -9,6 +9,8 @@ import ComposableArchitecture
|
|||
import Generated
|
||||
|
||||
// Path
|
||||
import RestoreInfo
|
||||
import WalletBirthday
|
||||
|
||||
extension RestoreWalletCoordFlow {
|
||||
public func coordinatorReduce() -> Reduce<RestoreWalletCoordFlow.State, RestoreWalletCoordFlow.Action> {
|
||||
|
@ -16,9 +18,67 @@ extension RestoreWalletCoordFlow {
|
|||
switch action {
|
||||
// MARK: - Self
|
||||
|
||||
// case .path(.element(id: _, action: .requestZec(.requestTapped))):
|
||||
// state.path.append(.requestZecSummary(state.requestZecState))
|
||||
// return .none
|
||||
case .nextTapped:
|
||||
state.path.append(.walletBirthday(WalletBirthday.State.initial))
|
||||
return .none
|
||||
|
||||
case .resolveRestoreWithBirthday(let birthday):
|
||||
do {
|
||||
let seedPhrase = state.words.joined(separator: " ")
|
||||
|
||||
// validate the seed
|
||||
try mnemonic.isValid(seedPhrase)
|
||||
|
||||
try walletStorage.importWallet(seedPhrase, birthday, .english, false)
|
||||
|
||||
// update the backup phrase validation flag
|
||||
try walletStorage.markUserPassedPhraseBackupTest(true)
|
||||
|
||||
state.path.append(.restoreInfo(RestoreInfo.State.initial))
|
||||
|
||||
// notify user
|
||||
return .send(.successfullyRecovered)
|
||||
} catch {
|
||||
return .send(.failedToRecover(error.toZcashError()))
|
||||
}
|
||||
|
||||
// MARK: - Wallet Birthday
|
||||
|
||||
case .path(.element(id: _, action: .walletBirthday(.helpSheetRequested))):
|
||||
state.isHelpSheetPreseted.toggle()
|
||||
return .none
|
||||
|
||||
case .path(.element(id: _, action: .walletBirthday(.estimateHeightTapped))):
|
||||
state.path.append(.estimateBirthdaysDate(WalletBirthday.State.initial))
|
||||
return .none
|
||||
|
||||
case .path(.element(id: _, action: .walletBirthday(.restoreTapped))):
|
||||
for element in state.path {
|
||||
if case .walletBirthday(let walletBirthdayState) = element {
|
||||
return .send(.resolveRestoreWithBirthday(walletBirthdayState.estimatedHeight))
|
||||
}
|
||||
}
|
||||
return .none
|
||||
|
||||
case .path(.element(id: _, action: .estimateBirthdaysDate(.helpSheetRequested))):
|
||||
state.isHelpSheetPreseted.toggle()
|
||||
return .none
|
||||
|
||||
case .path(.element(id: _, action: .estimateBirthdaysDate(.estimateHeightReady))):
|
||||
for element in state.path {
|
||||
if case .estimateBirthdaysDate(let estimateBirthdaysDateState) = element {
|
||||
state.path.append(.estimatedBirthday(estimateBirthdaysDateState))
|
||||
}
|
||||
}
|
||||
return .none
|
||||
|
||||
case .path(.element(id: _, action: .estimatedBirthday(.restoreTapped))):
|
||||
for element in state.path {
|
||||
if case .estimatedBirthday(let estimatedBirthdayState) = element {
|
||||
return .send(.resolveRestoreWithBirthday(estimatedBirthdayState.estimatedHeight))
|
||||
}
|
||||
}
|
||||
return .none
|
||||
|
||||
default: return .none
|
||||
}
|
||||
|
|
|
@ -9,38 +9,138 @@ import SwiftUI
|
|||
import ComposableArchitecture
|
||||
import ZcashLightClientKit
|
||||
|
||||
import MnemonicSwift
|
||||
import Pasteboard
|
||||
import WalletStorage
|
||||
|
||||
// Path
|
||||
import RestoreInfo
|
||||
import WalletBirthday
|
||||
|
||||
@Reducer
|
||||
public struct RestoreWalletCoordFlow {
|
||||
@Reducer
|
||||
public enum Path {
|
||||
case estimateBirthdaysDate(WalletBirthday)
|
||||
case estimatedBirthday(WalletBirthday)
|
||||
case restoreInfo(RestoreInfo)
|
||||
case walletBirthday(WalletBirthday)
|
||||
}
|
||||
|
||||
@ObservableState
|
||||
public struct State {
|
||||
public var isHelpSheetPreseted = false
|
||||
public var isValidSeed = false
|
||||
public var nextIndex: Int?
|
||||
public var path = StackState<Path.State>()
|
||||
// public var zecKeyboardState = ZecKeyboard.State.initial
|
||||
public var prevWords: [String] = Array(repeating: "", count: 24)
|
||||
public var selectedIndex: Int?
|
||||
public var suggestedWords: [String] = []
|
||||
public var words: [String] = Array(repeating: "", count: 24)
|
||||
public var wordsValidity: [Bool] = Array(repeating: true, count: 24)
|
||||
|
||||
public init() { }
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
public enum Action: BindableAction {
|
||||
case binding(BindingAction<RestoreWalletCoordFlow.State>)
|
||||
case evaluateSeedValidity
|
||||
case failedToRecover(ZcashError)
|
||||
case helpSheetRequested
|
||||
case nextTapped
|
||||
case path(StackActionOf<Path>)
|
||||
// case zecKeyboard(ZecKeyboard.Action)
|
||||
case resolveRestoreWithBirthday(BlockHeight)
|
||||
case selectedIndex(Int?)
|
||||
case successfullyRecovered
|
||||
case suggestedWordTapped(String)
|
||||
case suggestionsRequested(Int)
|
||||
#if DEBUG
|
||||
case debugPasteSeed
|
||||
#endif
|
||||
}
|
||||
|
||||
@Dependency(\.mnemonic) var mnemonic
|
||||
@Dependency(\.pasteboard) var pasteboard
|
||||
@Dependency(\.walletStorage) var walletStorage
|
||||
|
||||
public init() { }
|
||||
|
||||
public var body: some Reducer<State, Action> {
|
||||
coordinatorReduce()
|
||||
|
||||
// Scope(state: \.zecKeyboardState, action: \.zecKeyboard) {
|
||||
// ZecKeyboard()
|
||||
// }
|
||||
BindingReducer()
|
||||
|
||||
Reduce { state, action in
|
||||
switch action {
|
||||
case .binding(\.words):
|
||||
let changedIndices = state.words.indices.filter { state.words[$0] != state.prevWords[$0] }
|
||||
state.prevWords = state.words
|
||||
|
||||
if let index = changedIndices.first {
|
||||
let word = state.words[index]
|
||||
if word.hasSuffix(" ") {
|
||||
state.words[index] = word.trimmingCharacters(in: .whitespaces)
|
||||
state.prevWords = state.words
|
||||
return .send(.suggestedWordTapped(state.words[index]))
|
||||
}
|
||||
|
||||
return .send(.suggestionsRequested(index))
|
||||
}
|
||||
|
||||
return .none
|
||||
|
||||
case .selectedIndex(let index):
|
||||
state.selectedIndex = index
|
||||
if let index {
|
||||
return .send(.suggestionsRequested(index))
|
||||
}
|
||||
return .none
|
||||
|
||||
case .suggestionsRequested(let index):
|
||||
let prefix = state.words[index]
|
||||
if prefix.isEmpty {
|
||||
state.suggestedWords = []
|
||||
} else {
|
||||
state.suggestedWords = mnemonic.suggestWords(prefix)
|
||||
state.wordsValidity[index] = !state.suggestedWords.isEmpty
|
||||
}
|
||||
return .send(.evaluateSeedValidity)
|
||||
|
||||
case .suggestedWordTapped(let word):
|
||||
if let index = state.selectedIndex {
|
||||
state.words[index] = word
|
||||
state.prevWords = state.words
|
||||
state.nextIndex = index + 1 < 24 ? index + 1 : 0
|
||||
return .send(.evaluateSeedValidity)
|
||||
}
|
||||
return .none
|
||||
|
||||
case .helpSheetRequested:
|
||||
state.isHelpSheetPreseted.toggle()
|
||||
return .none
|
||||
|
||||
case .evaluateSeedValidity:
|
||||
do {
|
||||
try mnemonic.isValid(state.words.joined(separator: " "))
|
||||
state.isValidSeed = true
|
||||
} catch {
|
||||
state.isValidSeed = false
|
||||
}
|
||||
return .none
|
||||
|
||||
#if DEBUG
|
||||
case .debugPasteSeed:
|
||||
do {
|
||||
let seedToPaste = pasteboard.getString()?.data ?? ""
|
||||
try mnemonic.isValid(seedToPaste)
|
||||
state.words = seedToPaste.components(separatedBy: " ")
|
||||
state.isValidSeed = true
|
||||
} catch {
|
||||
state.isValidSeed = false
|
||||
}
|
||||
return .none
|
||||
#endif
|
||||
|
||||
default: return .none
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,21 @@ import UIComponents
|
|||
import Generated
|
||||
|
||||
// Path
|
||||
import RestoreInfo
|
||||
import WalletBirthday
|
||||
|
||||
public struct RestoreWalletCoordFlowView: View {
|
||||
enum FocusTextField: Hashable {
|
||||
case field(Int)
|
||||
}
|
||||
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
@Perception.Bindable var store: StoreOf<RestoreWalletCoordFlow>
|
||||
|
||||
@FocusState private var focusedField: FocusTextField?
|
||||
@State private var keyboardVisible: Bool = false
|
||||
|
||||
public init(store: StoreOf<RestoreWalletCoordFlow>) {
|
||||
self.store = store
|
||||
}
|
||||
|
@ -25,28 +34,247 @@ public struct RestoreWalletCoordFlowView: View {
|
|||
public var body: some View {
|
||||
WithPerceptionTracking {
|
||||
NavigationStack(path: $store.scope(state: \.path, action: \.path)) {
|
||||
Text("RestoreWalletCoordFlowView")
|
||||
// ZecKeyboardView(
|
||||
// store:
|
||||
// store.scope(
|
||||
// state: \.zecKeyboardState,
|
||||
// action: \.zecKeyboard
|
||||
// ),
|
||||
// tokenName: tokenName
|
||||
// )
|
||||
ZStack {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text(L10n.RestoreWallet.title)
|
||||
.zFont(.semiBold, size: 24, style: Design.Text.primary)
|
||||
.padding(.top, 20)
|
||||
.onLongPressGesture {
|
||||
#if DEBUG
|
||||
store.send(.debugPasteSeed)
|
||||
#endif
|
||||
}
|
||||
|
||||
Text(L10n.RestoreWallet.info)
|
||||
.zFont(size: 14, style: Design.Text.primary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.padding(.top, 8)
|
||||
.padding(.bottom, 20)
|
||||
|
||||
ForEach(0..<8, id: \.self) { j in
|
||||
HStack(spacing: 4) {
|
||||
ForEach(0..<3, id: \.self) { i in
|
||||
HStack(spacing: 0) {
|
||||
Text("\(j * 3 + i + 1)")
|
||||
.zFont(.medium, size: 14, style: Design.Text.primary)
|
||||
.padding(.trailing, 4)
|
||||
|
||||
TextField("", text: $store.words[j * 3 + i])
|
||||
.zFont(size: 16, style: Design.Text.primary)
|
||||
.disableAutocorrection(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
.focused($focusedField, equals: .field((j * 3 + i)))
|
||||
.keyboardType(.alphabet)
|
||||
.submitLabel(.next)
|
||||
.onSubmit {
|
||||
focusedField = ((j * 3 + i) < 23)
|
||||
? .field((j * 3 + i) + 1)
|
||||
: .field(0)
|
||||
}
|
||||
}
|
||||
.padding(6)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(
|
||||
focusedField == .field(j * 3 + i)
|
||||
? Design.Surfaces.bgPrimary.color(colorScheme)
|
||||
: Design.Surfaces.bgSecondary.color(colorScheme)
|
||||
)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.stroke(
|
||||
!store.wordsValidity[j * 3 + i]
|
||||
? Design.Inputs.ErrorFilled.stroke.color(colorScheme)
|
||||
: focusedField == .field(j * 3 + i)
|
||||
? Design.Text.primary.color(colorScheme)
|
||||
: Design.Surfaces.bgSecondary.color(colorScheme),
|
||||
lineWidth: 2
|
||||
)
|
||||
}
|
||||
}
|
||||
.padding(2)
|
||||
.padding(.bottom, 4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if keyboardVisible {
|
||||
Color.clear
|
||||
.frame(height: 44)
|
||||
}
|
||||
}
|
||||
.screenHorizontalPadding()
|
||||
}
|
||||
.padding(.vertical, 1)
|
||||
|
||||
VStack {
|
||||
Spacer()
|
||||
|
||||
ZashiButton(L10n.General.next) {
|
||||
store.send(.nextTapped)
|
||||
}
|
||||
.disabled(!store.isValidSeed)
|
||||
.padding(.bottom, 24)
|
||||
.screenHorizontalPadding()
|
||||
}
|
||||
.ignoresSafeArea(.keyboard, edges: .bottom)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.onAppear { observeKeyboardNotifications() }
|
||||
.onChange(of: focusedField) { handle in
|
||||
if case .field(let index) = handle {
|
||||
store.send(.selectedIndex(index))
|
||||
}
|
||||
|
||||
if handle == nil {
|
||||
store.send(.selectedIndex(nil))
|
||||
}
|
||||
}
|
||||
.onChange(of: store.nextIndex) { value in
|
||||
if let nextIndex = value {
|
||||
focusedField = .field(nextIndex)
|
||||
}
|
||||
}
|
||||
.applyScreenBackground()
|
||||
.overlay(
|
||||
VStack(spacing: 0) {
|
||||
Spacer()
|
||||
|
||||
Asset.Colors.primary.color
|
||||
.frame(height: 1)
|
||||
.opacity(0.1)
|
||||
|
||||
HStack(alignment: .center) {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack(spacing: 4) {
|
||||
ForEach(store.suggestedWords, id: \.self) { suggestedWord in
|
||||
Button {
|
||||
store.send(.suggestedWordTapped(suggestedWord))
|
||||
} label: {
|
||||
Text(suggestedWord)
|
||||
.zFont(size: 16, style: Design.Text.primary)
|
||||
.fixedSize()
|
||||
.padding(8)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(Design.Surfaces.bgSecondary.color(colorScheme))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.leading, 4)
|
||||
}
|
||||
.mask(
|
||||
LinearGradient(
|
||||
gradient: Gradient(stops: [
|
||||
.init(color: Design.Surfaces.bgSecondary.color(colorScheme).opacity(0.7), location: 0.9),
|
||||
.init(color: Design.Surfaces.bgSecondary.color(colorScheme).opacity(0), location: 0.98)
|
||||
]),
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
)
|
||||
.frame(height: 38)
|
||||
//.clipped()
|
||||
|
||||
Spacer()
|
||||
|
||||
Button {
|
||||
focusedField = nil
|
||||
} label: {
|
||||
Text(L10n.General.done.uppercased())
|
||||
.zFont(.regular, size: 14, style: Design.Text.primary)
|
||||
}
|
||||
// .padding(.bottom, 4)
|
||||
.padding(.trailing, 24)
|
||||
.padding(.leading, 4)
|
||||
}
|
||||
.applyScreenBackground()
|
||||
.frame(height: keyboardVisible ? 44 : 0)
|
||||
.frame(maxWidth: .infinity)
|
||||
.opacity(keyboardVisible ? 1 : 0)
|
||||
}
|
||||
)
|
||||
// .navigationBarHidden(true)
|
||||
} destination: { store in
|
||||
// switch store.case {
|
||||
// case let .requestZec(store):
|
||||
// RequestZecView(store: store, tokenName: tokenName)
|
||||
// }
|
||||
switch store.case {
|
||||
case let .estimateBirthdaysDate(store):
|
||||
WalletBirthdayEstimateDateView(store: store)
|
||||
case let .estimatedBirthday(store):
|
||||
WalletBirthdayEstimatedHeightView(store: store)
|
||||
case let .restoreInfo(store):
|
||||
RestoreInfoView(store: store)
|
||||
case let .walletBirthday(store):
|
||||
WalletBirthdayView(store: store)
|
||||
}
|
||||
}
|
||||
.navigationBarHidden(!store.path.isEmpty)
|
||||
.navigationBarItems(
|
||||
trailing:
|
||||
Button {
|
||||
store.send(.helpSheetRequested)
|
||||
} label: {
|
||||
Asset.Assets.Icons.help.image
|
||||
.zImage(size: 24, style: Design.Text.primary)
|
||||
.padding(8)
|
||||
}
|
||||
)
|
||||
.zashiSheet(isPresented: $store.isHelpSheetPreseted) {
|
||||
helpSheetContent()
|
||||
.screenHorizontalPadding()
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 4)
|
||||
.applyScreenBackground()
|
||||
.zashiBack()
|
||||
.screenTitle(L10n.General.request)
|
||||
.screenTitle(L10n.ImportWallet.Button.restoreWallet)
|
||||
}
|
||||
|
||||
private func observeKeyboardNotifications() {
|
||||
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { _ in
|
||||
withAnimation {
|
||||
keyboardVisible = true
|
||||
}
|
||||
}
|
||||
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { _ in
|
||||
withAnimation {
|
||||
keyboardVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func helpSheetContent() -> some View {
|
||||
Text(L10n.RestoreWallet.Help.title)
|
||||
.zFont(.semiBold, size: 24, style: Design.Text.primary)
|
||||
.padding(.top, 24)
|
||||
.padding(.bottom, 12)
|
||||
|
||||
infoContent(text: L10n.RestoreWallet.Help.phrase)
|
||||
.padding(.bottom, 12)
|
||||
|
||||
infoContent(text: L10n.RestoreWallet.Help.birthday)
|
||||
.padding(.bottom, 32)
|
||||
|
||||
ZashiButton(L10n.RestoreInfo.gotIt) {
|
||||
store.send(.helpSheetRequested)
|
||||
}
|
||||
.padding(.bottom, 24)
|
||||
}
|
||||
|
||||
@ViewBuilder private func infoContent(text: String) -> some View {
|
||||
HStack(alignment: .top, spacing: 8) {
|
||||
Asset.Assets.infoCircle.image
|
||||
.zImage(size: 20, style: Design.Text.primary)
|
||||
|
||||
if let attrText = try? AttributedString(
|
||||
markdown: text,
|
||||
including: \.zashiApp
|
||||
) {
|
||||
ZashiText(withAttributedString: attrText, colorScheme: colorScheme)
|
||||
.zFont(size: 14, style: Design.Text.tertiary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ public struct ExportTransactionHistoryView: View {
|
|||
}
|
||||
}
|
||||
.zashiBack()
|
||||
.walletStatusPanel()
|
||||
//..walletstatusPanel()
|
||||
|
||||
shareLogsView()
|
||||
}
|
||||
|
|
|
@ -73,14 +73,14 @@ public struct HomeView: View {
|
|||
}
|
||||
|
||||
button(
|
||||
"Scan",
|
||||
L10n.HomeScreen.scan,
|
||||
icon: Asset.Assets.Icons.scan.image
|
||||
) {
|
||||
store.send(.scanTapped)
|
||||
}
|
||||
|
||||
button(
|
||||
"More",
|
||||
L10n.HomeScreen.more,
|
||||
icon: Asset.Assets.Icons.dotsMenu.image
|
||||
) {
|
||||
store.send(.moreTapped)
|
||||
|
@ -292,7 +292,7 @@ public struct HomeView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.walletStatusPanel()
|
||||
//..walletstatusPanel()
|
||||
.applyScreenBackground()
|
||||
.onAppear {
|
||||
store.send(.onAppear)
|
||||
|
|
|
@ -84,6 +84,7 @@ public struct OnboardingFlow {
|
|||
return .none
|
||||
|
||||
case .importExistingWallet:
|
||||
state.restoreWalletCoordFlowState = .initial
|
||||
state.destination = .importExistingWallet
|
||||
return .none
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ public struct PrivateDataConsentView: View {
|
|||
.padding(.vertical, 1)
|
||||
.zashiBack()
|
||||
.onAppear { store.send(.onAppear)}
|
||||
.walletStatusPanel()
|
||||
//..walletstatusPanel()
|
||||
|
||||
shareLogsView()
|
||||
}
|
||||
|
|
|
@ -12,17 +12,25 @@ import Generated
|
|||
@Reducer
|
||||
public struct RestoreInfo {
|
||||
@ObservableState
|
||||
public struct State: Equatable { }
|
||||
public struct State: Equatable {
|
||||
public var isAcknowledged = true
|
||||
}
|
||||
|
||||
public enum Action: Equatable {
|
||||
public enum Action: BindableAction, Equatable {
|
||||
case binding(BindingAction<RestoreInfo.State>)
|
||||
case gotItTapped
|
||||
}
|
||||
|
||||
public init() { }
|
||||
|
||||
public var body: some Reducer<State, Action> {
|
||||
BindingReducer()
|
||||
|
||||
Reduce { state, action in
|
||||
switch action {
|
||||
case .binding:
|
||||
return .none
|
||||
|
||||
case .gotItTapped:
|
||||
return .none
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ import Generated
|
|||
import UIComponents
|
||||
|
||||
public struct RestoreInfoView: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
@Perception.Bindable var store: StoreOf<RestoreInfo>
|
||||
|
||||
public init(store: StoreOf<RestoreInfo>) {
|
||||
|
@ -19,47 +21,64 @@ public struct RestoreInfoView: View {
|
|||
|
||||
public var body: some View {
|
||||
WithPerceptionTracking {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Asset.Assets.Illustrations.connect.image
|
||||
.resizable()
|
||||
.frame(width: 132, height: 90)
|
||||
.padding(.top, 40)
|
||||
.padding(.bottom, 24)
|
||||
|
||||
Text(L10n.RestoreInfo.title)
|
||||
.font(.custom(FontFamily.Inter.semiBold.name, size: 25))
|
||||
.padding(.vertical, 30)
|
||||
|
||||
Asset.Assets.restoreInfo.image
|
||||
.zImage(width: 90, height: 172, color: Asset.Colors.primary.color)
|
||||
.zFont(.semiBold, size: 24, style: Design.Text.primary)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text(L10n.RestoreInfo.subTitle)
|
||||
.font(.custom(FontFamily.Inter.semiBold.name, size: 16))
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.vertical, 30)
|
||||
.padding(.horizontal, 50)
|
||||
.zFont(.medium, size: 16, style: Design.Text.primary)
|
||||
.padding(.bottom, 16)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Text(L10n.RestoreInfo.tips)
|
||||
.font(.custom(FontFamily.Inter.bold.name, size: 12))
|
||||
Text(L10n.RestoreInfo.tips)
|
||||
.zFont(size: 14, style: Design.Text.primary)
|
||||
.padding(.bottom, 4)
|
||||
|
||||
bulletpoint(L10n.RestoreInfo.tip1)
|
||||
bulletpoint(L10n.RestoreInfo.tip2)
|
||||
.padding(.bottom, 20)
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("\(Text(L10n.RestoreInfo.note).bold())\(L10n.RestoreInfo.noteInfo)")
|
||||
.zFont(size: 12, style: Design.Text.primary)
|
||||
.padding(.bottom, 24)
|
||||
|
||||
HStack {
|
||||
ZashiToggle(
|
||||
isOn: $store.isAcknowledged,
|
||||
label: L10n.RestoreInfo.checkbox,
|
||||
textSize: 16
|
||||
)
|
||||
|
||||
bulletpoint(L10n.RestoreInfo.tip1)
|
||||
bulletpoint(L10n.RestoreInfo.tip2)
|
||||
bulletpoint(L10n.RestoreInfo.tip3)
|
||||
.padding(.bottom, 20)
|
||||
|
||||
Text(L10n.RestoreInfo.note)
|
||||
.font(.custom(FontFamily.Inter.bold.name, size: 11))
|
||||
+ Text(L10n.RestoreInfo.noteInfo)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 11))
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, 30)
|
||||
.padding(.leading, 1)
|
||||
|
||||
// Group {
|
||||
// Text(L10n.RestoreInfo.note)
|
||||
// .font(.custom(FontFamily.Inter.bold.name, size: 12))
|
||||
// + Text(L10n.RestoreInfo.noteInfo)
|
||||
// .font(.custom(FontFamily.Inter.regular.name, size: 12))
|
||||
// }
|
||||
// .foregroundColor(Design.Text.primary.color(colorScheme))
|
||||
|
||||
ZashiButton(L10n.RestoreInfo.gotIt) {
|
||||
store.send(.gotItTapped)
|
||||
}
|
||||
.padding(.vertical, 50)
|
||||
.padding(.vertical, 24)
|
||||
}
|
||||
.padding(.vertical, 1)
|
||||
.zashiBack(hidden: true)
|
||||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.screenHorizontalPadding()
|
||||
.applyScreenBackground()
|
||||
.applyErredScreenBackground()
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
@ -71,7 +90,7 @@ public struct RestoreInfoView: View {
|
|||
.padding(.leading, 8)
|
||||
|
||||
Text(text)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 12))
|
||||
.zFont(size: 14, style: Design.Text.primary)
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
|
|
|
@ -168,6 +168,25 @@ extension Root {
|
|||
case .settings(.path(.element(id: _, action: .resetZashi(.deleteTapped)))):
|
||||
return .send(.initialization(.resetZashiRequest))
|
||||
|
||||
// MARK: - Restore Wallet Coord Flow from Onboarding
|
||||
|
||||
case .onboarding(.restoreWalletCoordFlow(.path(.element(id: _, action: .restoreInfo(.gotItTapped))))):
|
||||
var leavesScreenOpen = false
|
||||
for element in state.onboardingState.restoreWalletCoordFlowState.path {
|
||||
if case .restoreInfo(let restoreInfoState) = element {
|
||||
leavesScreenOpen = restoreInfoState.isAcknowledged
|
||||
}
|
||||
}
|
||||
userDefaults.setValue(leavesScreenOpen, Constants.udLeavesScreenOpen)
|
||||
state.isRestoringWallet = true
|
||||
userDefaults.setValue(true, Constants.udIsRestoringWallet)
|
||||
state.$walletStatus.withLock { $0 = .restoring }
|
||||
return .concatenate(
|
||||
.send(.initialization(.initializeSDK(.restoreWallet))),
|
||||
.send(.initialization(.checkBackupPhraseValidation)),
|
||||
.send(.batteryStateChanged(nil))
|
||||
)
|
||||
|
||||
// MARK: - Scan Coord Flow
|
||||
|
||||
case .scanCoordFlow(.scan(.cancelTapped)):
|
||||
|
|
|
@ -19,6 +19,7 @@ import WalletStorage
|
|||
extension Root {
|
||||
public enum Constants {
|
||||
static let udIsRestoringWallet = "udIsRestoringWallet"
|
||||
static let udLeavesScreenOpen = "udLeaves_screen_open"
|
||||
static let noAuthenticationWithinXMinutes = 15
|
||||
}
|
||||
|
||||
|
@ -494,6 +495,7 @@ extension Root {
|
|||
state.splashAppeared = true
|
||||
state.isRestoringWallet = false
|
||||
userDefaults.remove(Constants.udIsRestoringWallet)
|
||||
userDefaults.remove(Constants.udLeavesScreenOpen)
|
||||
flexaHandler.signOut()
|
||||
userStoredPreferences.removeAll()
|
||||
try? readTransactionsStorage.resetZashi()
|
||||
|
|
|
@ -492,7 +492,8 @@ public struct Root {
|
|||
return .none
|
||||
|
||||
case .batteryStateChanged:
|
||||
autolockHandler.value(state.walletStatus == .restoring)
|
||||
var leavesScreenOpen = userDefaults.objectForKey(Constants.udLeavesScreenOpen) as? Bool ?? false
|
||||
autolockHandler.value(state.walletStatus == .restoring && leavesScreenOpen)
|
||||
return .none
|
||||
|
||||
case .cancelAllRunningEffects:
|
||||
|
@ -646,15 +647,7 @@ extension AlertState where Action == Root.Action {
|
|||
TextState(L10n.Root.Initialization.Alert.Wipe.message)
|
||||
}
|
||||
}
|
||||
|
||||
public static func successfullyRecovered() -> AlertState {
|
||||
AlertState {
|
||||
TextState(L10n.General.success)
|
||||
} message: {
|
||||
TextState(L10n.ImportWallet.Alert.Success.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static func differentSeed() -> AlertState {
|
||||
AlertState {
|
||||
TextState(L10n.General.Alert.warning)
|
||||
|
|
|
@ -70,7 +70,7 @@ public struct AdvancedSettingsView: View {
|
|||
}
|
||||
.padding(.top, 24)
|
||||
.padding(.horizontal, 4)
|
||||
.walletStatusPanel()
|
||||
//..walletstatusPanel()
|
||||
|
||||
Spacer()
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ public struct IntegrationsView: View {
|
|||
}
|
||||
.padding(.top, 24)
|
||||
.padding(.horizontal, 4)
|
||||
.walletStatusPanel()
|
||||
//..walletstatusPanel()
|
||||
.sheet(isPresented: $store.isInAppBrowserOn) {
|
||||
if let urlStr = store.inAppBrowserURL, let url = URL(string: urlStr) {
|
||||
InAppBrowserView(url: url)
|
||||
|
|
|
@ -167,7 +167,7 @@ public struct SettingsView: View {
|
|||
.zashiBack()
|
||||
.navigationBarHidden(!store.path.isEmpty)
|
||||
.screenTitle(L10n.Settings.title)
|
||||
.walletStatusPanel()
|
||||
//..walletstatusPanel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -218,7 +218,7 @@ public struct TabsView: View {
|
|||
)
|
||||
}
|
||||
)
|
||||
.walletStatusPanel()
|
||||
//..walletstatusPanel()
|
||||
.sheet(isPresented: $store.isInAppBrowserOn) {
|
||||
if let urlStr = store.inAppBrowserURL, let url = URL(string: urlStr) {
|
||||
InAppBrowserView(url: url)
|
||||
|
@ -539,7 +539,7 @@ public struct TabsView: View {
|
|||
// }
|
||||
// .animation(nil, value: store.selectedTab)
|
||||
// )
|
||||
// .walletStatusPanel()
|
||||
// //..walletstatusPanel()
|
||||
// .sheet(isPresented: $store.isInAppBrowserOn) {
|
||||
// if let url = URL(string: store.inAppBrowserURL) {
|
||||
// InAppBrowserView(url: url)
|
||||
|
|
|
@ -153,7 +153,7 @@ public struct TransactionDetailsView: View {
|
|||
}
|
||||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.walletStatusPanel(background: .transparent)
|
||||
//..walletstatusPanel(background: .transparent)
|
||||
.applyDefaultGradientScreenBackground()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@ public struct TransactionsManagerView: View {
|
|||
}
|
||||
}
|
||||
.disabled(store.transactions.isEmpty)
|
||||
.walletStatusPanel()
|
||||
//..walletstatusPanel()
|
||||
.applyScreenBackground()
|
||||
.listStyle(.plain)
|
||||
.onAppear { store.send(.onAppear) }
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// WalletBirthdayEstimateDateView.swift
|
||||
// Zashi
|
||||
//
|
||||
// Created by Lukáš Korba on 03-31-2025.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
import Generated
|
||||
import UIComponents
|
||||
|
||||
public struct WalletBirthdayEstimateDateView: View {
|
||||
@Perception.Bindable var store: StoreOf<WalletBirthday>
|
||||
|
||||
public init(store: StoreOf<WalletBirthday>) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
WithPerceptionTracking {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text(L10n.RestoreWallet.Birthday.EstimateDate.title)
|
||||
.zFont(.semiBold, size: 24, style: Design.Text.primary)
|
||||
.padding(.top, 40)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text(L10n.RestoreWallet.Birthday.EstimateDate.info)
|
||||
.zFont(size: 14, style: Design.Text.primary)
|
||||
.padding(.bottom, 32)
|
||||
|
||||
HStack {
|
||||
Picker("", selection: $store.selectedMonth) {
|
||||
ForEach(store.months, id: \.self) { month in
|
||||
Text(month)
|
||||
.zFont(size: 23, style: Design.Text.primary)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.wheel)
|
||||
|
||||
Picker("", selection: $store.selectedYear) {
|
||||
ForEach(store.years, id: \.self) { year in
|
||||
Text("\(String(year))")
|
||||
.zFont(size: 23, style: Design.Text.primary)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.wheel)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
ZashiButton(L10n.General.next) {
|
||||
store.send(.estimateHeightRequested)
|
||||
}
|
||||
.padding(.bottom, 24)
|
||||
}
|
||||
.onAppear { store.send(.onAppear) }
|
||||
.zashiBack()
|
||||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(
|
||||
trailing:
|
||||
Button {
|
||||
store.send(.helpSheetRequested)
|
||||
} label: {
|
||||
Asset.Assets.Icons.help.image
|
||||
.zImage(size: 24, style: Design.Text.primary)
|
||||
.padding(8)
|
||||
}
|
||||
)
|
||||
.screenHorizontalPadding()
|
||||
.applyScreenBackground()
|
||||
.screenTitle(L10n.ImportWallet.Button.restoreWallet)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
#Preview {
|
||||
WalletBirthdayEstimateDateView(store: WalletBirthday.initial)
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// WalletBirthdayEstimatedHeightView.swift
|
||||
// Zashi
|
||||
//
|
||||
// Created by Lukáš Korba on 03-31-2025.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
import Generated
|
||||
import UIComponents
|
||||
|
||||
public struct WalletBirthdayEstimatedHeightView: View {
|
||||
@Perception.Bindable var store: StoreOf<WalletBirthday>
|
||||
|
||||
public init(store: StoreOf<WalletBirthday>) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
WithPerceptionTracking {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text(L10n.RestoreWallet.Birthday.Estimated.title)
|
||||
.zFont(.semiBold, size: 24, style: Design.Text.primary)
|
||||
.padding(.top, 40)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text(L10n.RestoreWallet.Birthday.Estimated.info)
|
||||
.zFont(size: 14, style: Design.Text.primary)
|
||||
.padding(.bottom, 56)
|
||||
|
||||
VStack {
|
||||
Text(store.estimatedHeightString)
|
||||
.zFont(.semiBold, size: 48, style: Design.Text.primary)
|
||||
.padding(.bottom, 12)
|
||||
|
||||
ZashiButton(
|
||||
L10n.Receive.copy,
|
||||
type: .tertiary,
|
||||
infinityWidth: false,
|
||||
prefixView:
|
||||
Asset.Assets.copy.image
|
||||
.zImage(size: 20, style: Design.Btns.Tertiary.fg)
|
||||
) {
|
||||
store.send(.copyBirthdayTapped)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
Spacer()
|
||||
|
||||
ZashiButton(L10n.ImportWallet.Button.restoreWallet) {
|
||||
store.send(.restoreTapped)
|
||||
}
|
||||
.padding(.bottom, 24)
|
||||
}
|
||||
.zashiBack()
|
||||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(
|
||||
trailing:
|
||||
Button {
|
||||
store.send(.helpSheetRequested)
|
||||
} label: {
|
||||
Asset.Assets.Icons.help.image
|
||||
.zImage(size: 24, style: Design.Text.primary)
|
||||
.padding(8)
|
||||
}
|
||||
)
|
||||
.screenHorizontalPadding()
|
||||
.applyScreenBackground()
|
||||
.screenTitle(L10n.ImportWallet.Button.restoreWallet)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
#Preview {
|
||||
WalletBirthdayEstimateDateView(store: WalletBirthday.initial)
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
//
|
||||
// WalletBirthdayStore.swift
|
||||
// Zashi
|
||||
//
|
||||
// Created by Lukáš Korba on 03-31-2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ComposableArchitecture
|
||||
|
||||
import Generated
|
||||
import SDKSynchronizer
|
||||
import Utils
|
||||
import ZcashLightClientKit
|
||||
import Pasteboard
|
||||
import UIComponents
|
||||
import ZcashSDKEnvironment
|
||||
|
||||
@Reducer
|
||||
public struct WalletBirthday {
|
||||
enum Constants {
|
||||
static let startYear: Int = 2018
|
||||
static let startMonth: Int = 10
|
||||
}
|
||||
|
||||
@ObservableState
|
||||
public struct State: Equatable {
|
||||
public var birthday = ""
|
||||
public var estimatedHeight = BlockHeight(0)
|
||||
public var isValidBirthday = false
|
||||
public var months: [String] = []
|
||||
public var selectedMonth = ""
|
||||
public var selectedYear = Constants.startYear
|
||||
@Shared(.inMemory(.toast)) public var toast: Toast.Edge? = nil
|
||||
public var years: [Int] = []
|
||||
|
||||
public var estimatedHeightString: String {
|
||||
Zatoshi(Int64(estimatedHeight * 100_000_000)).decimalString()
|
||||
}
|
||||
|
||||
public init() { }
|
||||
}
|
||||
|
||||
public enum Action: BindableAction, Equatable {
|
||||
case binding(BindingAction<WalletBirthday.State>)
|
||||
case copyBirthdayTapped
|
||||
case estimateHeightReady
|
||||
case estimateHeightRequested
|
||||
case estimateHeightTapped
|
||||
case helpSheetRequested
|
||||
case onAppear
|
||||
case restoreTapped
|
||||
case updateMonths
|
||||
}
|
||||
|
||||
@Dependency(\.sdkSynchronizer) var sdkSynchronizer
|
||||
@Dependency(\.pasteboard) var pasteboard
|
||||
@Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
|
||||
|
||||
public init() { }
|
||||
|
||||
public var body: some Reducer<State, Action> {
|
||||
BindingReducer()
|
||||
|
||||
Reduce { state, action in
|
||||
switch action {
|
||||
case .onAppear:
|
||||
let currentYear = Calendar.current.component(.year, from: Date())
|
||||
state.years = Array(Constants.startYear...currentYear)
|
||||
state.estimatedHeight = zcashSDKEnvironment.network.constants.saplingActivationHeight
|
||||
return .send(.updateMonths)
|
||||
|
||||
case .binding(\.birthday):
|
||||
let saplingActivation = zcashSDKEnvironment.network.constants.saplingActivationHeight
|
||||
|
||||
if let birthdayHeight = BlockHeight(state.birthday), birthdayHeight >= saplingActivation {
|
||||
state.estimatedHeight = birthdayHeight
|
||||
state.isValidBirthday = true
|
||||
} else {
|
||||
state.isValidBirthday = false
|
||||
}
|
||||
return .none
|
||||
|
||||
case .binding(\.selectedYear):
|
||||
return .send(.updateMonths)
|
||||
|
||||
case .binding:
|
||||
return .none
|
||||
|
||||
case .restoreTapped:
|
||||
return .none
|
||||
|
||||
case .copyBirthdayTapped:
|
||||
pasteboard.setString(state.birthday.redacted)
|
||||
state.$toast.withLock { $0 = .top(L10n.General.copiedToTheClipboard) }
|
||||
return .none
|
||||
|
||||
case .updateMonths:
|
||||
let currentYear = Calendar.current.component(.year, from: Date())
|
||||
let currentMonth = Calendar.current.component(.month, from: Date())
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = Locale.current
|
||||
if state.selectedYear > Constants.startYear && state.selectedYear < currentYear {
|
||||
state.months = formatter.monthSymbols
|
||||
} else if state.selectedYear == Constants.startYear {
|
||||
state.months = formatter.monthSymbols.suffix(13 - Constants.startMonth)
|
||||
if !state.months.contains(state.selectedMonth) {
|
||||
if let first = state.months.first {
|
||||
state.selectedMonth = first
|
||||
}
|
||||
}
|
||||
} else if state.selectedYear == currentYear {
|
||||
state.months = Array(formatter.monthSymbols.prefix(currentMonth))
|
||||
if !state.months.contains(state.selectedMonth) {
|
||||
if let last = state.months.last {
|
||||
state.selectedMonth = last
|
||||
}
|
||||
}
|
||||
}
|
||||
return .none
|
||||
|
||||
case .helpSheetRequested:
|
||||
return .none
|
||||
|
||||
case .estimateHeightRequested:
|
||||
// compute date based on the picker state
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "MMMM yyyy"
|
||||
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
|
||||
if let date = dateFormatter.date(from: "\(state.selectedMonth) \(state.selectedYear)") {
|
||||
state.estimatedHeight = sdkSynchronizer.estimateBirthdayHeight(date)
|
||||
state.isValidBirthday = true
|
||||
return .send(.estimateHeightReady)
|
||||
} else {
|
||||
state.estimatedHeight = BlockHeight(0)
|
||||
state.isValidBirthday = false
|
||||
}
|
||||
return .none
|
||||
|
||||
case .estimateHeightReady:
|
||||
return .none
|
||||
|
||||
case .estimateHeightTapped:
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// WalletBirthdayView.swift
|
||||
// Zashi
|
||||
//
|
||||
// Created by Lukáš Korba on 03-31-2025.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
import Generated
|
||||
import UIComponents
|
||||
|
||||
public struct WalletBirthdayView: View {
|
||||
@Perception.Bindable var store: StoreOf<WalletBirthday>
|
||||
|
||||
public init(store: StoreOf<WalletBirthday>) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
WithPerceptionTracking {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text(L10n.ImportWallet.Birthday.title)
|
||||
.zFont(.semiBold, size: 24, style: Design.Text.primary)
|
||||
.padding(.top, 40)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text(L10n.RestoreWallet.Birthday.info)
|
||||
.zFont(size: 14, style: Design.Text.primary)
|
||||
.padding(.bottom, 32)
|
||||
|
||||
ZashiTextField(
|
||||
text: $store.birthday,
|
||||
placeholder: L10n.RestoreWallet.Birthday.placeholder,
|
||||
title: L10n.RestoreWallet.Birthday.title
|
||||
)
|
||||
.padding(.bottom, 6)
|
||||
.keyboardType(.numberPad)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
|
||||
Text(L10n.RestoreWallet.Birthday.fieldInfo)
|
||||
.zFont(size: 12, style: Design.Text.tertiary)
|
||||
|
||||
Spacer()
|
||||
|
||||
ZashiButton(
|
||||
L10n.RestoreWallet.Birthday.estimate,
|
||||
type: .ghost
|
||||
) {
|
||||
store.send(.estimateHeightTapped)
|
||||
}
|
||||
.padding(.bottom, 12)
|
||||
|
||||
ZashiButton(L10n.ImportWallet.Button.restoreWallet) {
|
||||
store.send(.restoreTapped)
|
||||
}
|
||||
.disabled(!store.isValidBirthday)
|
||||
.padding(.bottom, 24)
|
||||
}
|
||||
.zashiBack()
|
||||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(
|
||||
trailing:
|
||||
Button {
|
||||
store.send(.helpSheetRequested)
|
||||
} label: {
|
||||
Asset.Assets.Icons.help.image
|
||||
.zImage(size: 24, style: Design.Text.primary)
|
||||
.padding(8)
|
||||
}
|
||||
)
|
||||
.screenHorizontalPadding()
|
||||
.applyScreenBackground()
|
||||
.screenTitle(L10n.ImportWallet.Button.restoreWallet)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
#Preview {
|
||||
WalletBirthdayView(store: WalletBirthday.initial)
|
||||
}
|
||||
|
||||
// MARK: - Store
|
||||
|
||||
extension WalletBirthday {
|
||||
public static var initial = StoreOf<WalletBirthday>(
|
||||
initialState: .initial
|
||||
) {
|
||||
WalletBirthday()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Placeholders
|
||||
|
||||
extension WalletBirthday.State {
|
||||
public static let initial = WalletBirthday.State()
|
||||
}
|
|
@ -12,6 +12,8 @@ import UIComponents
|
|||
import WhatsNewProvider
|
||||
|
||||
public struct WhatsNewView: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
@Perception.Bindable var store: StoreOf<WhatsNew>
|
||||
|
||||
public init(store: StoreOf<WhatsNew>) {
|
||||
|
@ -60,7 +62,7 @@ public struct WhatsNewView: View {
|
|||
Spacer()
|
||||
}
|
||||
|
||||
ZashiText(withAttributedString: previewText)
|
||||
ZashiText(withAttributedString: previewText, colorScheme: colorScheme)
|
||||
.zFont(size: 14, style: Design.Text.primary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.accentColor(Asset.Colors.primary.color)
|
||||
|
|
|
@ -368,6 +368,12 @@ public enum L10n {
|
|||
/// Upgrading databases…
|
||||
public static let migratingDatabases = L10n.tr("Localizable", "home.migratingDatabases", fallback: "Upgrading databases…")
|
||||
}
|
||||
public enum HomeScreen {
|
||||
/// More
|
||||
public static let more = L10n.tr("Localizable", "homeScreen.more", fallback: "More")
|
||||
/// Scan
|
||||
public static let scan = L10n.tr("Localizable", "homeScreen.scan", fallback: "Scan")
|
||||
}
|
||||
public enum ImportWallet {
|
||||
/// Enter secret
|
||||
/// recovery phrase
|
||||
|
@ -669,25 +675,65 @@ public enum L10n {
|
|||
}
|
||||
}
|
||||
public enum RestoreInfo {
|
||||
/// Keep screen on while restoring
|
||||
public static let checkbox = L10n.tr("Localizable", "restoreInfo.checkbox", fallback: "Keep screen on while restoring")
|
||||
/// Got it!
|
||||
public static let gotIt = L10n.tr("Localizable", "restoreInfo.gotIt", fallback: "Got it!")
|
||||
/// Note:
|
||||
public static let note = L10n.tr("Localizable", "restoreInfo.note", fallback: "Note: ")
|
||||
/// During the initial sync your funds cannot be sent or spent. Depending on the age of your wallet, it may take a few hours to fully sync.
|
||||
public static let noteInfo = L10n.tr("Localizable", "restoreInfo.noteInfo", fallback: "During the initial sync your funds cannot be sent or spent. Depending on the age of your wallet, it may take a few hours to fully sync.")
|
||||
/// Your wallet has been successfully restored and is now syncing
|
||||
public static let subTitle = L10n.tr("Localizable", "restoreInfo.subTitle", fallback: "Your wallet has been successfully restored and is now syncing")
|
||||
/// Zashi needs to stay open in order to continue syncing.
|
||||
public static let tip1 = L10n.tr("Localizable", "restoreInfo.tip1", fallback: "Zashi needs to stay open in order to continue syncing.")
|
||||
/// To prevent interruption, plug your open phone into a power source.
|
||||
public static let tip2 = L10n.tr("Localizable", "restoreInfo.tip2", fallback: "To prevent interruption, plug your open phone into a power source.")
|
||||
/// Keep your open phone in a secure place.
|
||||
public static let tip3 = L10n.tr("Localizable", "restoreInfo.tip3", fallback: "Keep your open phone in a secure place.")
|
||||
/// Syncing Tips:
|
||||
public static let tips = L10n.tr("Localizable", "restoreInfo.tips", fallback: "Syncing Tips:")
|
||||
/// Your funds cannot be spent with Zashi until your wallet is fully restored.
|
||||
public static let noteInfo = L10n.tr("Localizable", "restoreInfo.noteInfo", fallback: "Your funds cannot be spent with Zashi until your wallet is fully restored.")
|
||||
/// Your wallet is being restored.
|
||||
public static let subTitle = L10n.tr("Localizable", "restoreInfo.subTitle", fallback: "Your wallet is being restored.")
|
||||
/// Keep the Zashi app open on an active phone screen.
|
||||
public static let tip1 = L10n.tr("Localizable", "restoreInfo.tip1", fallback: "Keep the Zashi app open on an active phone screen.")
|
||||
/// To prevent your phone screen from going dark, turn off power-saving mode and keep your phone plugged in.
|
||||
public static let tip2 = L10n.tr("Localizable", "restoreInfo.tip2", fallback: "To prevent your phone screen from going dark, turn off power-saving mode and keep your phone plugged in.")
|
||||
/// Zashi is scanning the blockchain to retrieve your transactions. Older wallets can take hours to restore. Follow these steps to prevent interruption:
|
||||
public static let tips = L10n.tr("Localizable", "restoreInfo.tips", fallback: "Zashi is scanning the blockchain to retrieve your transactions. Older wallets can take hours to restore. Follow these steps to prevent interruption:")
|
||||
/// Keep Zashi open!
|
||||
public static let title = L10n.tr("Localizable", "restoreInfo.title", fallback: "Keep Zashi open!")
|
||||
}
|
||||
public enum RestoreWallet {
|
||||
/// Please type in your 24-word secret recovery phrase in the correct order.
|
||||
public static let info = L10n.tr("Localizable", "restoreWallet.info", fallback: "Please type in your 24-word secret recovery phrase in the correct order.")
|
||||
/// Secret Recovery Phrase
|
||||
public static let title = L10n.tr("Localizable", "restoreWallet.title", fallback: "Secret Recovery Phrase")
|
||||
public enum Birthday {
|
||||
/// Estimate my block height
|
||||
public static let estimate = L10n.tr("Localizable", "restoreWallet.birthday.estimate", fallback: "Estimate my block height")
|
||||
/// Wallet Birthday Height is the point in time when your wallet was created.
|
||||
public static let fieldInfo = L10n.tr("Localizable", "restoreWallet.birthday.fieldInfo", fallback: "Wallet Birthday Height is the point in time when your wallet was created.")
|
||||
/// Entering your Wallet Birthday Height helps speed up the restore process.
|
||||
public static let info = L10n.tr("Localizable", "restoreWallet.birthday.info", fallback: "Entering your Wallet Birthday Height helps speed up the restore process.")
|
||||
/// Enter number
|
||||
public static let placeholder = L10n.tr("Localizable", "restoreWallet.birthday.placeholder", fallback: "Enter number")
|
||||
/// Block Height
|
||||
public static let title = L10n.tr("Localizable", "restoreWallet.birthday.title", fallback: "Block Height")
|
||||
public enum EstimateDate {
|
||||
/// Entering the block height at which your wallet was created reduces the number of blocks that need to be scanned to recover your wallet.
|
||||
public static let info = L10n.tr("Localizable", "restoreWallet.birthday.estimateDate.info", fallback: "Entering the block height at which your wallet was created reduces the number of blocks that need to be scanned to recover your wallet.")
|
||||
/// First Wallet Transaction
|
||||
public static let title = L10n.tr("Localizable", "restoreWallet.birthday.estimateDate.title", fallback: "First Wallet Transaction")
|
||||
/// If you’re not sure, choose an earlier date.
|
||||
public static let warning = L10n.tr("Localizable", "restoreWallet.birthday.estimateDate.warning", fallback: "If you’re not sure, choose an earlier date.")
|
||||
}
|
||||
public enum Estimated {
|
||||
/// Zashi will scan and recover all transactions made after the following block number.
|
||||
public static let info = L10n.tr("Localizable", "restoreWallet.birthday.estimated.info", fallback: "Zashi will scan and recover all transactions made after the following block number.")
|
||||
/// Estimated Block Height
|
||||
public static let title = L10n.tr("Localizable", "restoreWallet.birthday.estimated.title", fallback: "Estimated Block Height")
|
||||
}
|
||||
}
|
||||
public enum Help {
|
||||
/// ^[The Wallet Birthday Height](style: 'boldPrimary') is the block height (block # in the blockchain) at which your wallet was created. If you ever lose access to your Zashi app and need to recover your funds, providing the block height along with your recovery phrase can significantly speed up the process.
|
||||
public static let birthday = L10n.tr("Localizable", "restoreWallet.help.birthday", fallback: "^[The Wallet Birthday Height](style: 'boldPrimary') is the block height (block # in the blockchain) at which your wallet was created. If you ever lose access to your Zashi app and need to recover your funds, providing the block height along with your recovery phrase can significantly speed up the process.")
|
||||
/// ^[The Secret Recovery Phrase](style: 'boldPrimary') is a unique set of 24 words, appearing in a precise order. It can be used to gain full control of your funds from any device via any Zcash wallet app. Think of it as the master key to your wallet. It is stored in Zashi’s Advanced Settings.
|
||||
public static let phrase = L10n.tr("Localizable", "restoreWallet.help.phrase", fallback: "^[The Secret Recovery Phrase](style: 'boldPrimary') is a unique set of 24 words, appearing in a precise order. It can be used to gain full control of your funds from any device via any Zcash wallet app. Think of it as the master key to your wallet. It is stored in Zashi’s Advanced Settings.")
|
||||
/// Need to know more?
|
||||
public static let title = L10n.tr("Localizable", "restoreWallet.help.title", fallback: "Need to know more?")
|
||||
}
|
||||
}
|
||||
public enum Root {
|
||||
public enum Debug {
|
||||
/// Startup
|
||||
|
|
22
modules/Sources/Generated/Resources/Assets.xcassets/Illustrations/connect.imageset/Contents.json
vendored
Normal file
22
modules/Sources/Generated/Resources/Assets.xcassets/Illustrations/connect.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "connect.png",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "connectDark.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
modules/Sources/Generated/Resources/Assets.xcassets/Illustrations/connect.imageset/connect.png
vendored
Normal file
BIN
modules/Sources/Generated/Resources/Assets.xcassets/Illustrations/connect.imageset/connect.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
BIN
modules/Sources/Generated/Resources/Assets.xcassets/Illustrations/connect.imageset/connectDark.png
vendored
Normal file
BIN
modules/Sources/Generated/Resources/Assets.xcassets/Illustrations/connect.imageset/connectDark.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
12
modules/Sources/Generated/Resources/Assets.xcassets/icons/help.imageset/Contents.json
vendored
Normal file
12
modules/Sources/Generated/Resources/Assets.xcassets/icons/help.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "help.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
modules/Sources/Generated/Resources/Assets.xcassets/icons/help.imageset/help.png
vendored
Normal file
BIN
modules/Sources/Generated/Resources/Assets.xcassets/icons/help.imageset/help.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
|
@ -61,13 +61,13 @@
|
|||
|
||||
// MARK: - Restore Info
|
||||
"restoreInfo.title" = "Keep Zashi open!";
|
||||
"restoreInfo.subTitle" = "Your wallet has been successfully restored and is now syncing";
|
||||
"restoreInfo.tips" = "Syncing Tips:";
|
||||
"restoreInfo.tip1" = "Zashi needs to stay open in order to continue syncing.";
|
||||
"restoreInfo.tip2" = "To prevent interruption, plug your open phone into a power source.";
|
||||
"restoreInfo.tip3" = "Keep your open phone in a secure place.";
|
||||
"restoreInfo.subTitle" = "Your wallet is being restored.";
|
||||
"restoreInfo.tips" = "Zashi is scanning the blockchain to retrieve your transactions. Older wallets can take hours to restore. Follow these steps to prevent interruption:";
|
||||
"restoreInfo.tip1" = "Keep the Zashi app open on an active phone screen.";
|
||||
"restoreInfo.tip2" = "To prevent your phone screen from going dark, turn off power-saving mode and keep your phone plugged in.";
|
||||
//"restoreInfo.tip3" = "Keep your open phone in a secure place.";
|
||||
"restoreInfo.note" = "Note: ";
|
||||
"restoreInfo.noteInfo" = "During the initial sync your funds cannot be sent or spent. Depending on the age of your wallet, it may take a few hours to fully sync.";
|
||||
"restoreInfo.noteInfo" = "Your funds cannot be spent with Zashi until your wallet is fully restored.";
|
||||
"restoreInfo.gotIt" = "Got it!";
|
||||
|
||||
// MARK: - Tabs
|
||||
|
@ -566,3 +566,25 @@
|
|||
|
||||
// More actions
|
||||
"more.options" = "More Options";
|
||||
|
||||
// Home screen
|
||||
"homeScreen.scan" = "Scan";
|
||||
"homeScreen.more" = "More";
|
||||
|
||||
// Onboarding: Restore Wallet
|
||||
"restoreWallet.title" = "Secret Recovery Phrase";
|
||||
"restoreWallet.info" = "Please type in your 24-word secret recovery phrase in the correct order.";
|
||||
"restoreWallet.help.title" = "Need to know more?";
|
||||
"restoreWallet.help.phrase" = "^[The Secret Recovery Phrase](style: 'boldPrimary') is a unique set of 24 words, appearing in a precise order. It can be used to gain full control of your funds from any device via any Zcash wallet app. Think of it as the master key to your wallet. It is stored in Zashi’s Advanced Settings.";
|
||||
"restoreWallet.help.birthday" = "^[The Wallet Birthday Height](style: 'boldPrimary') is the block height (block # in the blockchain) at which your wallet was created. If you ever lose access to your Zashi app and need to recover your funds, providing the block height along with your recovery phrase can significantly speed up the process.";
|
||||
"restoreWallet.birthday.placeholder" = "Enter number";
|
||||
"restoreWallet.birthday.title" = "Block Height";
|
||||
"restoreWallet.birthday.info" = "Entering your Wallet Birthday Height helps speed up the restore process.";
|
||||
"restoreWallet.birthday.fieldInfo" = "Wallet Birthday Height is the point in time when your wallet was created.";
|
||||
"restoreWallet.birthday.estimate" = "Estimate my block height";
|
||||
"restoreWallet.birthday.estimateDate.title" = "First Wallet Transaction";
|
||||
"restoreWallet.birthday.estimateDate.info" = "Entering the block height at which your wallet was created reduces the number of blocks that need to be scanned to recover your wallet.";
|
||||
"restoreWallet.birthday.estimateDate.warning" = "If you’re not sure, choose an earlier date.";
|
||||
"restoreWallet.birthday.estimated.title" = "Estimated Block Height";
|
||||
"restoreWallet.birthday.estimated.info" = "Zashi will scan and recover all transactions made after the following block number.";
|
||||
"restoreInfo.checkbox" = "Keep screen on while restoring";
|
||||
|
|
|
@ -32,6 +32,7 @@ public enum Asset {
|
|||
public static let fly = ImageAsset(name: "Fly")
|
||||
public static let flyReceived = ImageAsset(name: "FlyReceived")
|
||||
public enum Illustrations {
|
||||
public static let connect = ImageAsset(name: "connect")
|
||||
public static let emptyState = ImageAsset(name: "emptyState")
|
||||
public static let failure1 = ImageAsset(name: "failure1")
|
||||
public static let failure2 = ImageAsset(name: "failure2")
|
||||
|
@ -89,6 +90,7 @@ public enum Asset {
|
|||
public static let filter = ImageAsset(name: "filter")
|
||||
public static let flashOff = ImageAsset(name: "flashOff")
|
||||
public static let flashOn = ImageAsset(name: "flashOn")
|
||||
public static let help = ImageAsset(name: "help")
|
||||
public static let imageLibrary = ImageAsset(name: "imageLibrary")
|
||||
public static let integrations = ImageAsset(name: "integrations")
|
||||
public static let key = ImageAsset(name: "key")
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// ZashiSheet.swift
|
||||
// modules
|
||||
//
|
||||
// Created by Lukáš Korba on 31.03.2025.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
public struct ZashiSheetModifier<SheetContent: View>: ViewModifier {
|
||||
@Binding public var isPresented: Bool
|
||||
@State var sheetHeight: CGFloat = .zero
|
||||
var sheetContent: SheetContent
|
||||
|
||||
public func body(content: Content) -> some View {
|
||||
content
|
||||
.sheet(isPresented: $isPresented) {
|
||||
if #available(iOS 16.0, *) {
|
||||
mainBody()
|
||||
.presentationDetents([.height(sheetHeight)])
|
||||
.presentationDragIndicator(.visible)
|
||||
} else {
|
||||
mainBody(stickToBottom: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func mainBody(stickToBottom: Bool = false) -> some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
if stickToBottom {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
sheetContent
|
||||
}
|
||||
.background {
|
||||
GeometryReader { proxy in
|
||||
Color.clear
|
||||
.task {
|
||||
sheetHeight = proxy.size.height
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
public func zashiSheet(isPresented: Binding<Bool>, content: @escaping () -> some View) -> some View {
|
||||
modifier(
|
||||
ZashiSheetModifier(isPresented: isPresented, sheetContent: content())
|
||||
)
|
||||
}
|
||||
}
|
|
@ -81,7 +81,7 @@ private struct WalletStatusPanel: View {
|
|||
Text("Hello, World")
|
||||
}
|
||||
.padding(.vertical, 1)
|
||||
.walletStatusPanel()
|
||||
//..walletstatusPanel()
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(
|
||||
trailing: Text("M")
|
||||
|
|
|
@ -15,22 +15,29 @@ public struct ZashiText: View {
|
|||
Text(attributedString)
|
||||
}
|
||||
|
||||
public init(withAttributedString attributedString: AttributedString) {
|
||||
self.attributedString = ZashiText.annotateRainbowColors(from: attributedString)
|
||||
public init(withAttributedString attributedString: AttributedString, colorScheme: ColorScheme) {
|
||||
self.attributedString = AttributedString("")
|
||||
|
||||
self.attributedString = ZashiText.annotateStyle(from: attributedString, colorScheme: colorScheme)
|
||||
}
|
||||
|
||||
public init(_ localizedKey: String.LocalizationValue) {
|
||||
attributedString = ZashiText.annotateRainbowColors(
|
||||
from: AttributedString(localized: localizedKey, including: \.zashiApp))
|
||||
public init(_ localizedKey: String.LocalizationValue, colorScheme: ColorScheme) {
|
||||
self.attributedString = AttributedString("")
|
||||
|
||||
self.attributedString = ZashiText.annotateStyle(
|
||||
from: AttributedString(localized: localizedKey, including: \.zashiApp), colorScheme: colorScheme)
|
||||
}
|
||||
|
||||
private static func annotateRainbowColors(from source: AttributedString) -> AttributedString {
|
||||
private static func annotateStyle(from source: AttributedString, colorScheme: ColorScheme) -> AttributedString {
|
||||
var attrString = source
|
||||
for run in attrString.runs {
|
||||
if let zStyle = run.zStyle {
|
||||
switch zStyle {
|
||||
case .bold:
|
||||
attrString[run.range].font = .system(size: 14, weight: .bold)
|
||||
case .boldPrimary:
|
||||
attrString[run.range].font = .system(size: 14, weight: .bold)
|
||||
attrString[run.range].foregroundColor = Design.Text.primary.color(colorScheme)
|
||||
case .italic:
|
||||
attrString[run.range].font = .system(size: 14).italic()
|
||||
case .boldItalic:
|
||||
|
@ -47,6 +54,7 @@ public struct ZashiText: View {
|
|||
enum ZashiTextAttribute: CodableAttributedStringKey, MarkdownDecodableAttributedStringKey {
|
||||
enum Value: String, Codable, Hashable {
|
||||
case bold
|
||||
case boldPrimary
|
||||
case italic
|
||||
case boldItalic
|
||||
case link
|
||||
|
@ -69,13 +77,8 @@ extension AttributeDynamicLookup {
|
|||
}
|
||||
}
|
||||
|
||||
struct RainbowText_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
guard let previewText = try? AttributedString(
|
||||
markdown: "Some ^[bold](style: 'bold') ^[italic](style: 'italic') ^[boldItalic](style: 'boldItalic') [link example](https://electriccoin.co) text.",
|
||||
including: \.zashiApp) else {
|
||||
return ZashiText(withAttributedString: "Couldn't load the preview text.")
|
||||
}
|
||||
return ZashiText(withAttributedString: previewText)
|
||||
}
|
||||
}
|
||||
// Example:
|
||||
//let previewText = try? AttributedString(
|
||||
// markdown: "Some ^[bold](style: 'bold') ^[italic](style: 'italic') ^[boldItalic](style: 'boldItalic') [link example](https://electriccoin.co) text.",
|
||||
// including: \.zashiApp)
|
||||
//ZashiText(withAttributedString: previewText)
|
||||
|
|
|
@ -12,15 +12,18 @@ public struct ZashiToggle: View {
|
|||
@Binding var isOn: Bool
|
||||
let label: String
|
||||
let textColor: Color
|
||||
let textSize: CGFloat
|
||||
|
||||
public init(
|
||||
isOn: Binding<Bool>,
|
||||
label: String = "",
|
||||
textColor: Color = Asset.Colors.primary.color
|
||||
textColor: Color = Asset.Colors.primary.color,
|
||||
textSize: CGFloat = 14
|
||||
) {
|
||||
self._isOn = isOn
|
||||
self.label = label
|
||||
self.textColor = textColor
|
||||
self.textSize = textSize
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
|
@ -33,7 +36,7 @@ public struct ZashiToggle: View {
|
|||
.padding(.trailing, 8)
|
||||
|
||||
Text(label)
|
||||
.zFont(.medium, size: 14, style: Design.Text.primary)
|
||||
.zFont(.medium, size: textSize, style: Design.Text.primary)
|
||||
.multilineTextAlignment(.leading)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,13 +61,13 @@
|
|||
|
||||
// MARK: - Restore Info
|
||||
"restoreInfo.title" = "Keep Zashi open!";
|
||||
"restoreInfo.subTitle" = "Your wallet has been successfully restored and is now syncing";
|
||||
"restoreInfo.tips" = "Syncing Tips:";
|
||||
"restoreInfo.tip1" = "Zashi needs to stay open in order to continue syncing.";
|
||||
"restoreInfo.tip2" = "To prevent interruption, plug your open phone into a power source.";
|
||||
"restoreInfo.tip3" = "Keep your open phone in a secure place.";
|
||||
"restoreInfo.subTitle" = "Your wallet is being restored.";
|
||||
"restoreInfo.tips" = "Zashi is scanning the blockchain to retrieve your transactions. Older wallets can take hours to restore. Follow these steps to prevent interruption:";
|
||||
"restoreInfo.tip1" = "Keep the Zashi app open on an active phone screen.";
|
||||
"restoreInfo.tip2" = "To prevent your phone screen from going dark, turn off power-saving mode and keep your phone plugged in.";
|
||||
//"restoreInfo.tip3" = "Keep your open phone in a secure place.";
|
||||
"restoreInfo.note" = "Note: ";
|
||||
"restoreInfo.noteInfo" = "During the initial sync your funds cannot be sent or spent. Depending on the age of your wallet, it may take a few hours to fully sync.";
|
||||
"restoreInfo.noteInfo" = "Your funds cannot be spent with Zashi until your wallet is fully restored.";
|
||||
"restoreInfo.gotIt" = "Got it!";
|
||||
|
||||
// MARK: - Tabs
|
||||
|
@ -566,3 +566,25 @@
|
|||
|
||||
// More actions
|
||||
"more.options" = "More Options";
|
||||
|
||||
// Home screen
|
||||
"homeScreen.scan" = "Scan";
|
||||
"homeScreen.more" = "More";
|
||||
|
||||
// Onboarding: Restore Wallet
|
||||
"restoreWallet.title" = "Secret Recovery Phrase";
|
||||
"restoreWallet.info" = "Please type in your 24-word secret recovery phrase in the correct order.";
|
||||
"restoreWallet.help.title" = "Need to know more?";
|
||||
"restoreWallet.help.phrase" = "^[The Secret Recovery Phrase](style: 'boldPrimary') is a unique set of 24 words, appearing in a precise order. It can be used to gain full control of your funds from any device via any Zcash wallet app. Think of it as the master key to your wallet. It is stored in Zashi’s Advanced Settings.";
|
||||
"restoreWallet.help.birthday" = "^[The Wallet Birthday Height](style: 'boldPrimary') is the block height (block # in the blockchain) at which your wallet was created. If you ever lose access to your Zashi app and need to recover your funds, providing the block height along with your recovery phrase can significantly speed up the process.";
|
||||
"restoreWallet.birthday.placeholder" = "Enter number";
|
||||
"restoreWallet.birthday.title" = "Block Height";
|
||||
"restoreWallet.birthday.info" = "Entering your Wallet Birthday Height helps speed up the restore process.";
|
||||
"restoreWallet.birthday.fieldInfo" = "Wallet Birthday Height is the point in time when your wallet was created.";
|
||||
"restoreWallet.birthday.estimate" = "Estimate my block height";
|
||||
"restoreWallet.birthday.estimateDate.title" = "First Wallet Transaction";
|
||||
"restoreWallet.birthday.estimateDate.info" = "Entering the block height at which your wallet was created reduces the number of blocks that need to be scanned to recover your wallet.";
|
||||
"restoreWallet.birthday.estimateDate.warning" = "If you’re not sure, choose an earlier date.";
|
||||
"restoreWallet.birthday.estimated.title" = "Estimated Block Height";
|
||||
"restoreWallet.birthday.estimated.info" = "Zashi will scan and recover all transactions made after the following block number.";
|
||||
"restoreInfo.checkbox" = "Keep screen on while restoring";
|
||||
|
|
Loading…
Reference in New Issue