diff --git a/secant.xcodeproj/project.pbxproj b/secant.xcodeproj/project.pbxproj index 3a3d27a0..bfd9ed71 100644 --- a/secant.xcodeproj/project.pbxproj +++ b/secant.xcodeproj/project.pbxproj @@ -82,12 +82,13 @@ 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 */; }; - 9E0FD7A027C910C000357DF0 /* ImportWalletStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E0FD79F27C910C000357DF0 /* ImportWalletStore.swift */; }; - 9E0FD7A327C910D500357DF0 /* ImportWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E0FD7A227C910D500357DF0 /* ImportWalletView.swift */; }; - 9E0FD7A527C9163900357DF0 /* ImportSeedEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E0FD7A427C9163900357DF0 /* ImportSeedEditor.swift */; }; - 9E0FD7AF27CE0E2900357DF0 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9E0FD7AE27CE0E2900357DF0 /* Localizable.strings */; }; + 9E2DF99C27CF704D00649636 /* ImportWalletStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99827CF704D00649636 /* ImportWalletStore.swift */; }; + 9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */; }; + 9E2DF99E27CF704D00649636 /* ImportWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99B27CF704D00649636 /* ImportWalletView.swift */; }; + 9E37A2B827C8F59F00AE57B3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9E37A2B727C8F59F00AE57B3 /* Localizable.strings */; }; 9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */; }; 9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */; }; + 9EBEF87A27CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.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 */; }; @@ -215,12 +216,13 @@ 66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingProgressIndicator.swift; sourceTree = ""; }; 66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtonStyle.swift; sourceTree = ""; }; 66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardButtonStyle.swift; sourceTree = ""; }; - 9E0FD79F27C910C000357DF0 /* ImportWalletStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportWalletStore.swift; sourceTree = ""; }; - 9E0FD7A227C910D500357DF0 /* ImportWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportWalletView.swift; sourceTree = ""; }; - 9E0FD7A427C9163900357DF0 /* ImportSeedEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportSeedEditor.swift; sourceTree = ""; }; - 9E0FD7AE27CE0E2900357DF0 /* Localizable.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; + 9E2DF99827CF704D00649636 /* ImportWalletStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletStore.swift; sourceTree = ""; }; + 9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportSeedEditor.swift; sourceTree = ""; }; + 9E2DF99B27CF704D00649636 /* ImportWalletView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletView.swift; sourceTree = ""; }; + 9E37A2B727C8F59F00AE57B3 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; 9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeumorphicDesignModifier.swift; sourceTree = ""; }; 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecantButtonStyles.swift; sourceTree = ""; }; + 9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseTestPreambleView.swift; sourceTree = ""; }; F9322DBF273B555C00C105B5 /* NavigationLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationLinks.swift; sourceTree = ""; }; F93673D52742CB840099C6AF /* Previews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Previews.swift; sourceTree = ""; }; F93874ED273C4DE200F0E875 /* HomeStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeStore.swift; sourceTree = ""; }; @@ -332,6 +334,7 @@ 0D3D04052728B2D70032ABC1 /* BackupFlow */ = { isa = PBXGroup; children = ( + 9EBEF87827CE365D00B4F343 /* Preamble */, 0D6D628A276A528D002FB4CC /* DropDelegate.swift */, 0D3D04062728B2EC0032ABC1 /* Views */, 0D3D04092728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift */, @@ -390,7 +393,7 @@ 0D4E7A0A26B364170058B01E /* ContentView.swift */, 0D4E7A0C26B364180058B01E /* Assets.xcassets */, 660558E8270C7A54009D6954 /* Colors.xcassets */, - 9E0FD7AE27CE0E2900357DF0 /* Localizable.strings */, + 9E37A2B727C8F59F00AE57B3 /* Localizable.strings */, 0D4E7A1126B364180058B01E /* Info.plist */, 0D4E7A0E26B364180058B01E /* Preview Content */, ); @@ -596,7 +599,7 @@ 0D3D04052728B2D70032ABC1 /* BackupFlow */, 6654C73C2715A3FA00901167 /* Onboarding */, F9971A6727680E1000A2DB75 /* WalletInfo */, - 9E0FD79E27C910AE00357DF0 /* ImportWallet */, + 9E2DF99727CF704D00649636 /* ImportWallet */, ); path = Features; sourceTree = ""; @@ -652,24 +655,32 @@ path = CircularFrame; sourceTree = ""; }; - 9E0FD79E27C910AE00357DF0 /* ImportWallet */ = { + 9E2DF99727CF704D00649636 /* ImportWallet */ = { isa = PBXGroup; children = ( - 9E0FD79F27C910C000357DF0 /* ImportWalletStore.swift */, - 9E0FD7A127C910C300357DF0 /* Views */, + 9E2DF99827CF704D00649636 /* ImportWalletStore.swift */, + 9E2DF99927CF704D00649636 /* Views */, ); path = ImportWallet; sourceTree = ""; }; - 9E0FD7A127C910C300357DF0 /* Views */ = { + 9E2DF99927CF704D00649636 /* Views */ = { isa = PBXGroup; children = ( - 9E0FD7A227C910D500357DF0 /* ImportWalletView.swift */, - 9E0FD7A427C9163900357DF0 /* ImportSeedEditor.swift */, + 9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */, + 9E2DF99B27CF704D00649636 /* ImportWalletView.swift */, ); path = Views; sourceTree = ""; }; + 9EBEF87827CE365D00B4F343 /* Preamble */ = { + isa = PBXGroup; + children = ( + 9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */, + ); + path = Preamble; + sourceTree = ""; + }; F93874EC273C4DE200F0E875 /* Home */ = { isa = PBXGroup; children = ( @@ -954,7 +965,7 @@ 0D4E7A0D26B364180058B01E /* Assets.xcassets in Resources */, 0DACFA9727209FA70039EEA5 /* Roboto-Black.ttf in Resources */, 0DACFA9C27209FA70039EEA5 /* Roboto-ThinItalic.ttf in Resources */, - 9E0FD7AF27CE0E2900357DF0 /* Localizable.strings in Resources */, + 9E37A2B827C8F59F00AE57B3 /* Localizable.strings in Resources */, 0DACFA9627209FA70039EEA5 /* Roboto-Thin.ttf in Resources */, 0D2ACE8026C2C67100D62E3C /* Zboto.otf in Resources */, ); @@ -1028,7 +1039,6 @@ 2EB660E02747EAB900A06A07 /* OnboardingScreen.swift in Sources */, 660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */, 0D35CC46277A36E00074316A /* ScrollableWhenScaled.swift in Sources */, - 9E0FD7A027C910C000357DF0 /* ImportWalletStore.swift in Sources */, F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */, 669FDAE9272C23B3007B9422 /* CircularFrame.swift in Sources */, F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */, @@ -1039,13 +1049,15 @@ 9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */, 0DACFA7F27208CE00039EEA5 /* Clamped.swift in Sources */, 0DFE93E3272CA1AA000FCCA5 /* RecoveryPhraseValidation.swift in Sources */, + 9E2DF99E27CF704D00649636 /* ImportWalletView.swift in Sources */, 0D354A0B26D5A9D000315F45 /* MnemonicSeedPhraseHandling.swift in Sources */, - 9E0FD7A527C9163900357DF0 /* ImportSeedEditor.swift in Sources */, 0D535FE2271F9476009A9E3E /* EnumeratedChip.swift in Sources */, 6654C73E2715A41300901167 /* OnboardingStore.swift in Sources */, + 9EBEF87A27CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift in Sources */, 9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */, 0DDB6A5127737D4A0012A410 /* ValidationFailedView.swift in Sources */, 0D6D628B276A528E002FB4CC /* DropDelegate.swift in Sources */, + 9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */, F9971A5327680DD000A2DB75 /* Profile.swift in Sources */, F93874F0273C4DE200F0E875 /* HomeStore.swift in Sources */, 669FDAEB272C23C2007B9422 /* CircularFrameBadge.swift in Sources */, @@ -1093,7 +1105,7 @@ 0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */, F9C165C22740403600592F76 /* CreateView.swift in Sources */, F9C165B4274031F600592F76 /* Bindings.swift in Sources */, - 9E0FD7A327C910D500357DF0 /* ImportWalletView.swift in Sources */, + 9E2DF99C27CF704D00649636 /* ImportWalletStore.swift in Sources */, F9971A6627680DFE00A2DB75 /* SettingsView.swift in Sources */, F96B41EB273B50520021B49A /* Strings.swift in Sources */, 0D354A0A26D5A9D000315F45 /* KeyStoring.swift in Sources */, diff --git a/secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/Contents.json b/secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/Contents.json new file mode 100644 index 00000000..75482b0f --- /dev/null +++ b/secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "calloutBackupFlow1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "calloutBackupFlow1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1.png b/secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1.png new file mode 100644 index 00000000..7f5b21a2 Binary files /dev/null and b/secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1.png differ diff --git a/secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1@2x.png b/secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1@2x.png new file mode 100644 index 00000000..fab2126d Binary files /dev/null and b/secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1@2x.png differ diff --git a/secant/Features/App/App.swift b/secant/Features/App/App.swift index 1546fcb5..6801361a 100644 --- a/secant/Features/App/App.swift +++ b/secant/Features/App/App.swift @@ -37,10 +37,11 @@ extension AppReducer { routeReducer, homeReducer, onboardingReducer, - phraseValidationReducer.debug(), - phraseDisplayReducer.debug() + phraseValidationReducer, + phraseDisplayReducer ] ) + .debug() private static let routeReducer = AppReducer { state, action, _ in switch action { diff --git a/secant/Features/App/Views/AppView.swift b/secant/Features/App/Views/AppView.swift index f0490723..0381b866 100644 --- a/secant/Features/App/Views/AppView.swift +++ b/secant/Features/App/Views/AppView.swift @@ -38,25 +38,12 @@ struct AppView: View { case .phraseValidation: NavigationView { - RecoveryPhraseBackupValidationView( + RecoveryPhraseTestPreambleView( store: store.scope( state: \.phraseValidationState, action: AppAction.phraseValidation ) ) - .toolbar( - content: { - ToolbarItem( - placement: .navigationBarLeading, - content: { - Button( - action: { viewStore.send(.updateRoute(.startup)) }, - label: { Text("Back") } - ) - } - ) - } - ) .navigationViewStyle(StackNavigationViewStyle()) } diff --git a/secant/Features/BackupFlow/Preamble/RecoveryPhraseTestPreambleView.swift b/secant/Features/BackupFlow/Preamble/RecoveryPhraseTestPreambleView.swift new file mode 100644 index 00000000..e3119529 --- /dev/null +++ b/secant/Features/BackupFlow/Preamble/RecoveryPhraseTestPreambleView.swift @@ -0,0 +1,124 @@ +// +// RecoveryPhraseTestPreambleView.swift +// secant-testnet +// +// Created by Lukáš Korba on 03/01/22. +// + +import SwiftUI +import ComposableArchitecture + +struct RecoveryPhraseTestPreambleView: View { + var store: RecoveryPhraseValidationStore + + var body: some View { + WithViewStore(store) { viewStore in + GeometryReader { proxy in + VStack { + VStack(alignment: .center, spacing: 20) { + Text("recoveryPhraseTestPreamble.title") + .titleText() + .multilineTextAlignment(.center) + + Text("recoveryPhraseTestPreamble.paragraph1") + .paragraphText() + .multilineTextAlignment(.center) + .padding(.horizontal, 44) + .opacity(0.53) + } + .padding(.bottom, 20) + + CircularFrame() + .backgroundImage( + Asset.Assets.Backgrounds.calloutBackupFlow1.image + ) + .frame( + width: circularFrameUniformSize(width: proxy.size.width, height: proxy.size.height), + height: circularFrameUniformSize(width: proxy.size.width, height: proxy.size.height) + ) + .badgeIcon(.person) + + Spacer() + + VStack(alignment: .center, spacing: 40) { + VStack(alignment: .center, spacing: 20) { + Text("recoveryPhraseTestPreamble.paragraph2") + .paragraphText() + .multilineTextAlignment(.center) + .opacity(0.53) + + Text("recoveryPhraseTestPreamble.paragraph3") + .paragraphText() + .multilineTextAlignment(.center) + .padding(.horizontal, 10) + .opacity(0.53) + } + + Button( + action: { viewStore.send(.updateRoute(.validation)) }, + label: { Text("recoveryPhraseTestPreamble.button.goNext") } + ) + .activeButtonStyle + .frame( + minWidth: 0, + maxWidth: .infinity, + minHeight: 64, + maxHeight: .infinity, + alignment: .center + ) + .fixedSize(horizontal: false, vertical: true) + } + .padding() + + Spacer() + } + .frame(width: proxy.size.width) + .scrollableWhenScaledUp() + .navigationLinkEmpty( + isActive: viewStore.bindingForValidation, + destination: { + RecoveryPhraseBackupValidationView(store: store) + } + ) + } + .padding() + .navigationBarHidden(true) + .applyScreenBackground() + } + } +} + +/// Following computations are necessary to handle properly sizing and positioning of elements +/// on different devices (apects). iPhone SE and iPhone 8 are similar aspect family devices +/// while iPhone X, 11, etc are different family devices, capable to use more of the space. +extension RecoveryPhraseTestPreambleView { + func circularFrameUniformSize(width: CGFloat, height: CGFloat) -> CGFloat { + var deviceMultiplier = 1.0 + + if width > 0.0 { + let aspect = height / width + deviceMultiplier = 1.0 + (((aspect / 1.51) - 1.0) * 2.8) + } + + return width * 0.4 * deviceMultiplier + } +} + +struct RecoveryPhraseTestPreambleView_Previews: PreviewProvider { + static var previews: some View { + Group { + NavigationView { + RecoveryPhraseTestPreambleView(store: .demo) + } + + RecoveryPhraseTestPreambleView(store: .demo) + .preferredColorScheme(.dark) + + RecoveryPhraseTestPreambleView(store: .demo) + .previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)")) + + RecoveryPhraseTestPreambleView(store: .demo) + .environment(\.sizeCategory, .accessibilityLarge) + } + } +} diff --git a/secant/Features/BackupFlow/RecoveryPhraseValidation.swift b/secant/Features/BackupFlow/RecoveryPhraseValidation.swift index 3368e4bc..54771579 100644 --- a/secant/Features/BackupFlow/RecoveryPhraseValidation.swift +++ b/secant/Features/BackupFlow/RecoveryPhraseValidation.swift @@ -19,8 +19,11 @@ struct ValidationWord: Equatable { var word: String } +// MARK: - State + struct RecoveryPhraseValidationState: Equatable { enum Route: Equatable, CaseIterable { + case validation case success case failure } @@ -44,17 +47,6 @@ struct RecoveryPhraseValidationState: Equatable { } } -extension RecoveryPhraseValidationViewStore { - func bindingForRoute(_ route: RecoveryPhraseValidationState.Route) -> Binding { - self.binding( - get: { $0.route == route }, - send: { isActive in - return .updateRoute(isActive ? route : nil) - } - ) - } -} - extension RecoveryPhraseValidationState { /// creates an initial `RecoveryPhraseValidationState` with no completions and random missing indices. /// - Note: Use this function to create a random validation puzzle for a given phrase. @@ -127,6 +119,8 @@ extension RecoveryPhrase.Group { } } +// MARK: - Action + enum RecoveryPhraseValidationAction: Equatable { case updateRoute(RecoveryPhraseValidationState.Route?) case reset @@ -138,6 +132,8 @@ enum RecoveryPhraseValidationAction: Equatable { case displayBackedUpPhrase } +// MARK: - Reducer + typealias RecoveryPhraseValidationReducer = Reducer extension RecoveryPhraseValidationReducer { @@ -145,6 +141,8 @@ extension RecoveryPhraseValidationReducer { switch action { case .reset: state = RecoveryPhraseValidationState.random(phrase: state.phrase) + state.route = .validation + // FIXME: Resetting causes route to be nil = preamble screen, hence setting the .validation. The transition back is not animated though (issue 186) case let .move(wordChip, group): guard @@ -160,7 +158,7 @@ extension RecoveryPhraseValidationReducer { let effect = Effect(value: value) .delay(for: 1, scheduler: environment.mainQueue) .eraseToEffect() - + if value == .succeed { return effect } else { @@ -182,6 +180,10 @@ extension RecoveryPhraseValidationReducer { environment.feedbackGenerator.generateFeedback() case .updateRoute(let route): + guard let route = route else { + state = RecoveryPhraseValidationState.random(phrase: state.phrase) + return .none + } state.route = route case .proceedToHome: @@ -193,3 +195,45 @@ extension RecoveryPhraseValidationReducer { return .none } } + +// MARK: - ViewStore + +extension RecoveryPhraseValidationViewStore { + func bindingForRoute(_ route: RecoveryPhraseValidationState.Route) -> Binding { + self.binding( + get: { $0.route == route }, + send: { isActive in + return .updateRoute(isActive ? route : nil) + } + ) + } +} + +extension RecoveryPhraseValidationViewStore { + var bindingForValidation: Binding { + self.binding( + get: { $0.route != nil }, + send: { isActive in + return .updateRoute(isActive ? .validation : nil) + } + ) + } + + var bindingForSuccess: Binding { + self.binding( + get: { $0.route == .success }, + send: { isActive in + return .updateRoute(isActive ? .success : .validation) + } + ) + } + + var bindingForFailure: Binding { + self.binding( + get: { $0.route == .failure }, + send: { isActive in + return .updateRoute(isActive ? .failure : .validation) + } + ) + } +} diff --git a/secant/Features/BackupFlow/Views/RecoveryPhraseBackupValidationView.swift b/secant/Features/BackupFlow/Views/RecoveryPhraseBackupValidationView.swift index b2617c30..59d7e1d6 100644 --- a/secant/Features/BackupFlow/Views/RecoveryPhraseBackupValidationView.swift +++ b/secant/Features/BackupFlow/Views/RecoveryPhraseBackupValidationView.swift @@ -11,28 +11,31 @@ import ComposableArchitecture struct RecoveryPhraseBackupValidationView: View { let store: RecoveryPhraseValidationStore + var viewStore: RecoveryPhraseValidationViewStore { + ViewStore(store) + } + var body: some View { - WithViewStore(self.store) { viewStore in - VStack(alignment: .center) { - header(for: viewStore) - .padding(.horizontal) - .padding(.bottom, 10) - - ZStack { - Asset.Colors.BackgroundColors.phraseGridDarkGray.color - .edgesIgnoringSafeArea(.bottom) - - VStack(alignment: .center, spacing: 35) { - let state = viewStore.state - let groups = state.phrase.toGroups() - - ForEach(Array(zip(groups.indices, groups)), id: \.0) { index, group in - WordChipGrid( - state: state, - groupIndex: index, - wordGroup: group, - misingIndex: index - ) + VStack(alignment: .center) { + header(for: viewStore) + .padding(.horizontal) + .padding(.bottom, 10) + + ZStack { + Asset.Colors.BackgroundColors.phraseGridDarkGray.color + .edgesIgnoringSafeArea(.bottom) + + VStack(alignment: .center, spacing: 35) { + let state = viewStore.state + let groups = state.phrase.toGroups() + + ForEach(Array(zip(groups.indices, groups)), id: \.0) { index, group in + WordChipGrid( + state: state, + groupIndex: index, + wordGroup: group, + misingIndex: index + ) .frame(alignment: .center) .background(Asset.Colors.BackgroundColors.phraseGridDarkGray.color) .whenIsDroppable( @@ -41,28 +44,27 @@ struct RecoveryPhraseBackupValidationView: View { viewStore.send(.move(wordChip: chipKind, intoGroup: index)) } ) - } - - Spacer() } - .padding() - .padding(.top, 0) - .navigationLinkEmpty( - isActive: viewStore.bindingForRoute(.success), - destination: { ValidationSucceededView(store: store) } - ) - .navigationLinkEmpty( - isActive: viewStore.bindingForRoute(.failure), - destination: { ValidationFailedView(store: store) } - ) + + Spacer() } - .frame(alignment: .top) + .padding() + .padding(.top, 0) + .navigationLinkEmpty( + isActive: viewStore.bindingForSuccess, + destination: { ValidationSucceededView(store: store) } + ) + .navigationLinkEmpty( + isActive: viewStore.bindingForFailure, + destination: { ValidationFailedView(store: store) } + ) } - .applyScreenBackground() - .scrollableWhenScaledUp() - .navigationBarTitleDisplayMode(.inline) - .navigationTitle(Text("recoveryPhraseBackupValidation.title")) + .frame(alignment: .top) } + .applyScreenBackground() + .scrollableWhenScaledUp() + .navigationBarTitleDisplayMode(.inline) + .navigationTitle(Text("recoveryPhraseBackupValidation.title")) } @ViewBuilder func header(for viewStore: RecoveryPhraseValidationViewStore) -> some View { diff --git a/secant/Generated/XCAssets+Generated.swift b/secant/Generated/XCAssets+Generated.swift index 549edc3f..e65d9fbb 100644 --- a/secant/Generated/XCAssets+Generated.swift +++ b/secant/Generated/XCAssets+Generated.swift @@ -29,6 +29,7 @@ internal enum Asset { internal static let callout2 = ImageAsset(name: "callout2") internal static let callout3 = ImageAsset(name: "callout3") internal static let callout4 = ImageAsset(name: "callout4") + internal static let calloutBackupFlow1 = ImageAsset(name: "calloutBackupFlow1") } internal enum Icons { internal static let list = ImageAsset(name: "list") diff --git a/secant/Localizable.strings b/secant/Localizable.strings index 4467270d..c9a51423 100644 --- a/secant/Localizable.strings +++ b/secant/Localizable.strings @@ -41,7 +41,14 @@ "validationFailed.description" = "Your placed words did not match your secret recovery phrase."; "validationFailed.incorrectBackupDescription" = "Remember, you can't recover your funds if you lose (or incorrectly save) these 24 words."; "validationFailed.button.tryAgain" = "I'm ready to try again"; - + +// MARK: - Recovery Phrase Test Preamble +"recoveryPhraseTestPreamble.title" = "First things first"; +"recoveryPhraseTestPreamble.paragraph1" = "It is important to understand that you are in charge here. Great, right? YOU get to be the bank!"; +"recoveryPhraseTestPreamble.paragraph2" = "But it also means that YOU are the customer, and you need to be self-reliant."; +"recoveryPhraseTestPreamble.paragraph3" = "So how do you recover funds that you've hidden on a completely decentralized and private block-chain?"; +"recoveryPhraseTestPreamble.button.goNext" = "By understanding and preparing"; + // MARK: - Import Wallet Screen "importWallet.title" = "Wallet Import"; "importWallet.description" = "You can import your backed up wallet by entering your backup recovery phrase (aka seed phrase) now.";