Requested changes done
The failed color (red) updated. The haptic feedback (.error) now occurs when the chips are not at the right indices.
This commit is contained in:
parent
8c12556236
commit
c8e54550fc
|
@ -82,6 +82,7 @@
|
|||
66A0807B271993C500118B79 /* OnboardingProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */; };
|
||||
66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */; };
|
||||
66DC733F271D88CC0053CBB6 /* StandardButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */; };
|
||||
9E4DC71027C6981300E657F4 /* FeedbackGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC70F27C6981300E657F4 /* FeedbackGenerator.swift */; };
|
||||
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9322DBF273B555C00C105B5 /* NavigationLinks.swift */; };
|
||||
F93673D62742CB840099C6AF /* Previews.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93673D52742CB840099C6AF /* Previews.swift */; };
|
||||
F93874F0273C4DE200F0E875 /* HomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874ED273C4DE200F0E875 /* HomeStore.swift */; };
|
||||
|
@ -209,6 +210,7 @@
|
|||
66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingProgressIndicator.swift; sourceTree = "<group>"; };
|
||||
66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtonStyle.swift; sourceTree = "<group>"; };
|
||||
66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardButtonStyle.swift; sourceTree = "<group>"; };
|
||||
9E4DC70F27C6981300E657F4 /* FeedbackGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackGenerator.swift; sourceTree = "<group>"; };
|
||||
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationLinks.swift; sourceTree = "<group>"; };
|
||||
F93673D52742CB840099C6AF /* Previews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Previews.swift; sourceTree = "<group>"; };
|
||||
F93874ED273C4DE200F0E875 /* HomeStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeStore.swift; sourceTree = "<group>"; };
|
||||
|
@ -325,6 +327,7 @@
|
|||
0D3D04092728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift */,
|
||||
0DFE93E2272CA1AA000FCCA5 /* RecoveryPhraseValidation.swift */,
|
||||
0D7CE63327349B5D0020E050 /* View+WhenDraggable.swift */,
|
||||
9E4DC70F27C6981300E657F4 /* FeedbackGenerator.swift */,
|
||||
);
|
||||
path = BackupFlow;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1062,6 +1065,7 @@
|
|||
0DFE93E1272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift in Sources */,
|
||||
F9C165CB2741AB5D00592F76 /* SendView.swift in Sources */,
|
||||
0D0781C4278750E30083ACD7 /* WelcomeView.swift in Sources */,
|
||||
9E4DC71027C6981300E657F4 /* FeedbackGenerator.swift in Sources */,
|
||||
F9971A6527680DFE00A2DB75 /* Settings.swift in Sources */,
|
||||
0D0781C9278776D20083ACD7 /* ZcashSymbol.swift in Sources */,
|
||||
6654C7412715A47300901167 /* Onboarding.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// FeedbackGenerator.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 02/23/2022.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
protocol FeedbackGenerator {
|
||||
func generateFeedback()
|
||||
}
|
||||
|
||||
/// use in case of testing or when real haptic feedback is not appropriate
|
||||
class SilentFeedbackGenerator: FeedbackGenerator {
|
||||
func generateFeedback() { }
|
||||
}
|
||||
|
||||
/// haptic feedback for the failures (when we want to amplify importance of the failure)
|
||||
class ImpactFeedbackGenerator: FeedbackGenerator {
|
||||
let generator = UINotificationFeedbackGenerator()
|
||||
|
||||
func generateFeedback() {
|
||||
generator.notificationOccurred(.error)
|
||||
}
|
||||
}
|
|
@ -40,6 +40,7 @@ struct BackupPhraseEnvironment {
|
|||
let mainQueue: AnySchedulerOf<DispatchQueue>
|
||||
let newPhrase: () -> Effect<RecoveryPhrase, RecoveryPhraseError>
|
||||
let pasteboard: Pasteboard
|
||||
let feedbackGenerator: FeedbackGenerator
|
||||
}
|
||||
|
||||
extension BackupPhraseEnvironment {
|
||||
|
@ -51,13 +52,15 @@ extension BackupPhraseEnvironment {
|
|||
static let demo = Self(
|
||||
mainQueue: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
newPhrase: { Effect(value: .init(words: RecoveryPhrase.placeholder.words)) },
|
||||
pasteboard: .test
|
||||
pasteboard: .test,
|
||||
feedbackGenerator: SilentFeedbackGenerator()
|
||||
)
|
||||
|
||||
static let live = Self(
|
||||
mainQueue: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
newPhrase: { Effect(value: .init(words: RecoveryPhrase.placeholder.words)) },
|
||||
pasteboard: .live
|
||||
pasteboard: .live,
|
||||
feedbackGenerator: ImpactFeedbackGenerator()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -133,6 +133,7 @@ enum RecoveryPhraseValidationAction: Equatable {
|
|||
case move(wordChip: PhraseChip.Kind, intoGroup: Int)
|
||||
case succeed
|
||||
case fail
|
||||
case failureFeedback
|
||||
case proceedToHome
|
||||
case displayBackedUpPhrase
|
||||
}
|
||||
|
@ -147,7 +148,7 @@ extension RecoveryPhraseValidationReducer {
|
|||
|
||||
case let .move(wordChip, group):
|
||||
guard
|
||||
case let PhraseChip.Kind.unassigned(word) = wordChip,
|
||||
case let PhraseChip.Kind.unassigned(word, color) = wordChip,
|
||||
let missingChipIndex = state.missingWordChips.firstIndex(of: wordChip)
|
||||
else { return .none }
|
||||
|
||||
|
@ -156,9 +157,13 @@ extension RecoveryPhraseValidationReducer {
|
|||
|
||||
if state.isComplete {
|
||||
let value: RecoveryPhraseValidationAction = state.isValid ? .succeed : .fail
|
||||
return Effect(value: value)
|
||||
|
||||
return .concatenate(
|
||||
.init(value: .failureFeedback),
|
||||
Effect(value: value)
|
||||
.delay(for: 1, scheduler: environment.mainQueue)
|
||||
.eraseToEffect()
|
||||
)
|
||||
}
|
||||
return .none
|
||||
|
||||
|
@ -168,6 +173,9 @@ extension RecoveryPhraseValidationReducer {
|
|||
case .fail:
|
||||
state.route = .failure
|
||||
|
||||
case .failureFeedback:
|
||||
environment.feedbackGenerator.generateFeedback()
|
||||
|
||||
case .updateRoute(let route):
|
||||
state.route = route
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ extension PhraseChip {
|
|||
/// Makes a PhraseChip draggable when it is of kind .unassigned
|
||||
@ViewBuilder func makeDraggable() -> some View {
|
||||
switch self.kind {
|
||||
case .unassigned(let word):
|
||||
case let .unassigned(word, _):
|
||||
self.onDrag {
|
||||
NSItemProvider(object: word as NSString)
|
||||
}
|
||||
|
|
|
@ -116,7 +116,8 @@ extension RecoveryPhraseValidationState {
|
|||
func wordsChips(
|
||||
for groupIndex: Int,
|
||||
groupSize: Int,
|
||||
from wordGroup: RecoveryPhrase.Group
|
||||
from wordGroup: RecoveryPhrase.Group,
|
||||
state: RecoveryPhraseValidationState
|
||||
) -> [PhraseChip.Kind] {
|
||||
let validationWord = validationWords.first(where: { $0.groupIndex == groupIndex })
|
||||
|
||||
|
@ -126,7 +127,7 @@ extension RecoveryPhraseValidationState {
|
|||
}
|
||||
|
||||
if let completedWord = validationWord?.word {
|
||||
return .unassigned(word: completedWord)
|
||||
return .unassigned(word: completedWord, color: state.coloredChipColor)
|
||||
}
|
||||
|
||||
return .empty
|
||||
|
@ -248,7 +249,8 @@ private extension WordChipGrid {
|
|||
let chips = state.wordsChips(
|
||||
for: groupIndex,
|
||||
groupSize: RecoveryPhraseValidationState.wordGroupSize,
|
||||
from: wordGroup
|
||||
from: wordGroup,
|
||||
state: state
|
||||
)
|
||||
|
||||
self.init(chips: chips, coloredChipColor: state.coloredChipColor)
|
||||
|
|
|
@ -10,7 +10,7 @@ import SwiftUI
|
|||
struct PhraseChip: View {
|
||||
enum Kind: Hashable {
|
||||
case empty
|
||||
case unassigned(word: String)
|
||||
case unassigned(word: String, color: Color = Asset.Colors.Buttons.activeButton.color)
|
||||
case ordered(position: Int, word: String)
|
||||
}
|
||||
|
||||
|
@ -22,8 +22,8 @@ struct PhraseChip: View {
|
|||
EmptyChip()
|
||||
case let .ordered(position, word):
|
||||
EnumeratedChip(index: position, text: word)
|
||||
case .unassigned(let word):
|
||||
ColoredChip(word: word)
|
||||
case let .unassigned(word, color):
|
||||
ColoredChip(word: word, color: color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue