Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ABW-3655][ABW-3656] Entry seed phrase updates #1261

Merged
merged 6 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion RadixWallet/Core/DesignSystem/Components/AppTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public struct AppTextField<FocusValue: Hashable, Accessory: View, InnerAccessory

public var body: some View {
HStack(alignment: .textFieldAlignment, spacing: 0) {
VStack(alignment: .leading, spacing: .small1) {
VStack(alignment: .leading, spacing: Constants.appTextFieldSpacing) {
if primaryHeading != nil || secondaryHeading != nil {
HStack(spacing: 0) {
if let primaryHeading {
Expand Down Expand Up @@ -209,6 +209,10 @@ extension VerticalAlignment {
fileprivate static let textFieldAlignment = VerticalAlignment(TextFieldAlignment.self)
}

extension Constants {
static let appTextFieldSpacing: CGFloat = .small1
}

#if DEBUG
struct AppTextField_Previews: PreviewProvider {
static var previews: some View {
Expand Down
73 changes: 14 additions & 59 deletions RadixWallet/Features/ImportMnemonic/ImportMnemonic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ public struct ImportMnemonic: Sendable, FeatureReducer {
public typealias Words = IdentifiedArrayOf<ImportMnemonicWord.State>
public var words: Words

public var idOfWordWithTextFieldFocus: ImportMnemonicWord.State.ID?

public var language: BIP39Language
public var wordCount: BIP39WordCount {
guard let wordCount = BIP39WordCount(wordCount: words.count) else {
Expand All @@ -29,11 +27,6 @@ public struct ImportMnemonic: Sendable, FeatureReducer {
words.append(contentsOf: (wordCount ..< Int(newWordCount.rawValue)).map {
.init(
id: $0,
placeholder: ImportMnemonic.placeholder(
index: $0,
wordCount: newWordCount,
language: language
),
isReadonlyMode: mode.readonly != nil
)
})
Expand Down Expand Up @@ -229,11 +222,6 @@ public struct ImportMnemonic: Sendable, FeatureReducer {
word: $0.element,
completion: .auto(match: .exact)
),
placeholder: ImportMnemonic.placeholder(
index: $0.offset,
wordCount: mnemonic.wordCount,
language: mnemonic.language
),
isReadonlyMode: isReadonlyMode
)
}
Expand Down Expand Up @@ -278,10 +266,8 @@ public struct ImportMnemonic: Sendable, FeatureReducer {
public let savedIntoProfile: Bool
}

case focusNext(ImportMnemonicWord.State.ID)
case saveFactorSourceResult(
TaskResult<IntermediaryResult>
)
case focusOn(ImportMnemonicWord.State.ID)
case saveFactorSourceResult(TaskResult<IntermediaryResult>)
}

public enum ChildAction: Sendable, Equatable {
Expand Down Expand Up @@ -401,6 +387,9 @@ public struct ImportMnemonic: Sendable, FeatureReducer {
&state
)

case let .word(id, child: .delegate(.didSubmit)):
return focusNext(&state, after: id)

default:
return .none
}
Expand All @@ -409,7 +398,7 @@ public struct ImportMnemonic: Sendable, FeatureReducer {
public func reduce(into state: inout State, viewAction: ViewAction) -> Effect<Action> {
switch viewAction {
case .appeared:
return focusNext(&state)
return focusNext(&state, after: nil)

case let .passphraseChanged(passphrase):
state.bip39Passphrase = passphrase
Expand Down Expand Up @@ -532,8 +521,7 @@ public struct ImportMnemonic: Sendable, FeatureReducer {

public func reduce(into state: inout State, internalAction: InternalAction) -> Effect<Action> {
switch internalAction {
case let .focusNext(id):
state.idOfWordWithTextFieldFocus = id
case let .focusOn(id):
state.words[id: id]?.focus()
return .none

Expand Down Expand Up @@ -613,7 +601,7 @@ extension ImportMnemonic {
_ state: inout State
) -> Effect<Action> {
state.words[id: id]?.value = .complete(text: input, word: word, completion: completion)
return focusNext(&state)
return .none
}

private func updateWord(
Expand Down Expand Up @@ -659,48 +647,15 @@ extension ImportMnemonic {
}
}

private func focusNext(_ state: inout State) -> Effect<Action> {
if let current = state.idOfWordWithTextFieldFocus {
private func focusNext(_ state: inout State, after current: Int?) -> Effect<Action> {
if let current {
state.words[id: current]?.resignFocus()
}
guard let nextID = state.words.first(where: { !$0.isComplete })?.id else {
return delayedEffect(delay: .milliseconds(75), for: .internal(.focusOn(current + 1)))
CyonAlexRDX marked this conversation as resolved.
Show resolved Hide resolved
} else if let firstIncomplete = state.words.first(where: { !$0.isComplete })?.id {
return delayedEffect(delay: .milliseconds(75), for: .internal(.focusOn(firstIncomplete)))
} else {
return .none
}

return .run { send in
try? await clock.sleep(for: .milliseconds(75))
await send(.internal(.focusNext(nextID)))
}
}
}

extension ImportMnemonic {
static func placeholder(
index: Int,
wordCount: BIP39WordCount,
language: BIP39Language
) -> String {
precondition(index <= 23, "Invalid BIP39 word index, got index: \(index), exected less than 24.")
let word: BIP39Word = {
let wordList = language.wordlist() // BIP39.wordList(for: language)
switch language {
case .english:
let bip39Alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", /* X is missing */ "y", "z"]
return wordList
// we use `last` simply because we did not like the words "abandon baby"
// which we get by using `first`, too sad a combination.
.last(
where: { $0.word.hasPrefix(bip39Alphabet[index]) }
)!

default:
let scale = UInt16(89) // 2048 / 23
let indexScaled = U11(inner: scale * UInt16(index))
return wordList.first(where: { $0.index == indexScaled })!
}

}()
return word.word
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ extension ImportMnemonicWord.State {
.init(
isReadonlyMode: isReadonlyMode,
index: id,
placeholder: placeholder,
displayText: value.text,
autocompletionCandidates: autocompletionCandidates,
focusedField: focusedField,
Expand Down Expand Up @@ -37,7 +36,6 @@ extension ImportMnemonicWord {
public struct ViewState: Equatable {
let isReadonlyMode: Bool
let index: Int
let placeholder: String
let displayText: String
let autocompletionCandidates: ImportMnemonicWord.State.AutocompletionCandidates?
let focusedField: State.Field?
Expand Down Expand Up @@ -71,24 +69,23 @@ extension ImportMnemonicWord {

public var body: some SwiftUI.View {
WithViewStore(store, observe: \.viewState, send: { .view($0) }) { viewStore in
VStack(spacing: .small3) {
VStack(spacing: Constants.appTextFieldSpacing) {
AppTextField(
primaryHeading: .init(text: L10n.ImportMnemonic.wordHeading(viewStore.index + 1), isProminent: true),
placeholder: viewStore.placeholder,
placeholder: "",
text: .init(
get: { viewStore.displayText },
set: { viewStore.send(.wordChanged(input: $0.lowercased().trimmingWhitespacesAndNewlines())) }
),
hint: viewStore.hint,
// FIXME: Bring back autofocus
// focus: .on(
// .textField,
// binding: viewStore.binding(
// get: \.focusedField,
// send: { .textFieldFocused($0) }
// ),
// to: $focusedField
// ),
focus: .on(
State.Field.textField,
binding: viewStore.binding(
get: \.focusedField,
send: ViewAction.focusChanged
),
to: $focusedField
),
showClearButton: viewStore.showClearButton,
innerAccessory: {
if viewStore.displayValidAccessory {
Expand Down Expand Up @@ -126,6 +123,10 @@ extension ImportMnemonicWord {
}
}
}
.submitLabel(.next)
.onSubmit {
viewStore.send(.onSubmit)
}

if viewStore.hint == nil {
Hint(viewState: .iconError(L10n.Common.invalid)) // Dummy spacer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ public struct ImportMnemonicWord: Sendable, FeatureReducer {
public typealias ID = Int
public let id: ID
public var value: WordValue
public let placeholder: String
public let isReadonlyMode: Bool

public var autocompletionCandidates: AutocompletionCandidates? = nil
Expand All @@ -78,12 +77,10 @@ public struct ImportMnemonicWord: Sendable, FeatureReducer {
public init(
id: ID,
value: WordValue = .incomplete(text: "", hasFailedValidation: false),
placeholder: String,
isReadonlyMode: Bool
) {
self.id = id
self.value = value
self.placeholder = placeholder
self.isReadonlyMode = isReadonlyMode
}

Expand All @@ -110,13 +107,15 @@ public struct ImportMnemonicWord: Sendable, FeatureReducer {
public enum ViewAction: Sendable, Hashable {
case wordChanged(input: String)
case userSelectedCandidate(BIP39Word)
case textFieldFocused(State.Field?)
case focusChanged(State.Field?)
case onSubmit
}

public enum DelegateAction: Sendable, Hashable {
case lookupWord(input: String)
case lostFocus(displayText: String)
case userSelectedCandidate(BIP39Word, fromPartial: String)
case didSubmit
}

public init() {}
Expand Down Expand Up @@ -158,9 +157,12 @@ public struct ImportMnemonicWord: Sendable, FeatureReducer {
fromPartial: state.value.text
)))

case let .textFieldFocused(field):
case let .focusChanged(field):
state.focusedField = field
return field == nil ? .send(.delegate(.lostFocus(displayText: state.value.text))) : .none
return .none

case .onSubmit:
return .send(.delegate(.didSubmit))
}
}
}
Loading