BUG: broken seek of the cursor in the restore wallet inputs

This commit is contained in:
Lukas Korba 2025-01-07 12:20:51 +01:00
parent f105a1adf4
commit 6bbdf05abd
8 changed files with 34 additions and 73 deletions

View File

@ -31,7 +31,7 @@ public struct ImportBirthdayView: View {
Text(L10n.ImportWallet.optionalBirthday)
TextField("", text: store.bindingForRedactableBirthday(store.birthdayHeight))
TextField("", text: $store.birthdayHeight)
.frame(height: 40)
.font(.custom(FontFamily.Inter.semiBold.name, size: 25))
.focused($isFocused)

View File

@ -25,10 +25,10 @@ public struct ImportWallet {
}
@Presents public var alert: AlertState<Action>?
public var birthdayHeight = RedactableString.empty
public var birthdayHeight = ""
public var birthdayHeightValue: RedactableBlockHeight?
public var destination: Destination?
public var importedSeedPhrase = RedactableString.empty
public var importedSeedPhrase = ""
public var isValidMnemonic = false
public var isValidNumberOfWords = false
public var maxWordsCount = 0
@ -46,15 +46,15 @@ public struct ImportWallet {
public var isValidForm: Bool {
isValidMnemonic &&
(birthdayHeight.data.isEmpty ||
(!birthdayHeight.data.isEmpty && birthdayHeightValue != nil))
(birthdayHeight.isEmpty ||
(!birthdayHeight.isEmpty && birthdayHeightValue != nil))
}
public init(
birthdayHeight: RedactableString = .empty,
birthdayHeight: String = "",
birthdayHeightValue: RedactableBlockHeight? = nil,
destination: Destination? = nil,
importedSeedPhrase: RedactableString = .empty,
importedSeedPhrase: String = "",
isValidMnemonic: Bool = false,
isValidNumberOfWords: Bool = false,
maxWordsCount: Int = 0,
@ -76,7 +76,6 @@ public struct ImportWallet {
public enum Action: BindableAction, Equatable {
case alert(PresentationAction<Action>)
case binding(BindingAction<ImportWallet.State>)
case birthdayInputChanged(RedactableString)
case importPrivateOrViewingKey
case initializeSDK
case nextPressed
@ -84,7 +83,6 @@ public struct ImportWallet {
case restoreInfo(RestoreInfo.Action)
case restoreInfoRequested(Bool)
case restoreWallet
case seedPhraseInputChanged(RedactableString)
case successfullyRecovered
case updateDestination(ImportWallet.State.Destination?)
}
@ -107,17 +105,16 @@ public struct ImportWallet {
case .onAppear:
state.maxWordsCount = zcashSDKEnvironment.mnemonicWordsMaxCount
return .none
case .binding:
case .binding(\.importedSeedPhrase):
return .none
case .seedPhraseInputChanged(let redactedSeedPhrase):
state.importedSeedPhrase = redactedSeedPhrase
state.wordsCount = state.importedSeedPhrase.data.split(separator: " ").count
case .binding(\.importedSeedPhrase):
state.wordsCount = state.importedSeedPhrase.split(separator: " ").count
state.isValidNumberOfWords = state.wordsCount == state.maxWordsCount
// is the mnemonic valid one?
do {
try mnemonic.isValid(state.importedSeedPhrase.data)
try mnemonic.isValid(state.importedSeedPhrase)
} catch {
state.isValidMnemonic = false
return .none
@ -125,18 +122,19 @@ public struct ImportWallet {
state.isValidMnemonic = true
return .none
case .birthdayInputChanged(let redactedBirthday):
case .binding(\.birthdayHeight):
let saplingActivation = zcashSDKEnvironment.network.constants.saplingActivationHeight
state.birthdayHeight = redactedBirthday
if let birthdayHeight = BlockHeight(state.birthdayHeight.data), birthdayHeight >= saplingActivation {
if let birthdayHeight = BlockHeight(state.birthdayHeight), birthdayHeight >= saplingActivation {
state.birthdayHeightValue = birthdayHeight.redacted
} else {
state.birthdayHeightValue = nil
}
return .none
case .binding:
return .none
case .alert(.presented(let action)):
return Effect.send(action)
@ -160,19 +158,19 @@ public struct ImportWallet {
case .restoreWallet:
do {
// validate the seed
try mnemonic.isValid(state.importedSeedPhrase.data)
try mnemonic.isValid(state.importedSeedPhrase)
// store it to the keychain, if the user did not input a height,
// fall back to sapling activation
let birthday = state.birthdayHeightValue ?? zcashSDKEnvironment.network.constants.saplingActivationHeight.redacted
try walletStorage.importWallet(state.importedSeedPhrase.data, birthday.data, .english, false)
try walletStorage.importWallet(state.importedSeedPhrase, birthday.data, .english, false)
// update the backup phrase validation flag
try walletStorage.markUserPassedPhraseBackupTest(true)
state.birthdayHeight = .empty
state.importedSeedPhrase = .empty
state.birthdayHeight = ""
state.importedSeedPhrase = ""
// notify user
return .concatenate(

View File

@ -48,7 +48,7 @@ public struct ImportWalletView: View {
.padding(.horizontal, 10)
WithPerceptionTracking {
TextEditor(text: store.bindingForRedactableSeedPhrase(store.importedSeedPhrase))
TextEditor(text: $store.importedSeedPhrase)
.autocapitalization(.none)
.padding(8)
.background {
@ -74,7 +74,7 @@ public struct ImportWalletView: View {
}
.overlay {
WithPerceptionTracking {
if store.importedSeedPhrase.data.isEmpty {
if store.importedSeedPhrase.isEmpty {
HStack {
VStack {
Text(L10n.ImportWallet.enterPlaceholder)
@ -209,20 +209,6 @@ extension StoreOf<ImportWallet> {
set: { self.send(.updateDestination($0 ? destination : nil)) }
)
}
func bindingForRedactableSeedPhrase(_ importedSeedPhrase: RedactableString) -> Binding<String> {
Binding<String>(
get: { importedSeedPhrase.data },
set: { self.send(.seedPhraseInputChanged($0.redacted)) }
)
}
func bindingForRedactableBirthday(_ birthdayHeight: RedactableString) -> Binding<String> {
Binding<String>(
get: { birthdayHeight.data },
set: { self.send(.birthdayInputChanged($0.redacted)) }
)
}
}
// MARK: - Placeholders

View File

@ -528,7 +528,7 @@ extension Root {
case .onboarding(.importWallet(.nextPressed)):
if state.appInitializationState == .keysMissing {
let seedPhrase = state.onboardingState.importWalletState.importedSeedPhrase.data
let seedPhrase = state.onboardingState.importWalletState.importedSeedPhrase
return .run { send in
do {
let seedBytes = try mnemonic.toSeed(seedPhrase)

View File

@ -91,8 +91,8 @@ extension SecantApp {
featureFlags = FeatureFlags()
#elseif SECANT_TESTNET
featureFlags = FeatureFlags(
flexa: true,
appLaunchBiometric: true,
flexa: true,
sendingScreen: true
)
#else

View File

@ -38,14 +38,12 @@ class ImportWalletTests: XCTestCase {
store.dependencies.mnemonic = .noOp
store.dependencies.mnemonic.isValid = { _ in throw "invalid mnemonic" }
let seedPhrase = "one two three".redacted
let seedPhrase = "one two three"
await store.send(.seedPhraseInputChanged(seedPhrase)) { state in
await store.send(.binding(.set(\.importedSeedPhrase, seedPhrase))) { state in
state.importedSeedPhrase = seedPhrase
state.wordsCount = 3
state.isValidMnemonic = false
}
await store.finish()
}

View File

@ -50,7 +50,9 @@ class SettingsTests: XCTestCase {
markUserPassedPhraseBackupTest: { _ in
throw WalletStorage.KeychainError.encoding
},
resetZashi: { }
resetZashi: { },
importAddressBookEncryptionKeys: { _ in },
exportAddressBookEncryptionKeys: { .empty }
)
let store = TestStore(
@ -91,30 +93,7 @@ class SettingsTests: XCTestCase {
await store.finish()
}
func testCopySupportEmail() async throws {
let store = TestStore(
initialState: .initial
) {
Settings()
}
let testPasteboard = PasteboardClient.testPasteboard
store.dependencies.pasteboard = testPasteboard
await store.send(.copyEmail)
let supportEmail = SupportDataGenerator.Constants.email
XCTAssertEqual(
testPasteboard.getString()?.data,
supportEmail,
"SettingsTests: `testCopySupportEmail` is expected to match the input `\(supportEmail)`"
)
await store.finish()
}
func testSupportDataGeneratorSubject() async throws {
let generator = SupportDataGenerator.generate()

View File

@ -174,7 +174,7 @@ class TabsTests: XCTestCase {
store.dependencies.sdkSynchronizer = .mock
let proposal = Proposal.testOnlyFakeProposal(totalFee: 10_000)
store.dependencies.sdkSynchronizer.proposeShielding = { _, _, _, _ in proposal }
store.dependencies.sdkSynchronizer.createProposedTransactions = { _, _ in .success }
store.dependencies.sdkSynchronizer.createProposedTransactions = { _, _ in .success(txIds: []) }
store.dependencies.derivationTool = .liveValue
store.dependencies.mnemonic = .mock
store.dependencies.walletStorage.exportWallet = { .placeholder }
@ -192,7 +192,7 @@ class TabsTests: XCTestCase {
}
let accountsBalances = [AccountUUID(id: Array<UInt8>(repeating: 0, count: 16)): AccountBalance(saplingBalance: .zero, orchardBalance: .zero, unshielded: .zero)]
await store.receive(.balanceBreakdown(.walletBalances(.balancesUpdated(accountsBalances))))
await store.receive(.balanceBreakdown(.walletBalances(.balanceUpdated(accountsBalances.first?.value))))
await store.receive(.balanceBreakdown(.updateBalances(accountsBalances)))