From af054b098c14daea4b18d3f70361b4ac25bcfa88 Mon Sep 17 00:00:00 2001 From: Lukas Korba Date: Fri, 13 May 2022 18:29:57 +0200 Subject: [PATCH] [#180] Project Structure and Source code consistency (#317) phrase 1: - project restructured - swifgen rewired - files renamed [180] Code inconsistency phase 2: - stores and views refactored phase 3: - models, dependencies, utils and UI components [180] Code inconsistency - tests fixed --- secant.xcodeproj/project.pbxproj | 614 +++++++++--------- secant/AppErrors/AppError.swift | 12 - .../DatabaseFiles.swift | 69 -- .../UserPreferencesStorage.swift | 0 .../WalletStorage.swift | 0 .../ZCashSDKEnvironment.swift | 0 .../App/{App.swift => AppStore.swift} | 119 ++-- secant/Features/App/{Views => }/AppView.swift | 58 +- secant/Features/Home/HomeStore.swift | 154 ++--- .../Features/Home/{Views => }/HomeView.swift | 20 +- .../ImportWallet/ImportWalletStore.swift | 30 +- .../{Views => }/ImportWalletView.swift | 14 +- .../Onboarding/Views/Onboarding.swift | 105 --- .../OnboardingFlowStore.swift} | 97 ++- .../OnboardingFlow/OnboardingFlowView.swift} | 28 +- .../Views}/OnboardingContentView.swift | 10 +- .../Views}/OnboardingFooterView.swift | 12 +- .../Views}/OnboardingHeaderView.swift | 8 +- secant/Features/Profile/ProfileStore.swift | 28 +- .../Profile/{Views => }/ProfileView.swift | 2 + .../RecoveryPhraseDisplayStore.swift | 116 +--- .../RecoveryPhraseDisplayView.swift | 0 .../RecoveryPhraseValidationFlowStore.swift} | 184 +++++- .../RecoveryPhraseValidationFlowView.swift} | 18 +- .../RecoveryPhraseBackupFailedView.swift} | 20 +- .../RecoveryPhraseBackupSucceededView.swift} | 33 +- .../Views/RecoveryPhraseBackupView.swift} | 138 +--- secant/Features/Request/RequestStore.swift | 38 +- .../Request/{Views => }/RequestView.swift | 2 + secant/Features/Sandbox/SandboxStore.swift | 55 +- .../Sandbox/{Views => }/SandboxView.swift | 40 +- secant/Features/Scan/ScanStore.swift | 38 +- .../Features/Scan/{Views => }/ScanView.swift | 2 + .../SendFlowStore.swift} | 164 ++--- .../SendFlowView.swift} | 23 +- .../Views/CreateTransactionView.swift | 29 +- .../Views/TransactionConfirmationView.swift | 6 +- .../Views/TransactionFailedView.swift | 4 +- .../Views/TransactionSentView.swift | 4 +- .../{Settings.swift => SettingsStore.swift} | 29 +- .../Settings/{Views => }/SettingsView.swift | 2 + .../TransactionHistoryFlowStore.swift} | 85 ++- .../TransactionHistoryFlowView.swift} | 14 +- .../Views/TransactionDetailView.swift | 2 + ...WalletInfo.swift => WalletInfoStore.swift} | 28 +- .../{Views => }/WalletInfoView.swift | 2 + .../{Welcome.swift => WelcomeStore.swift} | 26 +- secant/Features/Welcome/WelcomeView.swift | 52 +- secant/MockedDependencies/Services.swift | 19 - .../App => Models}/AppDelegate.swift | 0 secant/Models/Balance.swift | 108 --- .../BackupFlow => Models}/DropDelegate.swift | 2 +- .../InitializationState.swift | 0 secant/Models/RecoveryPhrase.swift | 43 ++ secant/Models/SendFlowTransaction.swift | 25 + secant/Models/StoredWallet.swift | 20 + .../TransactionState.swift | 1 + secant/Models/ValidationWord.swift | 15 + .../AppIcon.appiconset/100.png | Bin .../AppIcon.appiconset/1024.png | Bin .../AppIcon.appiconset/114.png | Bin .../AppIcon.appiconset/120.png | Bin .../AppIcon.appiconset/128.png | Bin .../AppIcon.appiconset/144.png | Bin .../AppIcon.appiconset/152.png | Bin .../Assets.xcassets/AppIcon.appiconset/16.png | Bin .../AppIcon.appiconset/167.png | Bin .../AppIcon.appiconset/172.png | Bin .../AppIcon.appiconset/180.png | Bin .../AppIcon.appiconset/196.png | Bin .../Assets.xcassets/AppIcon.appiconset/20.png | Bin .../AppIcon.appiconset/216.png | Bin .../AppIcon.appiconset/256.png | Bin .../Assets.xcassets/AppIcon.appiconset/29.png | Bin .../Assets.xcassets/AppIcon.appiconset/32.png | Bin .../Assets.xcassets/AppIcon.appiconset/40.png | Bin .../Assets.xcassets/AppIcon.appiconset/48.png | Bin .../Assets.xcassets/AppIcon.appiconset/50.png | Bin .../AppIcon.appiconset/512.png | Bin .../Assets.xcassets/AppIcon.appiconset/55.png | Bin .../Assets.xcassets/AppIcon.appiconset/57.png | Bin .../Assets.xcassets/AppIcon.appiconset/58.png | Bin .../Assets.xcassets/AppIcon.appiconset/60.png | Bin .../Assets.xcassets/AppIcon.appiconset/64.png | Bin .../Assets.xcassets/AppIcon.appiconset/72.png | Bin .../Assets.xcassets/AppIcon.appiconset/76.png | Bin .../Assets.xcassets/AppIcon.appiconset/80.png | Bin .../Assets.xcassets/AppIcon.appiconset/87.png | Bin .../Assets.xcassets/AppIcon.appiconset/88.png | Bin .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/Backgrounds/Contents.json | 0 .../callout0.imageset/Contents.json | 0 .../callout0.imageset/callout0-1.jpg | Bin .../callout0.imageset/callout0-2.jpg | Bin .../callout0.imageset/callout0.jpg | Bin .../callout1.imageset/Contents.json | 0 .../callout1.imageset/callout1-1.jpg | Bin .../callout1.imageset/callout1-2.jpg | Bin .../callout1.imageset/callout1.jpg | Bin .../callout2.imageset/Contents.json | 0 .../callout2.imageset/callout2-1.jpg | Bin .../callout2.imageset/callout2-2.jpg | Bin .../callout2.imageset/callout2.jpg | Bin .../callout3.imageset/Contents.json | 0 .../callout3.imageset/callout3-1.jpg | Bin .../callout3.imageset/callout3-2.jpg | Bin .../callout3.imageset/callout3.jpg | Bin .../callout4.imageset/Contents.json | 0 .../callout4.imageset/finalcallout-1.jpg | Bin .../callout4.imageset/finalcallout-2.jpg | Bin .../callout4.imageset/finalcallout.jpg | Bin .../Contents.json | 0 .../calloutBackupFailed.png | Bin .../calloutBackupFailed@2x.png | Bin .../calloutBackupFlow1.imageset/Contents.json | 0 .../calloutBackupFlow1.png | Bin .../calloutBackupFlow1@2x.png | Bin .../Contents.json | 0 .../calloutBackupSucceeded.png | Bin .../calloutBackupSucceeded@2x.png | Bin .../Assets.xcassets/Contents.json | 0 .../Assets.xcassets/Icons/Contents.json | 0 .../Icons/bank.imageset/Contents.json | 0 .../bank.imageset/iconbank_darktheme.png | Bin .../bank.imageset/iconbank_darktheme@2x.png | Bin .../bank.imageset/iconbank_darktheme@3x.png | Bin .../bank.imageset/iconbank_lighttheme.png | Bin .../bank.imageset/iconbank_lighttheme@2x.png | Bin .../bank.imageset/iconbank_lighttheme@3x.png | Bin .../Icons/list.imageset/Contents.json | 0 .../list.imageset/iconlist_darktheme.png | Bin .../list.imageset/iconlist_darktheme@2x.png | Bin .../list.imageset/iconlist_darktheme@3x.png | Bin .../list.imageset/iconlist_lighttheme.png | Bin .../list.imageset/iconlist_lighttheme@2x.png | Bin .../list.imageset/iconlist_lighttheme@3x.png | Bin .../Icons/profile.imageset/Contents.json | 0 .../profile.imageset/iconperson_darktheme.png | Bin .../iconperson_darktheme@2x.png | Bin .../iconperson_darktheme@3x.png | Bin .../iconperson_lighttheme.png | Bin .../iconperson_lighttheme@2x.png | Bin .../iconperson_lighttheme@3x.png | Bin .../Icons/qr-code.imageset/Contents.json | 0 .../Icons/qr-code.imageset/qr_darktheme.png | Bin .../qr-code.imageset/qr_darktheme@2x.png | Bin .../qr-code.imageset/qr_darktheme@3x.png | Bin .../Icons/qr-code.imageset/qr_lighttheme.png | Bin .../qr-code.imageset/qr_lighttheme@2x.png | Bin .../qr-code.imageset/qr_lighttheme@3x.png | Bin .../Icons/shield.imageset/Contents.json | 0 .../shield.imageset/iconshield_darktheme.png | Bin .../iconshield_darktheme@2x.png | Bin .../iconshield_darktheme@3x.png | Bin .../shield.imageset/iconshield_lighttheme.png | Bin .../iconshield_lighttheme@2x.png | Bin .../iconshield_lighttheme@3x.png | Bin .../Icons/swap.imageset/Contents.json | 0 .../swap.imageset/swapicon_darktheme.png | Bin .../swap.imageset/swapicon_darktheme@2x.png | Bin .../swap.imageset/swapicon_darktheme@3x.png | Bin .../swap.imageset/swapicon_lighttheme.png | Bin .../swap.imageset/swapicon_lighttheme@2x.png | Bin .../swap.imageset/swapicon_lighttheme@3x.png | Bin .../WelcomeScreenLogo.imageset/Contents.json | 0 .../WelcomeScreenLogo.imageset/DarkLogo.png | Bin .../DarkLogo@2x.png | Bin .../DarkLogo@3x.png | Bin .../WelcomeScreenLogo.imageset/LightLogo.png | Bin .../LightLogo@2x.png | Bin .../LightLogo@3x.png | Bin .../BackgroundColors/Contents.json | 0 .../numberedChip.colorset/Contents.json | 0 .../phraseGridDarkGray.colorset/Contents.json | 0 .../red.colorset/Contents.json | 0 .../Contents.json | 0 .../ActiveButton.colorset/Contents.json | 0 .../Contents.json | 0 .../Contents.json | 0 .../ButtonsTitleShadow.colorset/Contents.json | 0 .../Colors.xcassets/Buttons/Contents.json | 0 .../Contents.json | 0 .../Contents.json | 0 .../Contents.json | 0 .../Contents.json | 0 .../PrimaryButton.colorset/Contents.json | 0 .../Contents.json | 0 .../Contents.json | 0 .../SecondaryButton.colorset/Contents.json | 0 .../Contents.json | 0 .../Colors.xcassets/Contents.json | 0 .../Cursor/Bar.colorset/Contents.json | 0 .../Colors.xcassets/Cursor/Contents.json | 0 .../BadgeShadow.colorset/Contents.json | 0 .../Contents.json | 0 .../Contents.json | 0 .../Contents.json | 0 .../Contents.json | 0 .../Colors.xcassets/Onboarding/Contents.json | 0 .../Contents.json | 0 .../Contents.json | 0 .../NeumorphicDarkSide.colorset/Contents.json | 0 .../Contents.json | 0 .../badgeBackground.colorset/Contents.json | 0 .../ProgressIndicator/Contents.json | 0 .../GradientLeft.colorset/Contents.json | 0 .../GradientRight.colorset/Contents.json | 0 .../NegativeSpace.colorset/Contents.json | 0 .../ScreenBackground/Contents.json | 0 .../gradientEnd.colorset/Contents.json | 0 .../gradientStart.colorset/Contents.json | 0 .../greenGradientEnd.colorset/Contents.json | 0 .../greenGradientStart.colorset/Contents.json | 0 .../redGradientEnd.colorset/Contents.json | 0 .../redGradientStart.colorset/Contents.json | 0 .../Colors.xcassets/Shadow/Contents.json | 0 .../drawerShadow.colorset/Contents.json | 0 .../Contents.json | 0 .../numberedTextShadow.colorset/Contents.json | 0 .../ActiveButtonText.colorset/Contents.json | 0 .../Text/Body.colorset/Contents.json | 0 .../Text/Button.colorset/Contents.json | 0 .../Colors.xcassets/Text/Contents.json | 0 .../Text/Heading.colorset/Contents.json | 0 .../ImportSeedEditor.colorset/Contents.json | 0 .../Text/Medium.colorset/Contents.json | 0 .../Text/Regular.colorset/Contents.json | 0 .../Contents.json | 0 .../Text/TitleText.colorset/Contents.json | 0 .../Text/captionText.colorset/Contents.json | 0 .../captionTextShadow.colorset/Contents.json | 0 .../Contents.json | 0 .../Colors.xcassets/TextField/Contents.json | 0 .../Contents.json | 0 .../Contents.json | 0 .../TextField/Underline/Contents.json | 0 .../Underline/Gray.colorset/Contents.json | 0 .../Underline/Purple.colorset/Contents.json | 0 .../Colors.xcassets/ZcashBadge/Contents.json | 0 .../ZcashLogoFill.colorset/Contents.json | 0 .../innerCircle.colorset/Contents.json | 0 .../Contents.json | 0 .../Contents.json | 0 .../shadowColor.colorset/Contents.json | 0 .../thickRing.colorset/Contents.json | 0 .../thinRing.colorset/Contents.json | 0 .../{ => Resources}/Fonts/Roboto/LICENSE.txt | 0 .../Fonts/Roboto/Roboto-Black.ttf | Bin .../Fonts/Roboto/Roboto-BlackItalic.ttf | Bin .../Fonts/Roboto/Roboto-Bold.ttf | Bin .../Fonts/Roboto/Roboto-BoldItalic.ttf | Bin .../Fonts/Roboto/Roboto-Italic.ttf | Bin .../Fonts/Roboto/Roboto-Light.ttf | Bin .../Fonts/Roboto/Roboto-LightItalic.ttf | Bin .../Fonts/Roboto/Roboto-Medium.ttf | Bin .../Fonts/Roboto/Roboto-MediumItalic.ttf | Bin .../Fonts/Roboto/Roboto-Regular.ttf | Bin .../Fonts/Roboto/Roboto-Thin.ttf | Bin .../Fonts/Roboto/Roboto-ThinItalic.ttf | Bin .../Rubik/Rubik-Italic-VariableFont_wght.ttf | Bin .../Fonts/Rubik/Rubik-VariableFont_wght.ttf | Bin secant/{ => Resources}/Fonts/Zboto.otf | Bin .../Generated/Fonts+Generated.swift | 0 .../Generated/XCAssets+Generated.swift | 0 secant/{ => Resources}/Localizable.strings | 0 secant/Stubs/MockServices.swift | 138 ---- .../Backgrounds/ScreenBackground.swift | 0 .../Buttons/ActiveButton.swift | 0 .../Buttons/NavigationButtonStyle.swift | 0 .../Buttons/NeumorphicDesignModifier.swift | 0 .../Buttons/PrimaryButton.swift | 0 .../Buttons/SecondaryButton.swift | 0 .../Buttons/StandardButtonStyle.swift | 0 .../Chips/ColoredChip.swift | 0 .../Chips/EmptyChip.swift | 0 .../Chips/EnumeratedChip.swift | 0 .../Chips/PhraseChip.swift | 0 .../Chips}/WordChipGrid.swift | 0 .../CircularFrame/CircularFrame.swift | 0 .../CircularFrameBackground.swift | 0 .../CircularFrame/CircularFrameBadge.swift | 0 .../DesignGuide.swift | 0 .../Drawer/Drawer.swift | 0 .../Extensions/ConditionalModifier.swift | 0 .../Extensions/View+InnerShadow.swift | 0 .../FontStyles/SecantButtonStyles.swift | 0 .../FontStyles/SecantTextStyles.swift | 0 .../ImportSeedEditor}/ImportSeedEditor.swift | 0 .../OnboardingProgressIndicator.swift | 0 .../Shapes/ZcashSymbol.swift | 0 .../CurrencySelectionStore.swift | 0 .../CurrencySelectionView.swift} | 4 +- .../Components/TextFieldFooter.swift | 0 .../TextFieldTitleAccessoryButtonStyle.swift | 0 .../TextFields}/SingleLineTextField.swift | 12 +- .../TCATextField/TCATextField.swift} | 4 +- .../TCATextField/TCATextFieldStore.swift} | 35 +- .../TransactionAddressTextField.swift | 8 +- .../TransactionAddressTextFieldStore.swift | 79 +++ .../TransactionAmountTextField.swift | 10 +- .../TransactionAmountTextFieldStore.swift} | 54 +- .../UI Components/ZcashBadge/ZcashBadge.swift | 60 ++ .../TransactionAddressInputStore.swift | 79 --- secant/Utils/Array+Chunked.swift | 16 + secant/{Util => Utils}/Bindings.swift | 0 secant/{Util => Utils}/Clamped.swift | 0 secant/Utils/Date+Readable.swift | 19 + secant/{Util => Utils}/DebugFrame.swift | 0 secant/{Util => Utils}/DebugMenu.swift | 0 secant/{Util => Utils}/Double+Zcash.swift | 0 secant/{Util => Utils}/Int64+Zcash.swift | 0 secant/{Util => Utils}/NavigationLinks.swift | 0 secant/{Util => Utils}/Previews.swift | 0 secant/Utils/SDKSynchronizer+SyncStatus.swift | 45 ++ .../ScrollableWhenScaled.swift | 0 secant/{Util => Utils}/Strings.swift | 0 .../UInt+SuperscriptText.swift | 0 .../View+WhenDraggable.swift | 0 secant/{Util => Utils}/WithStateBinding.swift | 0 secant/Wrappers/WrappedDatabaseFiles.swift | 78 +++ .../Wrappers/WrappedFeedbackGenerator.swift | 23 + .../WrappedMnemonic.swift} | 10 +- secant/Wrappers/WrappedPasteboard.swift | 31 + .../WrappedWalletStorage.swift} | 26 +- .../AppReducerTests/AppReducerTests.swift | 16 +- .../OnboardingStoreTests.swift | 20 +- .../RecoveryPhraseValidationTests.swift | 42 +- secantTests/SendTests/SendTests.swift | 118 ++-- .../TransactionAddressInputTests.swift | 14 +- .../TransactionAmountInputTests.swift | 60 +- .../TransactionHistoryTests.swift | 12 +- .../UtilTests/DatabaseFilesTests.swift | 14 +- swiftgen.yml | 4 +- 333 files changed, 1828 insertions(+), 1970 deletions(-) delete mode 100644 secant/AppErrors/AppError.swift rename secant/{Util => Dependencies}/DatabaseFiles.swift (58%) rename secant/{Util => Dependencies}/UserPreferencesStorage.swift (100%) rename secant/{Util => Dependencies}/WalletStorage.swift (100%) rename secant/{Util => Dependencies}/ZCashSDKEnvironment.swift (100%) rename secant/Features/App/{App.swift => AppStore.swift} (89%) rename secant/Features/App/{Views => }/AppView.swift (71%) rename secant/Features/Home/{Views => }/HomeView.swift (86%) rename secant/Features/ImportWallet/{Views => }/ImportWalletView.swift (90%) delete mode 100644 secant/Features/Onboarding/Views/Onboarding.swift rename secant/Features/{Onboarding/OnboardingStore.swift => OnboardingFlow/OnboardingFlowStore.swift} (56%) rename secant/{Screens/Onboarding/OnboardingScreen.swift => Features/OnboardingFlow/OnboardingFlowView.swift} (81%) rename secant/{Screens/Onboarding => Features/OnboardingFlow/Views}/OnboardingContentView.swift (95%) rename secant/{Screens/Onboarding => Features/OnboardingFlow/Views}/OnboardingFooterView.swift (91%) rename secant/{Screens/Onboarding => Features/OnboardingFlow/Views}/OnboardingHeaderView.swift (93%) rename secant/Features/Profile/{Views => }/ProfileView.swift (98%) rename secant/Features/{BackupFlow => RecoveryPhraseDisplay}/RecoveryPhraseDisplayStore.swift (52%) rename secant/Features/{BackupFlow/Views => RecoveryPhraseDisplay}/RecoveryPhraseDisplayView.swift (100%) rename secant/Features/{BackupFlow/RecoveryPhraseValidation.swift => RecoveryPhraseValidationFlow/RecoveryPhraseValidationFlowStore.swift} (55%) rename secant/Features/{BackupFlow/Preamble/RecoveryPhraseTestPreambleView.swift => RecoveryPhraseValidationFlow/RecoveryPhraseValidationFlowView.swift} (89%) rename secant/Features/{BackupFlow/Views/ValidationFailedView.swift => RecoveryPhraseValidationFlow/Views/RecoveryPhraseBackupFailedView.swift} (88%) rename secant/Features/{BackupFlow/Views/ValidationSucceededView.swift => RecoveryPhraseValidationFlow/Views/RecoveryPhraseBackupSucceededView.swift} (81%) rename secant/Features/{BackupFlow/Views/RecoveryPhraseBackupValidationView.swift => RecoveryPhraseValidationFlow/Views/RecoveryPhraseBackupView.swift} (58%) rename secant/Features/Request/{Views => }/RequestView.swift (94%) rename secant/Features/Sandbox/{Views => }/SandboxView.swift (80%) rename secant/Features/Scan/{Views => }/ScanView.swift (94%) rename secant/Features/{Send/SendStore.swift => SendFlow/SendFlowStore.swift} (60%) rename secant/Features/{Send/Views/SendView.swift => SendFlow/SendFlowView.swift} (75%) rename secant/Features/{Send => SendFlow}/Views/CreateTransactionView.swift (78%) rename secant/Features/{Send => SendFlow}/Views/TransactionConfirmationView.swift (92%) rename secant/Features/{Send => SendFlow}/Views/TransactionFailedView.swift (92%) rename secant/Features/{Send => SendFlow}/Views/TransactionSentView.swift (93%) rename secant/Features/Settings/{Settings.swift => SettingsStore.swift} (73%) rename secant/Features/Settings/{Views => }/SettingsView.swift (92%) rename secant/Features/{TransactionHistory/TransactionHistoryStore.swift => TransactionHistoryFlow/TransactionHistoryFlowStore.swift} (58%) rename secant/Features/{TransactionHistory/Views/TransactionHistoryView.swift => TransactionHistoryFlow/TransactionHistoryFlowView.swift} (89%) rename secant/Features/{TransactionHistory => TransactionHistoryFlow}/Views/TransactionDetailView.swift (95%) rename secant/Features/WalletInfo/{WalletInfo.swift => WalletInfoStore.swift} (79%) rename secant/Features/WalletInfo/{Views => }/WalletInfoView.swift (92%) rename secant/Features/Welcome/{Welcome.swift => WelcomeStore.swift} (70%) delete mode 100644 secant/MockedDependencies/Services.swift rename secant/{Features/App => Models}/AppDelegate.swift (100%) delete mode 100644 secant/Models/Balance.swift rename secant/{Features/BackupFlow => Models}/DropDelegate.swift (96%) rename secant/{Util => Models}/InitializationState.swift (100%) create mode 100644 secant/Models/RecoveryPhrase.swift create mode 100644 secant/Models/SendFlowTransaction.swift create mode 100644 secant/Models/StoredWallet.swift rename secant/{Features/TransactionHistory => Models}/TransactionState.swift (96%) create mode 100644 secant/Models/ValidationWord.swift rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/100.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/1024.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/114.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/120.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/128.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/144.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/152.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/16.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/167.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/172.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/180.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/196.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/20.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/216.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/256.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/29.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/32.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/40.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/48.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/50.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/512.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/55.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/57.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/58.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/60.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/64.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/72.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/76.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/80.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/87.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/88.png (100%) rename secant/{ => Resources}/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout0.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout0.imageset/callout0-1.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout0.imageset/callout0-2.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout0.imageset/callout0.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout1.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout1.imageset/callout1-1.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout1.imageset/callout1-2.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout1.imageset/callout1.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout2.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout2.imageset/callout2-1.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout2.imageset/callout2-2.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout2.imageset/callout2.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout3.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout3.imageset/callout3-1.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout3.imageset/callout3-2.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout3.imageset/callout3.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout4.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout-1.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout-2.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout.jpg (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/calloutBackupFailed.png (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/calloutBackupFailed@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1.png (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/calloutBackupSucceeded.png (100%) rename secant/{ => Resources}/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/calloutBackupSucceeded@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/bank.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme@3x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme@3x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/list.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/list.imageset/iconlist_darktheme.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/list.imageset/iconlist_darktheme@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/list.imageset/iconlist_darktheme@3x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme@3x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/profile.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme@3x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme@3x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/qr-code.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme@3x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@3x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/shield.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme@3x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme@3x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/swap.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@3x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme@3x.png (100%) rename secant/{ => Resources}/Assets.xcassets/WelcomeScreenLogo.imageset/Contents.json (100%) rename secant/{ => Resources}/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo.png (100%) rename secant/{ => Resources}/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo@3x.png (100%) rename secant/{ => Resources}/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo.png (100%) rename secant/{ => Resources}/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo@2x.png (100%) rename secant/{ => Resources}/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo@3x.png (100%) rename secant/{ => Resources}/Colors.xcassets/BackgroundColors/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/BackgroundColors/numberedChip.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/BackgroundColors/phraseGridDarkGray.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/BackgroundColors/red.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/BackgroundColors/staticWelcomeScreen.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/ActiveButton.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/ActiveButtonDisabled.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/ActiveButtonPressed.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/ButtonsTitleShadow.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/NeumorphicButtonDarkSide.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/NeumorphicButtonLightSide.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/OnboardingNavigation.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/OnboardingNavigationPressed.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/PrimaryButton.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/PrimaryButtonDisabled.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/PrimaryButtonPressed.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/SecondaryButton.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Buttons/SecondaryButtonPressed.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Cursor/Bar.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Cursor/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Onboarding/BadgeShadow.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Onboarding/CircularFrameDarkOutlineGradientEnd.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Onboarding/CircularFrameDarkOutlineGradientStart.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Onboarding/CircularFrameGradientEnd.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Onboarding/CircularFrameGradientStart.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Onboarding/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Onboarding/NavigationButtonDisabled.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Onboarding/NavigationButtonEnabled.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Onboarding/NeumorphicDarkSide.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Onboarding/NeumorphicLightSide.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Onboarding/badgeBackground.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ProgressIndicator/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ProgressIndicator/GradientLeft.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ProgressIndicator/GradientRight.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ProgressIndicator/NegativeSpace.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ScreenBackground/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ScreenBackground/gradientEnd.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ScreenBackground/gradientStart.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ScreenBackground/greenGradientEnd.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ScreenBackground/greenGradientStart.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ScreenBackground/redGradientEnd.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ScreenBackground/redGradientStart.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Shadow/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Shadow/drawerShadow.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Shadow/emptyChipInnerShadow.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Shadow/numberedTextShadow.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Text/ActiveButtonText.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Text/Body.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Text/Button.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Text/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Text/Heading.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Text/ImportSeedEditor.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Text/Medium.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Text/Regular.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Text/SecondaryButtonText.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Text/TitleText.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Text/captionText.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Text/captionTextShadow.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/Text/highlightedSuperscriptText.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/TextField/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/TextField/TitleAccessoryButton.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/TextField/TitleAccessoryButtonPressed.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/TextField/Underline/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/TextField/Underline/Gray.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/TextField/Underline/Purple.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ZcashBadge/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ZcashBadge/ZcashLogoFill.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ZcashBadge/innerCircle.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ZcashBadge/outerRingGradientEnd.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ZcashBadge/outerRingGradientStart.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ZcashBadge/shadowColor.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ZcashBadge/thickRing.colorset/Contents.json (100%) rename secant/{ => Resources}/Colors.xcassets/ZcashBadge/thinRing.colorset/Contents.json (100%) rename secant/{ => Resources}/Fonts/Roboto/LICENSE.txt (100%) rename secant/{ => Resources}/Fonts/Roboto/Roboto-Black.ttf (100%) rename secant/{ => Resources}/Fonts/Roboto/Roboto-BlackItalic.ttf (100%) rename secant/{ => Resources}/Fonts/Roboto/Roboto-Bold.ttf (100%) rename secant/{ => Resources}/Fonts/Roboto/Roboto-BoldItalic.ttf (100%) rename secant/{ => Resources}/Fonts/Roboto/Roboto-Italic.ttf (100%) rename secant/{ => Resources}/Fonts/Roboto/Roboto-Light.ttf (100%) rename secant/{ => Resources}/Fonts/Roboto/Roboto-LightItalic.ttf (100%) rename secant/{ => Resources}/Fonts/Roboto/Roboto-Medium.ttf (100%) rename secant/{ => Resources}/Fonts/Roboto/Roboto-MediumItalic.ttf (100%) rename secant/{ => Resources}/Fonts/Roboto/Roboto-Regular.ttf (100%) rename secant/{ => Resources}/Fonts/Roboto/Roboto-Thin.ttf (100%) rename secant/{ => Resources}/Fonts/Roboto/Roboto-ThinItalic.ttf (100%) rename secant/{ => Resources}/Fonts/Rubik/Rubik-Italic-VariableFont_wght.ttf (100%) rename secant/{ => Resources}/Fonts/Rubik/Rubik-VariableFont_wght.ttf (100%) rename secant/{ => Resources}/Fonts/Zboto.otf (100%) rename secant/{ => Resources}/Generated/Fonts+Generated.swift (100%) rename secant/{ => Resources}/Generated/XCAssets+Generated.swift (100%) rename secant/{ => Resources}/Localizable.strings (100%) delete mode 100644 secant/Stubs/MockServices.swift rename secant/{UIComponents => UI Components}/Backgrounds/ScreenBackground.swift (100%) rename secant/{UIComponents => UI Components}/Buttons/ActiveButton.swift (100%) rename secant/{UIComponents => UI Components}/Buttons/NavigationButtonStyle.swift (100%) rename secant/{UIComponents => UI Components}/Buttons/NeumorphicDesignModifier.swift (100%) rename secant/{UIComponents => UI Components}/Buttons/PrimaryButton.swift (100%) rename secant/{UIComponents => UI Components}/Buttons/SecondaryButton.swift (100%) rename secant/{UIComponents => UI Components}/Buttons/StandardButtonStyle.swift (100%) rename secant/{UIComponents => UI Components}/Chips/ColoredChip.swift (100%) rename secant/{UIComponents => UI Components}/Chips/EmptyChip.swift (100%) rename secant/{UIComponents => UI Components}/Chips/EnumeratedChip.swift (100%) rename secant/{UIComponents => UI Components}/Chips/PhraseChip.swift (100%) rename secant/{Features/BackupFlow/Views => UI Components/Chips}/WordChipGrid.swift (100%) rename secant/{UIComponents => UI Components}/CircularFrame/CircularFrame.swift (100%) rename secant/{UIComponents => UI Components}/CircularFrame/CircularFrameBackground.swift (100%) rename secant/{UIComponents => UI Components}/CircularFrame/CircularFrameBadge.swift (100%) rename secant/{UIComponents => UI Components}/DesignGuide.swift (100%) rename secant/{UIComponents => UI Components}/Drawer/Drawer.swift (100%) rename secant/{UIComponents => UI Components}/Extensions/ConditionalModifier.swift (100%) rename secant/{UIComponents => UI Components}/Extensions/View+InnerShadow.swift (100%) rename secant/{UIComponents => UI Components}/FontStyles/SecantButtonStyles.swift (100%) rename secant/{UIComponents => UI Components}/FontStyles/SecantTextStyles.swift (100%) rename secant/{Features/ImportWallet/Views => UI Components/ImportSeedEditor}/ImportSeedEditor.swift (100%) rename secant/{UIComponents => UI Components}/ProgressIndicators/OnboardingProgressIndicator.swift (100%) rename secant/{UIComponents => UI Components}/Shapes/ZcashSymbol.swift (100%) rename secant/{UIComponents/TextFields/TransactionAmount => UI Components/TextFields/Components/CurrencySelection}/CurrencySelectionStore.swift (100%) rename secant/{UIComponents/TextFields/TransactionAmount/TransactionCurrencySelector.swift => UI Components/TextFields/Components/CurrencySelection/CurrencySelectionView.swift} (92%) rename secant/{UIComponents => UI Components}/TextFields/Components/TextFieldFooter.swift (100%) rename secant/{UIComponents => UI Components}/TextFields/Components/TextFieldTitleAccessoryButtonStyle.swift (100%) rename secant/{UIComponents/TextFields/Components => UI Components/TextFields}/SingleLineTextField.swift (94%) rename secant/{UIComponents/TextFields/Components/TextFieldInput.swift => UI Components/TextFields/TCATextField/TCATextField.swift} (91%) rename secant/{UIComponents/TextFields/Components/TextFieldStore.swift => UI Components/TextFields/TCATextField/TCATextFieldStore.swift} (59%) rename secant/{UIComponents => UI Components}/TextFields/TransactionAddress/TransactionAddressTextField.swift (89%) create mode 100644 secant/UI Components/TextFields/TransactionAddress/TransactionAddressTextFieldStore.swift rename secant/{UIComponents => UI Components}/TextFields/TransactionAmount/TransactionAmountTextField.swift (91%) rename secant/{UIComponents/TextFields/TransactionAmount/TransactionAmountInputStore.swift => UI Components/TextFields/TransactionAmount/TransactionAmountTextFieldStore.swift} (59%) create mode 100644 secant/UI Components/ZcashBadge/ZcashBadge.swift delete mode 100644 secant/UIComponents/TextFields/TransactionAddress/TransactionAddressInputStore.swift create mode 100644 secant/Utils/Array+Chunked.swift rename secant/{Util => Utils}/Bindings.swift (100%) rename secant/{Util => Utils}/Clamped.swift (100%) create mode 100644 secant/Utils/Date+Readable.swift rename secant/{Util => Utils}/DebugFrame.swift (100%) rename secant/{Util => Utils}/DebugMenu.swift (100%) rename secant/{Util => Utils}/Double+Zcash.swift (100%) rename secant/{Util => Utils}/Int64+Zcash.swift (100%) rename secant/{Util => Utils}/NavigationLinks.swift (100%) rename secant/{Util => Utils}/Previews.swift (100%) create mode 100644 secant/Utils/SDKSynchronizer+SyncStatus.swift rename secant/{Util => Utils}/ScrollableWhenScaled.swift (100%) rename secant/{Util => Utils}/Strings.swift (100%) rename secant/{Util => Utils}/UInt+SuperscriptText.swift (100%) rename secant/{Features/BackupFlow => Utils}/View+WhenDraggable.swift (100%) rename secant/{Util => Utils}/WithStateBinding.swift (100%) create mode 100644 secant/Wrappers/WrappedDatabaseFiles.swift create mode 100644 secant/Wrappers/WrappedFeedbackGenerator.swift rename secant/{Util/MnemonicSeedPhraseProvider.swift => Wrappers/WrappedMnemonic.swift} (92%) create mode 100644 secant/Wrappers/WrappedPasteboard.swift rename secant/{Util/WalletStorageInteractor.swift => Wrappers/WrappedWalletStorage.swift} (84%) diff --git a/secant.xcodeproj/project.pbxproj b/secant.xcodeproj/project.pbxproj index 07a92d4..e28f485 100644 --- a/secant.xcodeproj/project.pbxproj +++ b/secant.xcodeproj/project.pbxproj @@ -13,7 +13,6 @@ 0D18581B272728D60046B928 /* PhraseChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D18581A272728D60046B928 /* PhraseChip.swift */; }; 0D1C1AA327611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1C1AA227611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift */; }; 0D2ACE8026C2C67100D62E3C /* Zboto.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0D2ACE7F26C2C67100D62E3C /* Zboto.otf */; }; - 0D354A0926D5A9D000315F45 /* Services.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D354A0626D5A9D000315F45 /* Services.swift */; }; 0D35CC46277A36E00074316A /* ScrollableWhenScaled.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D35CC45277A36E00074316A /* ScrollableWhenScaled.swift */; }; 0D3D04082728B3440032ABC1 /* RecoveryPhraseDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D3D04072728B3440032ABC1 /* RecoveryPhraseDisplayView.swift */; }; 0D3D040A2728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D3D04092728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift */; }; @@ -25,7 +24,6 @@ 0D535FDE271F4214009A9E3E /* Rubik-Italic-VariableFont_wght.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0D535FDC271F4214009A9E3E /* Rubik-Italic-VariableFont_wght.ttf */; }; 0D535FDF271F4214009A9E3E /* Rubik-VariableFont_wght.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0D535FDD271F4214009A9E3E /* Rubik-VariableFont_wght.ttf */; }; 0D535FE2271F9476009A9E3E /* EnumeratedChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D535FE1271F9476009A9E3E /* EnumeratedChip.swift */; }; - 0D5D16F526E24CCF00AD33D1 /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D5D16F426E24CCF00AD33D1 /* AppError.swift */; }; 0D6D628B276A528E002FB4CC /* DropDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D6D628A276A528D002FB4CC /* DropDelegate.swift */; }; 0D7CE63427349B5D0020E050 /* View+WhenDraggable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7CE63327349B5D0020E050 /* View+WhenDraggable.swift */; }; 0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7DF08B271DCC0E00530046 /* ScreenBackground.swift */; }; @@ -46,27 +44,27 @@ 0DACFA9A27209FA70039EEA5 /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0DACFA8D27209FA70039EEA5 /* Roboto-Light.ttf */; }; 0DACFA9C27209FA70039EEA5 /* Roboto-ThinItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0DACFA8F27209FA70039EEA5 /* Roboto-ThinItalic.ttf */; }; 0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB8AA80271DC7520035BC9D /* DesignGuide.swift */; }; - 0DC487C32772574C00BE6A63 /* ValidationSucceededView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DC487C22772574C00BE6A63 /* ValidationSucceededView.swift */; }; - 0DDB6A5127737D4A0012A410 /* ValidationFailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DDB6A5027737D4A0012A410 /* ValidationFailedView.swift */; }; + 0DC487C32772574C00BE6A63 /* RecoveryPhraseBackupSucceededView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DC487C22772574C00BE6A63 /* RecoveryPhraseBackupSucceededView.swift */; }; + 0DDB6A5127737D4A0012A410 /* RecoveryPhraseBackupFailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DDB6A5027737D4A0012A410 /* RecoveryPhraseBackupFailedView.swift */; }; 0DF2DC51272344E400FA31E2 /* EmptyChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF2DC50272344E400FA31E2 /* EmptyChip.swift */; }; 0DF2DC5427235E3E00FA31E2 /* View+InnerShadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF2DC5327235E3E00FA31E2 /* View+InnerShadow.swift */; }; 0DF482BA2787ADA800EB37D6 /* ConditionalModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF482B92787ADA800EB37D6 /* ConditionalModifier.swift */; }; 0DFE93DF272C6D4B000FCCA5 /* RecoveryPhraseBackupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DFE93DE272C6D4B000FCCA5 /* RecoveryPhraseBackupTests.swift */; }; - 0DFE93E1272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DFE93E0272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift */; }; - 0DFE93E3272CA1AA000FCCA5 /* RecoveryPhraseValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DFE93E2272CA1AA000FCCA5 /* RecoveryPhraseValidation.swift */; }; + 0DFE93E1272C9ECB000FCCA5 /* RecoveryPhraseBackupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DFE93E0272C9ECB000FCCA5 /* RecoveryPhraseBackupView.swift */; }; + 0DFE93E3272CA1AA000FCCA5 /* RecoveryPhraseValidationFlowStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DFE93E2272CA1AA000FCCA5 /* RecoveryPhraseValidationFlowStore.swift */; }; 0DFE93E6272CB6F7000FCCA5 /* RecoveryPhraseValidationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DFE93E5272CB6F7000FCCA5 /* RecoveryPhraseValidationTests.swift */; }; 2E35F99227B28E7600EB79CD /* SingleLineTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E35F99127B28E7600EB79CD /* SingleLineTextField.swift */; }; 2E35F99A27B3E99C00EB79CD /* TextFieldTitleAccessoryButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E35F99927B3E99C00EB79CD /* TextFieldTitleAccessoryButtonStyle.swift */; }; 2E58E73B274679F000B2B84B /* OnboardingHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E58E73A274679F000B2B84B /* OnboardingHeaderView.swift */; }; 2E6CF8DD27D78319004DCD7A /* CurrencySelectionStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E6CF8DC27D78319004DCD7A /* CurrencySelectionStore.swift */; }; 2E8719CB27FB09990082C926 /* TransactionAmountTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8719CA27FB09990082C926 /* TransactionAmountTextField.swift */; }; - 2E8719CD27FB0D3B0082C926 /* TransactionCurrencySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8719CC27FB0D3B0082C926 /* TransactionCurrencySelector.swift */; }; + 2E8719CD27FB0D3B0082C926 /* CurrencySelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8719CC27FB0D3B0082C926 /* CurrencySelectionView.swift */; }; 2EA11F5B27467EF800709571 /* OnboardingFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EA11F5A27467EF800709571 /* OnboardingFooterView.swift */; }; 2EA11F5D27467F7700709571 /* OnboardingContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EA11F5C27467F7700709571 /* OnboardingContentView.swift */; }; - 2EB1C5E827D77F6100BC43D7 /* TextFieldStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB1C5E727D77F6100BC43D7 /* TextFieldStore.swift */; }; - 2EB660E02747EAB900A06A07 /* OnboardingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E5C03802738C570008BFFD3 /* OnboardingScreen.swift */; }; - 2EB7758727FC67FD00269373 /* TransactionAmountInputStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB7758627FC67FD00269373 /* TransactionAmountInputStore.swift */; }; - 2EDA07A027EDE18C00D6F09B /* TextFieldInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EDA079F27EDE18C00D6F09B /* TextFieldInput.swift */; }; + 2EB1C5E827D77F6100BC43D7 /* TCATextFieldStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB1C5E727D77F6100BC43D7 /* TCATextFieldStore.swift */; }; + 2EB660E02747EAB900A06A07 /* OnboardingFlowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E5C03802738C570008BFFD3 /* OnboardingFlowView.swift */; }; + 2EB7758727FC67FD00269373 /* TransactionAmountTextFieldStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB7758627FC67FD00269373 /* TransactionAmountTextFieldStore.swift */; }; + 2EDA07A027EDE18C00D6F09B /* TCATextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EDA079F27EDE18C00D6F09B /* TCATextField.swift */; }; 2EDA07A227EDE1AE00D6F09B /* TextFieldFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EDA07A127EDE1AE00D6F09B /* TextFieldFooter.swift */; }; 2EDA07A427EDE2A900D6F09B /* DebugFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EDA07A327EDE2A900D6F09B /* DebugFrame.swift */; }; 660558E9270C7A54009D6954 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 660558E8270C7A54009D6954 /* Colors.xcassets */; }; @@ -76,8 +74,7 @@ 663FABA0271D876200E495F8 /* PrimaryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663FAB9F271D876200E495F8 /* PrimaryButton.swift */; }; 663FABA2271D876C00E495F8 /* SecondaryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663FABA1271D876C00E495F8 /* SecondaryButton.swift */; }; 6654C73A2715A38000901167 /* ComposableArchitecture in Frameworks */ = {isa = PBXBuildFile; productRef = 6654C7392715A38000901167 /* ComposableArchitecture */; }; - 6654C73E2715A41300901167 /* OnboardingStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6654C73D2715A41300901167 /* OnboardingStore.swift */; }; - 6654C7412715A47300901167 /* Onboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6654C7402715A47300901167 /* Onboarding.swift */; }; + 6654C73E2715A41300901167 /* OnboardingFlowStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6654C73D2715A41300901167 /* OnboardingFlowStore.swift */; }; 6654C7442715A4AC00901167 /* OnboardingStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6654C7432715A4AC00901167 /* OnboardingStoreTests.swift */; }; 665C963F272C26E600BC04FB /* CircularFrameBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 665C963E272C26E600BC04FB /* CircularFrameBackground.swift */; }; 669FDAE9272C23B3007B9422 /* CircularFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 669FDAE8272C23B3007B9422 /* CircularFrame.swift */; }; @@ -89,7 +86,7 @@ 9E02B56C27FED475005B809B /* DatabaseFilesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */; }; 9E02B5C3280458D2005B809B /* WrappedDerivationTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E02B5C2280458D2005B809B /* WrappedDerivationTool.swift */; }; 9E2AC0FF27D8EC120042AA47 /* MnemonicSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9E2AC0FE27D8EC120042AA47 /* MnemonicSwift */; }; - 9E2AC10127D8EF0B0042AA47 /* MnemonicSeedPhraseProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2AC10027D8EF0B0042AA47 /* MnemonicSeedPhraseProvider.swift */; }; + 9E2AC10127D8EF0B0042AA47 /* WrappedMnemonic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2AC10027D8EF0B0042AA47 /* WrappedMnemonic.swift */; }; 9E2AC10327DA28200042AA47 /* WalletStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2AC10227DA28200042AA47 /* WalletStorage.swift */; }; 9E2DF99C27CF704D00649636 /* ImportWalletStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99827CF704D00649636 /* ImportWalletStore.swift */; }; 9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */; }; @@ -101,15 +98,26 @@ 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 */; }; - 9E5BF63C2818305D00BA3F17 /* TransactionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF63B2818305D00BA3F17 /* TransactionState.swift */; }; 9E5BF63F2819542C00BA3F17 /* TransactionHistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF63E2819542C00BA3F17 /* TransactionHistoryTests.swift */; }; 9E5BF641281FD7B600BA3F17 /* TransactionFailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF640281FD7B600BA3F17 /* TransactionFailedView.swift */; }; 9E5BF644281FEC9900BA3F17 /* SendTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF643281FEC9900BA3F17 /* SendTests.swift */; }; 9E5BF6462821028C00BA3F17 /* WrappedUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF6452821028C00BA3F17 /* WrappedUserDefaults.swift */; }; 9E5BF648282277BE00BA3F17 /* WrappedNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF647282277BE00BA3F17 /* WrappedNotificationCenter.swift */; }; 9E5BF64F2823E94900BA3F17 /* TransactionAddressTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF64D2823E94900BA3F17 /* TransactionAddressTextField.swift */; }; - 9E5BF6502823E94900BA3F17 /* TransactionAddressInputStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF64E2823E94900BA3F17 /* TransactionAddressInputStore.swift */; }; - 9E69A24D27FB002800A55317 /* Welcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E69A24C27FB002800A55317 /* Welcome.swift */; }; + 9E5BF6502823E94900BA3F17 /* TransactionAddressTextFieldStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF64E2823E94900BA3F17 /* TransactionAddressTextFieldStore.swift */; }; + 9E69A24D27FB002800A55317 /* WelcomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E69A24C27FB002800A55317 /* WelcomeStore.swift */; }; + 9E7FE0CF282D257400C374E8 /* SDKSynchronizer+SyncStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0CE282D257400C374E8 /* SDKSynchronizer+SyncStatus.swift */; }; + 9E7FE0D3282D274E00C374E8 /* Date+Readable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0D2282D274E00C374E8 /* Date+Readable.swift */; }; + 9E7FE0D5282D281800C374E8 /* Array+Chunked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0D4282D281800C374E8 /* Array+Chunked.swift */; }; + 9E7FE0D7282D286500C374E8 /* RecoveryPhrase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0D6282D286500C374E8 /* RecoveryPhrase.swift */; }; + 9E7FE0D9282D289B00C374E8 /* WrappedFeedbackGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0D8282D289B00C374E8 /* WrappedFeedbackGenerator.swift */; }; + 9E7FE0DB282D28F100C374E8 /* WrappedPasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0DA282D28F100C374E8 /* WrappedPasteboard.swift */; }; + 9E7FE0DD282D298900C374E8 /* ValidationWord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0DC282D298900C374E8 /* ValidationWord.swift */; }; + 9E7FE0DF282D2DD600C374E8 /* ZcashBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0DE282D2DD600C374E8 /* ZcashBadge.swift */; }; + 9E7FE0E6282E7B1100C374E8 /* StoredWallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0E5282E7B1100C374E8 /* StoredWallet.swift */; }; + 9E7FE0E8282E7B7C00C374E8 /* WrappedDatabaseFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0E7282E7B7C00C374E8 /* WrappedDatabaseFiles.swift */; }; + 9E7FE0EC282E7D9400C374E8 /* TransactionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF63B2818305D00BA3F17 /* TransactionState.swift */; }; + 9E7FE0EE282E7E8700C374E8 /* SendFlowTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0D0282D26BD00C374E8 /* SendFlowTransaction.swift */; }; 9E80B47227E4B34B008FF493 /* UserPreferencesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */; }; 9EAFEB822805793200199FC9 /* AppReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB812805793200199FC9 /* AppReducerTests.swift */; }; 9EAFEB84280597B700199FC9 /* WrappedSecItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */; }; @@ -120,7 +128,7 @@ 9EAFEB902808183D00199FC9 /* SandboxStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */; }; 9EAFEB9128081E9400199FC9 /* HomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874ED273C4DE200F0E875 /* HomeStore.swift */; }; 9EAFEB9228081E9400199FC9 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874EF273C4DE200F0E875 /* HomeView.swift */; }; - 9EBEF87A27CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */; }; + 9EBEF87A27CE369800B4F343 /* RecoveryPhraseValidationFlowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBEF87927CE369800B4F343 /* RecoveryPhraseValidationFlowView.swift */; }; 9ECAE56827FC713C0089A0EF /* DatabaseFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */; }; 9EDDEA8C28250F9C00B4100C /* Double+Zcash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EDDEA8B28250F9C00B4100C /* Double+Zcash.swift */; }; 9EDDEAA22829610D00B4100C /* CurrencySelectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EDDEA9F2829610D00B4100C /* CurrencySelectionTests.swift */; }; @@ -129,16 +137,16 @@ 9EF8135C27ECC25E0075AF48 /* WalletStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */; }; 9EF8135D27ECC25E0075AF48 /* UserPreferencesStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135B27ECC25E0075AF48 /* UserPreferencesStorageTests.swift */; }; 9EF8136027F043CC0075AF48 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135F27F043CC0075AF48 /* AppDelegate.swift */; }; - 9EF8139127F191BF0075AF48 /* WalletStorageInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8139027F191BF0075AF48 /* WalletStorageInteractor.swift */; }; + 9EF8139127F191BF0075AF48 /* WrappedWalletStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8139027F191BF0075AF48 /* WrappedWalletStorage.swift */; }; 9EF8139827F1FAEC0075AF48 /* ZcashLightClientKit in Frameworks */ = {isa = PBXBuildFile; productRef = 9EF8139727F1FAEC0075AF48 /* ZcashLightClientKit */; }; 9EF8139C27F47AED0075AF48 /* InitializationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8139B27F47AED0075AF48 /* InitializationState.swift */; }; F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9322DBF273B555C00C105B5 /* NavigationLinks.swift */; }; F93673D62742CB840099C6AF /* Previews.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93673D52742CB840099C6AF /* Previews.swift */; }; - F96B41E7273B501F0021B49A /* TransactionHistoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */; }; + F96B41E7273B501F0021B49A /* TransactionHistoryFlowStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E3273B501F0021B49A /* TransactionHistoryFlowStore.swift */; }; F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E5273B501F0021B49A /* TransactionDetailView.swift */; }; - F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */; }; + F96B41E9273B501F0021B49A /* TransactionHistoryFlowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E6273B501F0021B49A /* TransactionHistoryFlowView.swift */; }; F96B41EB273B50520021B49A /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41EA273B50520021B49A /* Strings.swift */; }; - F9971A4D27680DC400A2DB75 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A4A27680DC400A2DB75 /* App.swift */; }; + F9971A4D27680DC400A2DB75 /* AppStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A4A27680DC400A2DB75 /* AppStore.swift */; }; F9971A4E27680DC400A2DB75 /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A4C27680DC400A2DB75 /* AppView.swift */; }; F9971A5327680DD000A2DB75 /* ProfileStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5027680DD000A2DB75 /* ProfileStore.swift */; }; F9971A5427680DD000A2DB75 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5227680DD000A2DB75 /* ProfileView.swift */; }; @@ -146,16 +154,16 @@ F9971A5A27680DDE00A2DB75 /* RequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5827680DDE00A2DB75 /* RequestView.swift */; }; F9971A5F27680DF600A2DB75 /* ScanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5D27680DF600A2DB75 /* ScanView.swift */; }; F9971A6027680DF600A2DB75 /* ScanStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A5E27680DF600A2DB75 /* ScanStore.swift */; }; - F9971A6527680DFE00A2DB75 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A6227680DFE00A2DB75 /* Settings.swift */; }; + F9971A6527680DFE00A2DB75 /* SettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A6227680DFE00A2DB75 /* SettingsStore.swift */; }; F9971A6627680DFE00A2DB75 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A6427680DFE00A2DB75 /* SettingsView.swift */; }; - F9971A6B27680E1000A2DB75 /* WalletInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A6827680E1000A2DB75 /* WalletInfo.swift */; }; + F9971A6B27680E1000A2DB75 /* WalletInfoStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A6827680E1000A2DB75 /* WalletInfoStore.swift */; }; F9971A6C27680E1000A2DB75 /* WalletInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A6A27680E1000A2DB75 /* WalletInfoView.swift */; }; F9C165B4274031F600592F76 /* Bindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165B3274031F600592F76 /* Bindings.swift */; }; - F9C165BF2740403600592F76 /* SendStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165B72740403600592F76 /* SendStore.swift */; }; + F9C165BF2740403600592F76 /* SendFlowStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165B72740403600592F76 /* SendFlowStore.swift */; }; F9C165C02740403600592F76 /* TransactionConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165B92740403600592F76 /* TransactionConfirmationView.swift */; }; F9C165C22740403600592F76 /* CreateTransactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165BB2740403600592F76 /* CreateTransactionView.swift */; }; F9C165C42740403600592F76 /* TransactionSentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165BD2740403600592F76 /* TransactionSentView.swift */; }; - F9C165CB2741AB5D00592F76 /* SendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165CA2741AB5D00592F76 /* SendView.swift */; }; + F9C165CB2741AB5D00592F76 /* SendFlowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165CA2741AB5D00592F76 /* SendFlowView.swift */; }; F9EEB8162742C2210032EEB8 /* WithStateBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */; }; /* End PBXBuildFile section */ @@ -183,7 +191,6 @@ 0D18581A272728D60046B928 /* PhraseChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhraseChip.swift; sourceTree = ""; }; 0D1C1AA227611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseDisplayReducerTests.swift; sourceTree = ""; }; 0D2ACE7F26C2C67100D62E3C /* Zboto.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Zboto.otf; sourceTree = ""; }; - 0D354A0626D5A9D000315F45 /* Services.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Services.swift; sourceTree = ""; }; 0D35CC45277A36E00074316A /* ScrollableWhenScaled.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollableWhenScaled.swift; sourceTree = ""; }; 0D3D04072728B3440032ABC1 /* RecoveryPhraseDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseDisplayView.swift; sourceTree = ""; }; 0D3D04092728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseDisplayStore.swift; sourceTree = ""; }; @@ -201,13 +208,11 @@ 0D535FDC271F4214009A9E3E /* Rubik-Italic-VariableFont_wght.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Rubik-Italic-VariableFont_wght.ttf"; sourceTree = ""; }; 0D535FDD271F4214009A9E3E /* Rubik-VariableFont_wght.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Rubik-VariableFont_wght.ttf"; sourceTree = ""; }; 0D535FE1271F9476009A9E3E /* EnumeratedChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumeratedChip.swift; sourceTree = ""; }; - 0D5D16F426E24CCF00AD33D1 /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = ""; }; 0D6D628A276A528D002FB4CC /* DropDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropDelegate.swift; sourceTree = ""; }; 0D7CE63327349B5D0020E050 /* View+WhenDraggable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+WhenDraggable.swift"; sourceTree = ""; }; 0D7DF08B271DCC0E00530046 /* ScreenBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenBackground.swift; sourceTree = ""; }; 0D8A43C3272AEEDE005A6414 /* SecantTextStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecantTextStyles.swift; sourceTree = ""; }; 0D8A43C5272B129C005A6414 /* WordChipGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordChipGrid.swift; sourceTree = ""; }; - 0DA13CA426C1963000E3B610 /* Balance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Balance.swift; sourceTree = ""; }; 0DACFA7E27208CE00039EEA5 /* Clamped.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clamped.swift; sourceTree = ""; }; 0DACFA8027208D940039EEA5 /* UInt+SuperscriptText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UInt+SuperscriptText.swift"; sourceTree = ""; }; 0DACFA8327209FA60039EEA5 /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = ""; }; @@ -223,27 +228,27 @@ 0DACFA8D27209FA70039EEA5 /* Roboto-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Light.ttf"; sourceTree = ""; }; 0DACFA8F27209FA70039EEA5 /* Roboto-ThinItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-ThinItalic.ttf"; sourceTree = ""; }; 0DB8AA80271DC7520035BC9D /* DesignGuide.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DesignGuide.swift; sourceTree = ""; }; - 0DC487C22772574C00BE6A63 /* ValidationSucceededView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationSucceededView.swift; sourceTree = ""; }; - 0DDB6A5027737D4A0012A410 /* ValidationFailedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidationFailedView.swift; sourceTree = ""; }; + 0DC487C22772574C00BE6A63 /* RecoveryPhraseBackupSucceededView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseBackupSucceededView.swift; sourceTree = ""; }; + 0DDB6A5027737D4A0012A410 /* RecoveryPhraseBackupFailedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseBackupFailedView.swift; sourceTree = ""; }; 0DF2DC50272344E400FA31E2 /* EmptyChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyChip.swift; sourceTree = ""; }; 0DF2DC5327235E3E00FA31E2 /* View+InnerShadow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+InnerShadow.swift"; sourceTree = ""; }; 0DF482B92787ADA800EB37D6 /* ConditionalModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionalModifier.swift; sourceTree = ""; }; 0DFE93DE272C6D4B000FCCA5 /* RecoveryPhraseBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseBackupTests.swift; sourceTree = ""; }; - 0DFE93E0272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseBackupValidationView.swift; sourceTree = ""; }; - 0DFE93E2272CA1AA000FCCA5 /* RecoveryPhraseValidation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseValidation.swift; sourceTree = ""; }; + 0DFE93E0272C9ECB000FCCA5 /* RecoveryPhraseBackupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseBackupView.swift; sourceTree = ""; }; + 0DFE93E2272CA1AA000FCCA5 /* RecoveryPhraseValidationFlowStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseValidationFlowStore.swift; sourceTree = ""; }; 0DFE93E5272CB6F7000FCCA5 /* RecoveryPhraseValidationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseValidationTests.swift; sourceTree = ""; }; 2E35F99127B28E7600EB79CD /* SingleLineTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleLineTextField.swift; sourceTree = ""; }; 2E35F99927B3E99C00EB79CD /* TextFieldTitleAccessoryButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldTitleAccessoryButtonStyle.swift; sourceTree = ""; }; 2E58E73A274679F000B2B84B /* OnboardingHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingHeaderView.swift; sourceTree = ""; }; - 2E5C03802738C570008BFFD3 /* OnboardingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingScreen.swift; sourceTree = ""; }; + 2E5C03802738C570008BFFD3 /* OnboardingFlowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingFlowView.swift; sourceTree = ""; }; 2E6CF8DC27D78319004DCD7A /* CurrencySelectionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencySelectionStore.swift; sourceTree = ""; }; 2E8719CA27FB09990082C926 /* TransactionAmountTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionAmountTextField.swift; sourceTree = ""; }; - 2E8719CC27FB0D3B0082C926 /* TransactionCurrencySelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionCurrencySelector.swift; sourceTree = ""; }; + 2E8719CC27FB0D3B0082C926 /* CurrencySelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencySelectionView.swift; sourceTree = ""; }; 2EA11F5A27467EF800709571 /* OnboardingFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingFooterView.swift; sourceTree = ""; }; 2EA11F5C27467F7700709571 /* OnboardingContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingContentView.swift; sourceTree = ""; }; - 2EB1C5E727D77F6100BC43D7 /* TextFieldStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldStore.swift; sourceTree = ""; }; - 2EB7758627FC67FD00269373 /* TransactionAmountInputStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionAmountInputStore.swift; sourceTree = ""; }; - 2EDA079F27EDE18C00D6F09B /* TextFieldInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldInput.swift; sourceTree = ""; }; + 2EB1C5E727D77F6100BC43D7 /* TCATextFieldStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TCATextFieldStore.swift; sourceTree = ""; }; + 2EB7758627FC67FD00269373 /* TransactionAmountTextFieldStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionAmountTextFieldStore.swift; sourceTree = ""; }; + 2EDA079F27EDE18C00D6F09B /* TCATextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TCATextField.swift; sourceTree = ""; }; 2EDA07A127EDE1AE00D6F09B /* TextFieldFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldFooter.swift; sourceTree = ""; }; 2EDA07A327EDE2A900D6F09B /* DebugFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugFrame.swift; sourceTree = ""; }; 660558E8270C7A54009D6954 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; @@ -252,8 +257,7 @@ 663FAB9B271D874D00E495F8 /* ActiveButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveButton.swift; sourceTree = ""; }; 663FAB9F271D876200E495F8 /* PrimaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryButton.swift; sourceTree = ""; }; 663FABA1271D876C00E495F8 /* SecondaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondaryButton.swift; sourceTree = ""; }; - 6654C73D2715A41300901167 /* OnboardingStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingStore.swift; sourceTree = ""; }; - 6654C7402715A47300901167 /* Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding.swift; sourceTree = ""; }; + 6654C73D2715A41300901167 /* OnboardingFlowStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingFlowStore.swift; sourceTree = ""; }; 6654C7432715A4AC00901167 /* OnboardingStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingStoreTests.swift; sourceTree = ""; }; 665C963E272C26E600BC04FB /* CircularFrameBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularFrameBackground.swift; sourceTree = ""; }; 669FDAE8272C23B3007B9422 /* CircularFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularFrame.swift; sourceTree = ""; }; @@ -264,7 +268,7 @@ 9E02B56927FED43E005B809B /* WrappedFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedFileManager.swift; sourceTree = ""; }; 9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFilesTests.swift; sourceTree = ""; }; 9E02B5C2280458D2005B809B /* WrappedDerivationTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedDerivationTool.swift; sourceTree = ""; }; - 9E2AC10027D8EF0B0042AA47 /* MnemonicSeedPhraseProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicSeedPhraseProvider.swift; sourceTree = ""; }; + 9E2AC10027D8EF0B0042AA47 /* WrappedMnemonic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedMnemonic.swift; sourceTree = ""; }; 9E2AC10227DA28200042AA47 /* WalletStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletStorage.swift; 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 = ""; }; @@ -283,8 +287,19 @@ 9E5BF6452821028C00BA3F17 /* WrappedUserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedUserDefaults.swift; sourceTree = ""; }; 9E5BF647282277BE00BA3F17 /* WrappedNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedNotificationCenter.swift; sourceTree = ""; }; 9E5BF64D2823E94900BA3F17 /* TransactionAddressTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressTextField.swift; sourceTree = ""; }; - 9E5BF64E2823E94900BA3F17 /* TransactionAddressInputStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressInputStore.swift; sourceTree = ""; }; - 9E69A24C27FB002800A55317 /* Welcome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Welcome.swift; sourceTree = ""; }; + 9E5BF64E2823E94900BA3F17 /* TransactionAddressTextFieldStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressTextFieldStore.swift; sourceTree = ""; }; + 9E69A24C27FB002800A55317 /* WelcomeStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeStore.swift; sourceTree = ""; }; + 9E7FE0CE282D257400C374E8 /* SDKSynchronizer+SyncStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SDKSynchronizer+SyncStatus.swift"; sourceTree = ""; }; + 9E7FE0D0282D26BD00C374E8 /* SendFlowTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendFlowTransaction.swift; sourceTree = ""; }; + 9E7FE0D2282D274E00C374E8 /* Date+Readable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Readable.swift"; sourceTree = ""; }; + 9E7FE0D4282D281800C374E8 /* Array+Chunked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Chunked.swift"; sourceTree = ""; }; + 9E7FE0D6282D286500C374E8 /* RecoveryPhrase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhrase.swift; sourceTree = ""; }; + 9E7FE0D8282D289B00C374E8 /* WrappedFeedbackGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedFeedbackGenerator.swift; sourceTree = ""; }; + 9E7FE0DA282D28F100C374E8 /* WrappedPasteboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedPasteboard.swift; sourceTree = ""; }; + 9E7FE0DC282D298900C374E8 /* ValidationWord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidationWord.swift; sourceTree = ""; }; + 9E7FE0DE282D2DD600C374E8 /* ZcashBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZcashBadge.swift; sourceTree = ""; }; + 9E7FE0E5282E7B1100C374E8 /* StoredWallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoredWallet.swift; sourceTree = ""; }; + 9E7FE0E7282E7B7C00C374E8 /* WrappedDatabaseFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedDatabaseFiles.swift; sourceTree = ""; }; 9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorage.swift; sourceTree = ""; }; 9EAFEB812805793200199FC9 /* AppReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReducerTests.swift; sourceTree = ""; }; 9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItem.swift; sourceTree = ""; }; @@ -293,7 +308,7 @@ 9EAFEB892806F48100199FC9 /* ZCashSDKEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZCashSDKEnvironment.swift; sourceTree = ""; }; 9EAFEB8D2808183D00199FC9 /* SandboxView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxView.swift; sourceTree = ""; }; 9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxStore.swift; sourceTree = ""; }; - 9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseTestPreambleView.swift; sourceTree = ""; }; + 9EBEF87927CE369800B4F343 /* RecoveryPhraseValidationFlowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseValidationFlowView.swift; sourceTree = ""; }; 9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFiles.swift; sourceTree = ""; }; 9EDDEA8B28250F9C00B4100C /* Double+Zcash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Zcash.swift"; sourceTree = ""; }; 9EDDEA9F2829610D00B4100C /* CurrencySelectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrencySelectionTests.swift; sourceTree = ""; }; @@ -302,17 +317,17 @@ 9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageTests.swift; sourceTree = ""; }; 9EF8135B27ECC25E0075AF48 /* UserPreferencesStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorageTests.swift; sourceTree = ""; }; 9EF8135F27F043CC0075AF48 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 9EF8139027F191BF0075AF48 /* WalletStorageInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletStorageInteractor.swift; sourceTree = ""; }; + 9EF8139027F191BF0075AF48 /* WrappedWalletStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedWalletStorage.swift; sourceTree = ""; }; 9EF8139B27F47AED0075AF48 /* InitializationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitializationState.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 = ""; }; F93874EF273C4DE200F0E875 /* HomeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; - F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryStore.swift; sourceTree = ""; }; + F96B41E3273B501F0021B49A /* TransactionHistoryFlowStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryFlowStore.swift; sourceTree = ""; }; F96B41E5273B501F0021B49A /* TransactionDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionDetailView.swift; sourceTree = ""; }; - F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryView.swift; sourceTree = ""; }; + F96B41E6273B501F0021B49A /* TransactionHistoryFlowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryFlowView.swift; sourceTree = ""; }; F96B41EA273B50520021B49A /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; - F9971A4A27680DC400A2DB75 /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; + F9971A4A27680DC400A2DB75 /* AppStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppStore.swift; sourceTree = ""; }; F9971A4C27680DC400A2DB75 /* AppView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppView.swift; sourceTree = ""; }; F9971A5027680DD000A2DB75 /* ProfileStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProfileStore.swift; sourceTree = ""; }; F9971A5227680DD000A2DB75 /* ProfileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = ""; }; @@ -320,16 +335,16 @@ F9971A5827680DDE00A2DB75 /* RequestView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestView.swift; sourceTree = ""; }; F9971A5D27680DF600A2DB75 /* ScanView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanView.swift; sourceTree = ""; }; F9971A5E27680DF600A2DB75 /* ScanStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanStore.swift; sourceTree = ""; }; - F9971A6227680DFE00A2DB75 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; + F9971A6227680DFE00A2DB75 /* SettingsStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsStore.swift; sourceTree = ""; }; F9971A6427680DFE00A2DB75 /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; - F9971A6827680E1000A2DB75 /* WalletInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletInfo.swift; sourceTree = ""; }; + F9971A6827680E1000A2DB75 /* WalletInfoStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletInfoStore.swift; sourceTree = ""; }; F9971A6A27680E1000A2DB75 /* WalletInfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletInfoView.swift; sourceTree = ""; }; F9C165B3274031F600592F76 /* Bindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bindings.swift; sourceTree = ""; }; - F9C165B72740403600592F76 /* SendStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendStore.swift; sourceTree = ""; }; + F9C165B72740403600592F76 /* SendFlowStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendFlowStore.swift; sourceTree = ""; }; F9C165B92740403600592F76 /* TransactionConfirmationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionConfirmationView.swift; sourceTree = ""; }; F9C165BB2740403600592F76 /* CreateTransactionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateTransactionView.swift; sourceTree = ""; }; F9C165BD2740403600592F76 /* TransactionSentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionSentView.swift; sourceTree = ""; }; - F9C165CA2741AB5D00592F76 /* SendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendView.swift; sourceTree = ""; }; + F9C165CA2741AB5D00592F76 /* SendFlowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendFlowView.swift; sourceTree = ""; }; F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithStateBinding.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -364,7 +379,7 @@ 0D0781C2278750C00083ACD7 /* Welcome */ = { isa = PBXGroup; children = ( - 9E69A24C27FB002800A55317 /* Welcome.swift */, + 9E69A24C27FB002800A55317 /* WelcomeStore.swift */, 0D0781C3278750E30083ACD7 /* WelcomeView.swift */, ); path = Welcome; @@ -378,22 +393,6 @@ path = Shapes; sourceTree = ""; }; - 0D170A7426BC9B7500EB6A46 /* MockedDependencies */ = { - isa = PBXGroup; - children = ( - 0D354A0626D5A9D000315F45 /* Services.swift */, - ); - path = MockedDependencies; - sourceTree = ""; - }; - 0D1922EB26BDD9A500052649 /* Screens */ = { - isa = PBXGroup; - children = ( - 2E5C037F2738C55F008BFFD3 /* Onboarding */, - ); - path = Screens; - sourceTree = ""; - }; 0D2ACE7E26C2C65E00D62E3C /* Fonts */ = { isa = PBXGroup; children = ( @@ -404,27 +403,12 @@ path = Fonts; sourceTree = ""; }; - 0D3D04052728B2D70032ABC1 /* BackupFlow */ = { - isa = PBXGroup; - children = ( - 9EBEF87827CE365D00B4F343 /* Preamble */, - 0D6D628A276A528D002FB4CC /* DropDelegate.swift */, - 0D3D04062728B2EC0032ABC1 /* Views */, - 0D3D04092728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift */, - 0DFE93E2272CA1AA000FCCA5 /* RecoveryPhraseValidation.swift */, - 0D7CE63327349B5D0020E050 /* View+WhenDraggable.swift */, - ); - path = BackupFlow; - sourceTree = ""; - }; 0D3D04062728B2EC0032ABC1 /* Views */ = { isa = PBXGroup; children = ( - 0DC487C22772574C00BE6A63 /* ValidationSucceededView.swift */, - 0D3D04072728B3440032ABC1 /* RecoveryPhraseDisplayView.swift */, - 0D8A43C5272B129C005A6414 /* WordChipGrid.swift */, - 0DFE93E0272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift */, - 0DDB6A5027737D4A0012A410 /* ValidationFailedView.swift */, + 0DFE93E0272C9ECB000FCCA5 /* RecoveryPhraseBackupView.swift */, + 0DC487C22772574C00BE6A63 /* RecoveryPhraseBackupSucceededView.swift */, + 0DDB6A5027737D4A0012A410 /* RecoveryPhraseBackupFailedView.swift */, ); path = Views; sourceTree = ""; @@ -452,20 +436,14 @@ 0D4E7A0726B364170058B01E /* secant */ = { isa = PBXGroup; children = ( - 9E02B56827FED42D005B809B /* Wrappers */, - 0DACFA7D27208CC80039EEA5 /* Util */, - 6654C73B2715A3F000901167 /* Features */, - 660558F4270C85F7009D6954 /* Generated */, - 0D5D16F326E24CB900AD33D1 /* AppErrors */, - 0D2ACE7E26C2C65E00D62E3C /* Fonts */, 0DA13CA326C1960A00E3B610 /* Models */, - 0DA13C9126C15E1900E3B610 /* UIComponents */, - 0D1922EB26BDD9A500052649 /* Screens */, - 0D170A7426BC9B7500EB6A46 /* MockedDependencies */, + 9E02B56827FED42D005B809B /* Wrappers */, + 9E7FE0BB282D1DC200C374E8 /* Utils */, + 9E7FE0BD282D1DE100C374E8 /* Dependencies */, + 6654C73B2715A3F000901167 /* Features */, + 9E7FE0BE282D1DFE00C374E8 /* UI Components */, + 9E7FE0B6282D1D9800C374E8 /* Resources */, 0D4E7A0826B364170058B01E /* SecantApp.swift */, - 0D4E7A0C26B364180058B01E /* Assets.xcassets */, - 660558E8270C7A54009D6954 /* Colors.xcassets */, - 9E37A2B727C8F59F00AE57B3 /* Localizable.strings */, 9E2F1C8B280ED6A7004E65FE /* LaunchScreen.storyboard */, 0D4E7A1126B364180058B01E /* Info.plist */, 0D4E7A0E26B364180058B01E /* Preview Content */, @@ -518,6 +496,7 @@ 0D535FE0271F945C009A9E3E /* Chips */ = { isa = PBXGroup; children = ( + 0D8A43C5272B129C005A6414 /* WordChipGrid.swift */, 0D535FE1271F9476009A9E3E /* EnumeratedChip.swift */, 0DF2DC50272344E400FA31E2 /* EmptyChip.swift */, 0D185818272723FF0046B928 /* ColoredChip.swift */, @@ -526,14 +505,6 @@ path = Chips; sourceTree = ""; }; - 0D5D16F326E24CB900AD33D1 /* AppErrors */ = { - isa = PBXGroup; - children = ( - 0D5D16F426E24CCF00AD33D1 /* AppError.swift */, - ); - path = AppErrors; - sourceTree = ""; - }; 0D8A43C2272AEEA7005A6414 /* FontStyles */ = { isa = PBXGroup; children = ( @@ -543,58 +514,21 @@ path = FontStyles; sourceTree = ""; }; - 0DA13C9126C15E1900E3B610 /* UIComponents */ = { - isa = PBXGroup; - children = ( - 9E2F1C8D280EDDEF004E65FE /* Drawer */, - 2E35F99027B28E6800EB79CD /* TextFields */, - 0D0781C5278776B90083ACD7 /* Shapes */, - 0D8A43C2272AEEA7005A6414 /* FontStyles */, - 669FDAE7272C239D007B9422 /* CircularFrame */, - 0DB8AA80271DC7520035BC9D /* DesignGuide.swift */, - 0DF2DC5227235E1F00FA31E2 /* Extensions */, - 0D535FE0271F945C009A9E3E /* Chips */, - 663FAB9A271D873300E495F8 /* Buttons */, - 669FDAE5272C2371007B9422 /* ProgressIndicators */, - 669FDAE6272C2380007B9422 /* Backgrounds */, - ); - path = UIComponents; - sourceTree = ""; - }; 0DA13CA326C1960A00E3B610 /* Models */ = { isa = PBXGroup; children = ( - 0DA13CA426C1963000E3B610 /* Balance.swift */, + 9EF8135F27F043CC0075AF48 /* AppDelegate.swift */, + 9EF8139B27F47AED0075AF48 /* InitializationState.swift */, + 0D6D628A276A528D002FB4CC /* DropDelegate.swift */, + 9E5BF63B2818305D00BA3F17 /* TransactionState.swift */, + 9E7FE0D0282D26BD00C374E8 /* SendFlowTransaction.swift */, + 9E7FE0D6282D286500C374E8 /* RecoveryPhrase.swift */, + 9E7FE0DC282D298900C374E8 /* ValidationWord.swift */, + 9E7FE0E5282E7B1100C374E8 /* StoredWallet.swift */, ); path = Models; sourceTree = ""; }; - 0DACFA7D27208CC80039EEA5 /* Util */ = { - isa = PBXGroup; - children = ( - 0DACFA7E27208CE00039EEA5 /* Clamped.swift */, - 0DACFA8027208D940039EEA5 /* UInt+SuperscriptText.swift */, - F96B41EA273B50520021B49A /* Strings.swift */, - F9322DBF273B555C00C105B5 /* NavigationLinks.swift */, - F9C165B3274031F600592F76 /* Bindings.swift */, - F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */, - F93673D52742CB840099C6AF /* Previews.swift */, - 0D35CC45277A36E00074316A /* ScrollableWhenScaled.swift */, - 2EDA07A327EDE2A900D6F09B /* DebugFrame.swift */, - 9E2AC10027D8EF0B0042AA47 /* MnemonicSeedPhraseProvider.swift */, - 9E2AC10227DA28200042AA47 /* WalletStorage.swift */, - 9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */, - 9EF8139B27F47AED0075AF48 /* InitializationState.swift */, - 9EF8139027F191BF0075AF48 /* WalletStorageInteractor.swift */, - 9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */, - 9EAFEB892806F48100199FC9 /* ZCashSDKEnvironment.swift */, - 9E2F1C8128095AFE004E65FE /* Int64+Zcash.swift */, - 9EDDEA8B28250F9C00B4100C /* Double+Zcash.swift */, - 9E2F1C832809B606004E65FE /* DebugMenu.swift */, - ); - path = Util; - sourceTree = ""; - }; 0DACFA8227209F930039EEA5 /* Roboto */ = { isa = PBXGroup; children = ( @@ -643,6 +577,8 @@ 2E35F99027B28E6800EB79CD /* TextFields */ = { isa = PBXGroup; children = ( + 9E7FE0F0282E80C100C374E8 /* TCATextField */, + 2E35F99127B28E7600EB79CD /* SingleLineTextField.swift */, 9E5BF64C2823E84300BA3F17 /* TransactionAddress */, 9E5BF64B2823C91200BA3F17 /* TransactionAmount */, 2EDA07A527EDE31100D6F09B /* Components */, @@ -650,38 +586,16 @@ path = TextFields; sourceTree = ""; }; - 2E5C037F2738C55F008BFFD3 /* Onboarding */ = { - isa = PBXGroup; - children = ( - 2E5C03802738C570008BFFD3 /* OnboardingScreen.swift */, - 2E58E73A274679F000B2B84B /* OnboardingHeaderView.swift */, - 2EA11F5C27467F7700709571 /* OnboardingContentView.swift */, - 2EA11F5A27467EF800709571 /* OnboardingFooterView.swift */, - ); - path = Onboarding; - sourceTree = ""; - }; 2EDA07A527EDE31100D6F09B /* Components */ = { isa = PBXGroup; children = ( - 2EB1C5E727D77F6100BC43D7 /* TextFieldStore.swift */, - 2E35F99127B28E7600EB79CD /* SingleLineTextField.swift */, - 2EDA079F27EDE18C00D6F09B /* TextFieldInput.swift */, + 9E7FE0EF282E7FA900C374E8 /* CurrencySelection */, 2EDA07A127EDE1AE00D6F09B /* TextFieldFooter.swift */, 2E35F99927B3E99C00EB79CD /* TextFieldTitleAccessoryButtonStyle.swift */, ); path = Components; sourceTree = ""; }; - 660558F4270C85F7009D6954 /* Generated */ = { - isa = PBXGroup; - children = ( - 660558F5270C862F009D6954 /* Fonts+Generated.swift */, - 660558F6270C862F009D6954 /* XCAssets+Generated.swift */, - ); - path = Generated; - sourceTree = ""; - }; 663FAB9A271D873300E495F8 /* Buttons */ = { isa = PBXGroup; children = ( @@ -705,30 +619,34 @@ F9971A4F27680DD000A2DB75 /* Profile */, F9971A5527680DDE00A2DB75 /* Request */, F9971A5B27680DF600A2DB75 /* Scan */, - F9C165B62740403600592F76 /* Send */, + F9C165B62740403600592F76 /* SendFlow */, F9971A6127680DFE00A2DB75 /* Settings */, - F96B41E2273B501F0021B49A /* TransactionHistory */, - 0D3D04052728B2D70032ABC1 /* BackupFlow */, - 6654C73C2715A3FA00901167 /* Onboarding */, + F96B41E2273B501F0021B49A /* TransactionHistoryFlow */, + 9E7FE0E4282E753700C374E8 /* RecoveryPhraseDisplay */, + 9E7FE0E3282E751A00C374E8 /* RecoveryPhraseValidationFlow */, + 6654C73C2715A3FA00901167 /* OnboardingFlow */, F9971A6727680E1000A2DB75 /* WalletInfo */, 9E2DF99727CF704D00649636 /* ImportWallet */, ); path = Features; sourceTree = ""; }; - 6654C73C2715A3FA00901167 /* Onboarding */ = { + 6654C73C2715A3FA00901167 /* OnboardingFlow */ = { isa = PBXGroup; children = ( - 6654C73D2715A41300901167 /* OnboardingStore.swift */, + 6654C73D2715A41300901167 /* OnboardingFlowStore.swift */, + 2E5C03802738C570008BFFD3 /* OnboardingFlowView.swift */, 6654C73F2715A45900901167 /* Views */, ); - path = Onboarding; + path = OnboardingFlow; sourceTree = ""; }; 6654C73F2715A45900901167 /* Views */ = { isa = PBXGroup; children = ( - 6654C7402715A47300901167 /* Onboarding.swift */, + 2E58E73A274679F000B2B84B /* OnboardingHeaderView.swift */, + 2EA11F5C27467F7700709571 /* OnboardingContentView.swift */, + 2EA11F5A27467EF800709571 /* OnboardingFooterView.swift */, ); path = Views; sourceTree = ""; @@ -776,6 +694,11 @@ 9EAFEB872806E5AE00199FC9 /* WrappedSDKSynchronizer.swift */, 9E5BF6452821028C00BA3F17 /* WrappedUserDefaults.swift */, 9E5BF647282277BE00BA3F17 /* WrappedNotificationCenter.swift */, + 9E2AC10027D8EF0B0042AA47 /* WrappedMnemonic.swift */, + 9EF8139027F191BF0075AF48 /* WrappedWalletStorage.swift */, + 9E7FE0D8282D289B00C374E8 /* WrappedFeedbackGenerator.swift */, + 9E7FE0DA282D28F100C374E8 /* WrappedPasteboard.swift */, + 9E7FE0E7282E7B7C00C374E8 /* WrappedDatabaseFiles.swift */, ); path = Wrappers; sourceTree = ""; @@ -784,18 +707,9 @@ isa = PBXGroup; children = ( 9E2DF99827CF704D00649636 /* ImportWalletStore.swift */, - 9E2DF99927CF704D00649636 /* Views */, - ); - path = ImportWallet; - sourceTree = ""; - }; - 9E2DF99927CF704D00649636 /* Views */ = { - isa = PBXGroup; - children = ( - 9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */, 9E2DF99B27CF704D00649636 /* ImportWalletView.swift */, ); - path = Views; + path = ImportWallet; sourceTree = ""; }; 9E2F1C8D280EDDEF004E65FE /* Drawer */ = { @@ -828,10 +742,8 @@ 9E5BF64B2823C91200BA3F17 /* TransactionAmount */ = { isa = PBXGroup; children = ( - 2EB7758627FC67FD00269373 /* TransactionAmountInputStore.swift */, + 2EB7758627FC67FD00269373 /* TransactionAmountTextFieldStore.swift */, 2E8719CA27FB09990082C926 /* TransactionAmountTextField.swift */, - 2E6CF8DC27D78319004DCD7A /* CurrencySelectionStore.swift */, - 2E8719CC27FB0D3B0082C926 /* TransactionCurrencySelector.swift */, ); path = TransactionAmount; sourceTree = ""; @@ -839,12 +751,140 @@ 9E5BF64C2823E84300BA3F17 /* TransactionAddress */ = { isa = PBXGroup; children = ( - 9E5BF64E2823E94900BA3F17 /* TransactionAddressInputStore.swift */, + 9E5BF64E2823E94900BA3F17 /* TransactionAddressTextFieldStore.swift */, 9E5BF64D2823E94900BA3F17 /* TransactionAddressTextField.swift */, ); path = TransactionAddress; sourceTree = ""; }; + 9E7FE0B6282D1D9800C374E8 /* Resources */ = { + isa = PBXGroup; + children = ( + 0D4E7A0C26B364180058B01E /* Assets.xcassets */, + 660558E8270C7A54009D6954 /* Colors.xcassets */, + 9E37A2B727C8F59F00AE57B3 /* Localizable.strings */, + 0D2ACE7E26C2C65E00D62E3C /* Fonts */, + 9E7FE0B7282D1D9800C374E8 /* Generated */, + ); + path = Resources; + sourceTree = ""; + }; + 9E7FE0B7282D1D9800C374E8 /* Generated */ = { + isa = PBXGroup; + children = ( + 660558F5270C862F009D6954 /* Fonts+Generated.swift */, + 660558F6270C862F009D6954 /* XCAssets+Generated.swift */, + ); + path = Generated; + sourceTree = ""; + }; + 9E7FE0BB282D1DC200C374E8 /* Utils */ = { + isa = PBXGroup; + children = ( + F96B41EA273B50520021B49A /* Strings.swift */, + 9E7FE0D4282D281800C374E8 /* Array+Chunked.swift */, + 9E2F1C8128095AFE004E65FE /* Int64+Zcash.swift */, + 0DACFA8027208D940039EEA5 /* UInt+SuperscriptText.swift */, + 0D7CE63327349B5D0020E050 /* View+WhenDraggable.swift */, + 9E7FE0D2282D274E00C374E8 /* Date+Readable.swift */, + 9E7FE0CE282D257400C374E8 /* SDKSynchronizer+SyncStatus.swift */, + 9EDDEA8B28250F9C00B4100C /* Double+Zcash.swift */, + 0DACFA7E27208CE00039EEA5 /* Clamped.swift */, + F9322DBF273B555C00C105B5 /* NavigationLinks.swift */, + F9C165B3274031F600592F76 /* Bindings.swift */, + F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */, + F93673D52742CB840099C6AF /* Previews.swift */, + 0D35CC45277A36E00074316A /* ScrollableWhenScaled.swift */, + 2EDA07A327EDE2A900D6F09B /* DebugFrame.swift */, + 9E2F1C832809B606004E65FE /* DebugMenu.swift */, + ); + path = Utils; + sourceTree = ""; + }; + 9E7FE0BD282D1DE100C374E8 /* Dependencies */ = { + isa = PBXGroup; + children = ( + 9E2AC10227DA28200042AA47 /* WalletStorage.swift */, + 9EAFEB892806F48100199FC9 /* ZCashSDKEnvironment.swift */, + 9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */, + 9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */, + ); + path = Dependencies; + sourceTree = ""; + }; + 9E7FE0BE282D1DFE00C374E8 /* UI Components */ = { + isa = PBXGroup; + children = ( + 0DF2DC5227235E1F00FA31E2 /* Extensions */, + 0DB8AA80271DC7520035BC9D /* DesignGuide.swift */, + 9E7FE0E9282E7CF800C374E8 /* ImportSeedEditor */, + 9E2F1C8D280EDDEF004E65FE /* Drawer */, + 2E35F99027B28E6800EB79CD /* TextFields */, + 0D0781C5278776B90083ACD7 /* Shapes */, + 0D8A43C2272AEEA7005A6414 /* FontStyles */, + 669FDAE7272C239D007B9422 /* CircularFrame */, + 0D535FE0271F945C009A9E3E /* Chips */, + 663FAB9A271D873300E495F8 /* Buttons */, + 669FDAE5272C2371007B9422 /* ProgressIndicators */, + 669FDAE6272C2380007B9422 /* Backgrounds */, + 9E7FE0EA282E7D1A00C374E8 /* ZcashBadge */, + ); + path = "UI Components"; + sourceTree = ""; + }; + 9E7FE0E3282E751A00C374E8 /* RecoveryPhraseValidationFlow */ = { + isa = PBXGroup; + children = ( + 0DFE93E2272CA1AA000FCCA5 /* RecoveryPhraseValidationFlowStore.swift */, + 9EBEF87927CE369800B4F343 /* RecoveryPhraseValidationFlowView.swift */, + 0D3D04062728B2EC0032ABC1 /* Views */, + ); + path = RecoveryPhraseValidationFlow; + sourceTree = ""; + }; + 9E7FE0E4282E753700C374E8 /* RecoveryPhraseDisplay */ = { + isa = PBXGroup; + children = ( + 0D3D04092728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift */, + 0D3D04072728B3440032ABC1 /* RecoveryPhraseDisplayView.swift */, + ); + path = RecoveryPhraseDisplay; + sourceTree = ""; + }; + 9E7FE0E9282E7CF800C374E8 /* ImportSeedEditor */ = { + isa = PBXGroup; + children = ( + 9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */, + ); + path = ImportSeedEditor; + sourceTree = ""; + }; + 9E7FE0EA282E7D1A00C374E8 /* ZcashBadge */ = { + isa = PBXGroup; + children = ( + 9E7FE0DE282D2DD600C374E8 /* ZcashBadge.swift */, + ); + path = ZcashBadge; + sourceTree = ""; + }; + 9E7FE0EF282E7FA900C374E8 /* CurrencySelection */ = { + isa = PBXGroup; + children = ( + 2E6CF8DC27D78319004DCD7A /* CurrencySelectionStore.swift */, + 2E8719CC27FB0D3B0082C926 /* CurrencySelectionView.swift */, + ); + path = CurrencySelection; + sourceTree = ""; + }; + 9E7FE0F0282E80C100C374E8 /* TCATextField */ = { + isa = PBXGroup; + children = ( + 2EB1C5E727D77F6100BC43D7 /* TCATextFieldStore.swift */, + 2EDA079F27EDE18C00D6F09B /* TCATextField.swift */, + ); + path = TCATextField; + sourceTree = ""; + }; 9EAFEB802805791400199FC9 /* AppReducerTests */ = { isa = PBXGroup; children = ( @@ -857,25 +897,9 @@ isa = PBXGroup; children = ( 9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */, - 9EAFEB8C2808183D00199FC9 /* Views */, - ); - path = Sandbox; - sourceTree = ""; - }; - 9EAFEB8C2808183D00199FC9 /* Views */ = { - isa = PBXGroup; - children = ( 9EAFEB8D2808183D00199FC9 /* SandboxView.swift */, ); - path = Views; - sourceTree = ""; - }; - 9EBEF87827CE365D00B4F343 /* Preamble */ = { - isa = PBXGroup; - children = ( - 9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */, - ); - path = Preamble; + path = Sandbox; sourceTree = ""; }; 9EF8135927ECC25E0075AF48 /* UtilTests */ = { @@ -893,34 +917,25 @@ isa = PBXGroup; children = ( F93874ED273C4DE200F0E875 /* HomeStore.swift */, - F93874EE273C4DE200F0E875 /* Views */, + F93874EF273C4DE200F0E875 /* HomeView.swift */, ); path = Home; sourceTree = ""; }; - F93874EE273C4DE200F0E875 /* Views */ = { + F96B41E2273B501F0021B49A /* TransactionHistoryFlow */ = { isa = PBXGroup; children = ( - F93874EF273C4DE200F0E875 /* HomeView.swift */, - ); - path = Views; - sourceTree = ""; - }; - F96B41E2273B501F0021B49A /* TransactionHistory */ = { - isa = PBXGroup; - children = ( - F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */, - 9E5BF63B2818305D00BA3F17 /* TransactionState.swift */, + F96B41E3273B501F0021B49A /* TransactionHistoryFlowStore.swift */, + F96B41E6273B501F0021B49A /* TransactionHistoryFlowView.swift */, F96B41E4273B501F0021B49A /* Views */, ); - path = TransactionHistory; + path = TransactionHistoryFlow; sourceTree = ""; }; F96B41E4273B501F0021B49A /* Views */ = { isa = PBXGroup; children = ( F96B41E5273B501F0021B49A /* TransactionDetailView.swift */, - F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */, ); path = Views; sourceTree = ""; @@ -928,119 +943,70 @@ F9971A4927680DC400A2DB75 /* App */ = { isa = PBXGroup; children = ( - F9971A4A27680DC400A2DB75 /* App.swift */, - 9EF8135F27F043CC0075AF48 /* AppDelegate.swift */, - F9971A4B27680DC400A2DB75 /* Views */, - ); - path = App; - sourceTree = ""; - }; - F9971A4B27680DC400A2DB75 /* Views */ = { - isa = PBXGroup; - children = ( + F9971A4A27680DC400A2DB75 /* AppStore.swift */, F9971A4C27680DC400A2DB75 /* AppView.swift */, ); - path = Views; + path = App; sourceTree = ""; }; F9971A4F27680DD000A2DB75 /* Profile */ = { isa = PBXGroup; children = ( F9971A5027680DD000A2DB75 /* ProfileStore.swift */, - F9971A5127680DD000A2DB75 /* Views */, - ); - path = Profile; - sourceTree = ""; - }; - F9971A5127680DD000A2DB75 /* Views */ = { - isa = PBXGroup; - children = ( F9971A5227680DD000A2DB75 /* ProfileView.swift */, ); - path = Views; + path = Profile; sourceTree = ""; }; F9971A5527680DDE00A2DB75 /* Request */ = { isa = PBXGroup; children = ( F9971A5627680DDE00A2DB75 /* RequestStore.swift */, - F9971A5727680DDE00A2DB75 /* Views */, - ); - path = Request; - sourceTree = ""; - }; - F9971A5727680DDE00A2DB75 /* Views */ = { - isa = PBXGroup; - children = ( F9971A5827680DDE00A2DB75 /* RequestView.swift */, ); - path = Views; + path = Request; sourceTree = ""; }; F9971A5B27680DF600A2DB75 /* Scan */ = { isa = PBXGroup; children = ( F9971A5E27680DF600A2DB75 /* ScanStore.swift */, - F9971A5C27680DF600A2DB75 /* Views */, - ); - path = Scan; - sourceTree = ""; - }; - F9971A5C27680DF600A2DB75 /* Views */ = { - isa = PBXGroup; - children = ( F9971A5D27680DF600A2DB75 /* ScanView.swift */, ); - path = Views; + path = Scan; sourceTree = ""; }; F9971A6127680DFE00A2DB75 /* Settings */ = { isa = PBXGroup; children = ( - F9971A6227680DFE00A2DB75 /* Settings.swift */, - F9971A6327680DFE00A2DB75 /* Views */, - ); - path = Settings; - sourceTree = ""; - }; - F9971A6327680DFE00A2DB75 /* Views */ = { - isa = PBXGroup; - children = ( + F9971A6227680DFE00A2DB75 /* SettingsStore.swift */, F9971A6427680DFE00A2DB75 /* SettingsView.swift */, ); - path = Views; + path = Settings; sourceTree = ""; }; F9971A6727680E1000A2DB75 /* WalletInfo */ = { isa = PBXGroup; children = ( - F9971A6827680E1000A2DB75 /* WalletInfo.swift */, - F9971A6927680E1000A2DB75 /* Views */, + F9971A6827680E1000A2DB75 /* WalletInfoStore.swift */, + F9971A6A27680E1000A2DB75 /* WalletInfoView.swift */, ); path = WalletInfo; sourceTree = ""; }; - F9971A6927680E1000A2DB75 /* Views */ = { + F9C165B62740403600592F76 /* SendFlow */ = { isa = PBXGroup; children = ( - F9971A6A27680E1000A2DB75 /* WalletInfoView.swift */, - ); - path = Views; - sourceTree = ""; - }; - F9C165B62740403600592F76 /* Send */ = { - isa = PBXGroup; - children = ( - F9C165B72740403600592F76 /* SendStore.swift */, + F9C165B72740403600592F76 /* SendFlowStore.swift */, + F9C165CA2741AB5D00592F76 /* SendFlowView.swift */, F9C165B82740403600592F76 /* Views */, ); - path = Send; + path = SendFlow; sourceTree = ""; }; F9C165B82740403600592F76 /* Views */ = { isa = PBXGroup; children = ( - F9C165CA2741AB5D00592F76 /* SendView.swift */, F9C165BB2740403600592F76 /* CreateTransactionView.swift */, F9C165B92740403600592F76 /* TransactionConfirmationView.swift */, F9C165BD2740403600592F76 /* TransactionSentView.swift */, @@ -1213,15 +1179,15 @@ ); inputPaths = ( "$(SRCROOT)/swiftlint.yml", - "$(SRCROOT)/secant/Colors.xcassets", - "$(SRCROOT)/secant/Assets/xcassets", + "$(SRCROOT)/secant/Resources/Colors.xcassets", + "$(SRCROOT)/secant/Resources/Assets/xcassets", ); name = SwiftGen; outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Generated/XCAssets+Generated.swift", - "$(DERIVED_FILE_DIR)/Generated/Fonts+Generated.swift", + "$(DERIVED_FILE_DIR)/Resources/Generated/XCAssets+Generated.swift", + "$(DERIVED_FILE_DIR)/Resources/Generated/Fonts+Generated.swift", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1252,13 +1218,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2EB660E02747EAB900A06A07 /* OnboardingScreen.swift in Sources */, + 2EB660E02747EAB900A06A07 /* OnboardingFlowView.swift in Sources */, + 9E7FE0DF282D2DD600C374E8 /* ZcashBadge.swift in Sources */, 660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */, 9EAFEB902808183D00199FC9 /* SandboxStore.swift in Sources */, 0D35CC46277A36E00074316A /* ScrollableWhenScaled.swift in Sources */, - F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */, - 2EDA07A027EDE18C00D6F09B /* TextFieldInput.swift in Sources */, - 2EB7758727FC67FD00269373 /* TransactionAmountInputStore.swift in Sources */, + F96B41E9273B501F0021B49A /* TransactionHistoryFlowView.swift in Sources */, + 2EDA07A027EDE18C00D6F09B /* TCATextField.swift in Sources */, + 9E7FE0DB282D28F100C374E8 /* WrappedPasteboard.swift in Sources */, + 2EB7758727FC67FD00269373 /* TransactionAmountTextFieldStore.swift in Sources */, 669FDAE9272C23B3007B9422 /* CircularFrame.swift in Sources */, 9EF8136027F043CC0075AF48 /* AppDelegate.swift in Sources */, 9E80B47227E4B34B008FF493 /* UserPreferencesStorage.swift in Sources */, @@ -1266,39 +1234,43 @@ 9E2F1C8228095AFE004E65FE /* Int64+Zcash.swift in Sources */, 9E02B56A27FED43E005B809B /* WrappedFileManager.swift in Sources */, 663FABA2271D876C00E495F8 /* SecondaryButton.swift in Sources */, - 0DC487C32772574C00BE6A63 /* ValidationSucceededView.swift in Sources */, - 2EB1C5E827D77F6100BC43D7 /* TextFieldStore.swift in Sources */, + 0DC487C32772574C00BE6A63 /* RecoveryPhraseBackupSucceededView.swift in Sources */, + 2EB1C5E827D77F6100BC43D7 /* TCATextFieldStore.swift in Sources */, 9EAFEB8A2806F48100199FC9 /* ZCashSDKEnvironment.swift in Sources */, 9E5BF648282277BE00BA3F17 /* WrappedNotificationCenter.swift in Sources */, 0D8A43C4272AEEDE005A6414 /* SecantTextStyles.swift in Sources */, 9E5BF641281FD7B600BA3F17 /* TransactionFailedView.swift in Sources */, + 9E7FE0CF282D257400C374E8 /* SDKSynchronizer+SyncStatus.swift in Sources */, 9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */, 0DACFA7F27208CE00039EEA5 /* Clamped.swift in Sources */, - 0DFE93E3272CA1AA000FCCA5 /* RecoveryPhraseValidation.swift in Sources */, + 0DFE93E3272CA1AA000FCCA5 /* RecoveryPhraseValidationFlowStore.swift in Sources */, 9E2DF99E27CF704D00649636 /* ImportWalletView.swift in Sources */, 0D535FE2271F9476009A9E3E /* EnumeratedChip.swift in Sources */, - 6654C73E2715A41300901167 /* OnboardingStore.swift in Sources */, + 9E7FE0E8282E7B7C00C374E8 /* WrappedDatabaseFiles.swift in Sources */, + 6654C73E2715A41300901167 /* OnboardingFlowStore.swift in Sources */, + 9E7FE0D9282D289B00C374E8 /* WrappedFeedbackGenerator.swift in Sources */, 2E6CF8DD27D78319004DCD7A /* CurrencySelectionStore.swift in Sources */, - 9EBEF87A27CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift in Sources */, + 9EBEF87A27CE369800B4F343 /* RecoveryPhraseValidationFlowView.swift in Sources */, 9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */, - 0DDB6A5127737D4A0012A410 /* ValidationFailedView.swift in Sources */, + 0DDB6A5127737D4A0012A410 /* RecoveryPhraseBackupFailedView.swift in Sources */, 0D6D628B276A528E002FB4CC /* DropDelegate.swift in Sources */, 9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */, F9971A5327680DD000A2DB75 /* ProfileStore.swift in Sources */, 669FDAEB272C23C2007B9422 /* CircularFrameBadge.swift in Sources */, - 2E8719CD27FB0D3B0082C926 /* TransactionCurrencySelector.swift in Sources */, + 2E8719CD27FB0D3B0082C926 /* CurrencySelectionView.swift in Sources */, F9971A6C27680E1000A2DB75 /* WalletInfoView.swift in Sources */, - 9E5BF6502823E94900BA3F17 /* TransactionAddressInputStore.swift in Sources */, + 9E5BF6502823E94900BA3F17 /* TransactionAddressTextFieldStore.swift in Sources */, F9EEB8162742C2210032EEB8 /* WithStateBinding.swift in Sources */, + 9E7FE0D3282D274E00C374E8 /* Date+Readable.swift in Sources */, F93673D62742CB840099C6AF /* Previews.swift in Sources */, - 0D5D16F526E24CCF00AD33D1 /* AppError.swift in Sources */, 0D18581B272728D60046B928 /* PhraseChip.swift in Sources */, 0DF482BA2787ADA800EB37D6 /* ConditionalModifier.swift in Sources */, + 9E7FE0EC282E7D9400C374E8 /* TransactionState.swift in Sources */, 9E2F1C8F280EDE09004E65FE /* Drawer.swift in Sources */, 665C963F272C26E600BC04FB /* CircularFrameBackground.swift in Sources */, 9EAFEB882806E5AE00199FC9 /* WrappedSDKSynchronizer.swift in Sources */, 0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */, - F9971A4D27680DC400A2DB75 /* App.swift in Sources */, + F9971A4D27680DC400A2DB75 /* AppStore.swift in Sources */, 9EAFEB9228081E9400199FC9 /* HomeView.swift in Sources */, F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */, 9EAFEB8F2808183D00199FC9 /* SandboxView.swift in Sources */, @@ -1310,15 +1282,17 @@ 66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */, 2EDA07A427EDE2A900D6F09B /* DebugFrame.swift in Sources */, 0D3D040A2728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift in Sources */, - 9E2AC10127D8EF0B0042AA47 /* MnemonicSeedPhraseProvider.swift in Sources */, - 0D354A0926D5A9D000315F45 /* Services.swift in Sources */, + 9E2AC10127D8EF0B0042AA47 /* WrappedMnemonic.swift in Sources */, + 9E7FE0D7282D286500C374E8 /* RecoveryPhrase.swift in Sources */, 660558F7270C862F009D6954 /* Fonts+Generated.swift in Sources */, - F96B41E7273B501F0021B49A /* TransactionHistoryStore.swift in Sources */, + F96B41E7273B501F0021B49A /* TransactionHistoryFlowStore.swift in Sources */, + 9E7FE0E6282E7B1100C374E8 /* StoredWallet.swift in Sources */, 9EAFEB9128081E9400199FC9 /* HomeStore.swift in Sources */, F9971A5A27680DDE00A2DB75 /* RequestView.swift in Sources */, + 9E7FE0D5282D281800C374E8 /* Array+Chunked.swift in Sources */, 0DACFA8127208D940039EEA5 /* UInt+SuperscriptText.swift in Sources */, 0DF2DC51272344E400FA31E2 /* EmptyChip.swift in Sources */, - F9C165BF2740403600592F76 /* SendStore.swift in Sources */, + F9C165BF2740403600592F76 /* SendFlowStore.swift in Sources */, 0D4E7A0926B364170058B01E /* SecantApp.swift in Sources */, 66DC733F271D88CC0053CBB6 /* StandardButtonStyle.swift in Sources */, 663FABA0271D876200E495F8 /* PrimaryButton.swift in Sources */, @@ -1332,9 +1306,10 @@ 9EDDEA8C28250F9C00B4100C /* Double+Zcash.swift in Sources */, 9ECAE56827FC713C0089A0EF /* DatabaseFiles.swift in Sources */, 9E5BF6462821028C00BA3F17 /* WrappedUserDefaults.swift in Sources */, - F9971A6B27680E1000A2DB75 /* WalletInfo.swift in Sources */, + F9971A6B27680E1000A2DB75 /* WalletInfoStore.swift in Sources */, 0D185819272723FF0046B928 /* ColoredChip.swift in Sources */, 2EA11F5D27467F7700709571 /* OnboardingContentView.swift in Sources */, + 9E7FE0EE282E7E8700C374E8 /* SendFlowTransaction.swift in Sources */, 2E58E73B274679F000B2B84B /* OnboardingHeaderView.swift in Sources */, 9E5BF64F2823E94900BA3F17 /* TransactionAddressTextField.swift in Sources */, 2E35F99227B28E7600EB79CD /* SingleLineTextField.swift in Sources */, @@ -1347,20 +1322,19 @@ 9E2DF99C27CF704D00649636 /* ImportWalletStore.swift in Sources */, F9971A6627680DFE00A2DB75 /* SettingsView.swift in Sources */, F96B41EB273B50520021B49A /* Strings.swift in Sources */, - 9E5BF63C2818305D00BA3F17 /* TransactionState.swift in Sources */, 2EDA07A227EDE1AE00D6F09B /* TextFieldFooter.swift in Sources */, F9971A5427680DD000A2DB75 /* ProfileView.swift in Sources */, F9971A6027680DF600A2DB75 /* ScanStore.swift in Sources */, - 9EF8139127F191BF0075AF48 /* WalletStorageInteractor.swift in Sources */, - 0DFE93E1272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift in Sources */, - 9E69A24D27FB002800A55317 /* Welcome.swift in Sources */, - F9C165CB2741AB5D00592F76 /* SendView.swift in Sources */, + 9EF8139127F191BF0075AF48 /* WrappedWalletStorage.swift in Sources */, + 0DFE93E1272C9ECB000FCCA5 /* RecoveryPhraseBackupView.swift in Sources */, + 9E69A24D27FB002800A55317 /* WelcomeStore.swift in Sources */, + F9C165CB2741AB5D00592F76 /* SendFlowView.swift in Sources */, + 9E7FE0DD282D298900C374E8 /* ValidationWord.swift in Sources */, 0D0781C4278750E30083ACD7 /* WelcomeView.swift in Sources */, - F9971A6527680DFE00A2DB75 /* Settings.swift in Sources */, + F9971A6527680DFE00A2DB75 /* SettingsStore.swift in Sources */, 9EF8139C27F47AED0075AF48 /* InitializationState.swift in Sources */, 0D0781C9278776D20083ACD7 /* ZcashSymbol.swift in Sources */, 2E8719CB27FB09990082C926 /* TransactionAmountTextField.swift in Sources */, - 6654C7412715A47300901167 /* Onboarding.swift in Sources */, F9C165C42740403600592F76 /* TransactionSentView.swift in Sources */, F9971A5927680DDE00A2DB75 /* RequestStore.swift in Sources */, ); diff --git a/secant/AppErrors/AppError.swift b/secant/AppErrors/AppError.swift deleted file mode 100644 index a0b8e79..0000000 --- a/secant/AppErrors/AppError.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// AppError.swift -// secant-testnet -// -// Created by Francisco Gindre on 9/3/21. -// - -import Foundation - -enum AppError: Error, Equatable { - case failedToInitialize -} diff --git a/secant/Util/DatabaseFiles.swift b/secant/Dependencies/DatabaseFiles.swift similarity index 58% rename from secant/Util/DatabaseFiles.swift rename to secant/Dependencies/DatabaseFiles.swift index 3877103..ff6924e 100644 --- a/secant/Util/DatabaseFiles.swift +++ b/secant/Dependencies/DatabaseFiles.swift @@ -116,72 +116,3 @@ struct DatabaseFiles { } } } - -struct DatabaseFilesInteractor { - let documentsDirectory: () throws -> URL - let cacheDbURLFor: (ZcashNetwork) throws -> URL - let dataDbURLFor: (ZcashNetwork) throws -> URL - let outputParamsURLFor: (ZcashNetwork) throws -> URL - let pendingDbURLFor: (ZcashNetwork) throws -> URL - let spendParamsURLFor: (ZcashNetwork) throws -> URL - let areDbFilesPresentFor: (ZcashNetwork) throws -> Bool - let nukeDbFilesFor: (ZcashNetwork) throws -> Void -} - -extension DatabaseFilesInteractor { - static func live(databaseFiles: DatabaseFiles = DatabaseFiles(fileManager: .live)) -> Self { - Self( - documentsDirectory: { - try databaseFiles.documentsDirectory() - }, - cacheDbURLFor: { network in - try databaseFiles.cacheDbURL(for: network) - }, - dataDbURLFor: { network in - try databaseFiles.dataDbURL(for: network) - }, - outputParamsURLFor: { network in - try databaseFiles.outputParamsURL(for: network) - }, - pendingDbURLFor: { network in - try databaseFiles.pendingDbURL(for: network) - }, - spendParamsURLFor: { network in - try databaseFiles.spendParamsURL(for: network) - }, - areDbFilesPresentFor: { network in - try databaseFiles.areDbFilesPresent(for: network) - }, - nukeDbFilesFor: { network in - try databaseFiles.nukeDbFiles(for: network) - } - ) - } - - static var throwing = DatabaseFilesInteractor( - documentsDirectory: { - throw DatabaseFiles.DatabaseFilesError.getDocumentsURL - }, - cacheDbURLFor: { _ in - throw DatabaseFiles.DatabaseFilesError.getCacheURL - }, - dataDbURLFor: { _ in - throw DatabaseFiles.DatabaseFilesError.getDataURL - }, - outputParamsURLFor: { _ in - throw DatabaseFiles.DatabaseFilesError.getOutputParamsURL - }, - pendingDbURLFor: { _ in - throw DatabaseFiles.DatabaseFilesError.getPendingURL - }, - spendParamsURLFor: { _ in - throw DatabaseFiles.DatabaseFilesError.getSpendParamsURL - }, - areDbFilesPresentFor: { _ in - throw DatabaseFiles.DatabaseFilesError.filesPresentCheck - }, - nukeDbFilesFor: { _ in - throw DatabaseFiles.DatabaseFilesError.nukeFiles - } - ) -} diff --git a/secant/Util/UserPreferencesStorage.swift b/secant/Dependencies/UserPreferencesStorage.swift similarity index 100% rename from secant/Util/UserPreferencesStorage.swift rename to secant/Dependencies/UserPreferencesStorage.swift diff --git a/secant/Util/WalletStorage.swift b/secant/Dependencies/WalletStorage.swift similarity index 100% rename from secant/Util/WalletStorage.swift rename to secant/Dependencies/WalletStorage.swift diff --git a/secant/Util/ZCashSDKEnvironment.swift b/secant/Dependencies/ZCashSDKEnvironment.swift similarity index 100% rename from secant/Util/ZCashSDKEnvironment.swift rename to secant/Dependencies/ZCashSDKEnvironment.swift diff --git a/secant/Features/App/App.swift b/secant/Features/App/AppStore.swift similarity index 89% rename from secant/Features/App/App.swift rename to secant/Features/App/AppStore.swift index 1f84d8c..15182af 100644 --- a/secant/Features/App/App.swift +++ b/secant/Features/App/AppStore.swift @@ -1,6 +1,12 @@ import ComposableArchitecture import ZcashLightClientKit +typealias AppReducer = Reducer +typealias AppStore = Store +typealias AppViewStore = ViewStore + +// MARK: - State + struct AppState: Equatable { enum Route: Equatable { case welcome @@ -14,8 +20,8 @@ struct AppState: Equatable { var appInitializationState: InitializationState = .uninitialized var homeState: HomeState - var onboardingState: OnboardingState - var phraseValidationState: RecoveryPhraseValidationState + var onboardingState: OnboardingFlowState + var phraseValidationState: RecoveryPhraseValidationFlowState var phraseDisplayState: RecoveryPhraseDisplayState var route: Route = .welcome var sandboxState: SandboxState @@ -23,6 +29,8 @@ struct AppState: Equatable { var welcomeState: WelcomeState } +// MARK: - Action + enum AppAction: Equatable { case appDelegate(AppDelegateAction) case checkBackupPhraseValidation @@ -31,54 +39,54 @@ enum AppAction: Equatable { case home(HomeAction) case initializeSDK case nukeWallet - case onboarding(OnboardingAction) + case onboarding(OnboardingFlowAction) case phraseDisplay(RecoveryPhraseDisplayAction) - case phraseValidation(RecoveryPhraseValidationAction) + case phraseValidation(RecoveryPhraseValidationFlowAction) case respondToWalletInitializationState(InitializationState) case sandbox(SandboxAction) case updateRoute(AppState.Route) case welcome(WelcomeAction) } +// MARK: - Environment + struct AppEnvironment { - let wrappedSDKSynchronizer: WrappedSDKSynchronizer - let databaseFiles: DatabaseFilesInteractor - let mnemonicSeedPhraseProvider: MnemonicSeedPhraseProvider + let SDKSynchronizer: WrappedSDKSynchronizer + let databaseFiles: WrappedDatabaseFiles + let mnemonicSeedPhraseProvider: WrappedMnemonic let scheduler: AnySchedulerOf - let walletStorage: WalletStorageInteractor - let wrappedDerivationTool: WrappedDerivationTool + let walletStorage: WrappedWalletStorage + let derivationTool: WrappedDerivationTool let zcashSDKEnvironment: ZCashSDKEnvironment } extension AppEnvironment { static let live = AppEnvironment( - wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer(), + SDKSynchronizer: LiveWrappedSDKSynchronizer(), databaseFiles: .live(), mnemonicSeedPhraseProvider: .live, scheduler: DispatchQueue.main.eraseToAnyScheduler(), walletStorage: .live(), - wrappedDerivationTool: .live(), + derivationTool: .live(), zcashSDKEnvironment: .mainnet ) static let mock = AppEnvironment( - wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer(), + SDKSynchronizer: LiveWrappedSDKSynchronizer(), databaseFiles: .live(), mnemonicSeedPhraseProvider: .mock, scheduler: DispatchQueue.main.eraseToAnyScheduler(), walletStorage: .live(), - wrappedDerivationTool: .live(derivationTool: DerivationTool(networkType: .mainnet)), + derivationTool: .live(derivationTool: DerivationTool(networkType: .mainnet)), zcashSDKEnvironment: .mainnet ) } -// MARK: - AppReducer - -private struct ListenerId: Hashable {} - -typealias AppReducer = Reducer +// MARK: - Reducer extension AppReducer { + private struct ListenerId: Hashable {} + static let `default` = AppReducer.combine( [ appReducer, @@ -153,8 +161,8 @@ extension AppReducer { birthday: birthday, with: environment ) - try environment.wrappedSDKSynchronizer.prepareWith(initializer: initializer) - try environment.wrappedSDKSynchronizer.start() + try environment.SDKSynchronizer.prepareWith(initializer: initializer) + try environment.SDKSynchronizer.start() } catch { state.appInitializationState = .failed // TODO: error we need to handle, issue #221 (https://github.com/zcash/secant-ios-wallet/issues/221) @@ -176,7 +184,7 @@ extension AppReducer { let recoveryPhrase = RecoveryPhrase(words: phraseWords) state.phraseDisplayState.phrase = recoveryPhrase - state.phraseValidationState = RecoveryPhraseValidationState.random(phrase: recoveryPhrase) + state.phraseValidationState = RecoveryPhraseValidationFlowState.random(phrase: recoveryPhrase) landingRoute = .phraseDisplay } catch { // TODO: - merge with issue 201 (https://github.com/zcash/secant-ios-wallet/issues/201) and its Error States @@ -203,7 +211,7 @@ extension AppReducer { let randomPhraseWords = try environment.mnemonicSeedPhraseProvider.asWords(randomPhrase) let recoveryPhrase = RecoveryPhrase(words: randomPhraseWords) state.phraseDisplayState.phrase = recoveryPhrase - state.phraseValidationState = RecoveryPhraseValidationState.random(phrase: recoveryPhrase) + state.phraseValidationState = RecoveryPhraseValidationFlowState.random(phrase: recoveryPhrase) return .concatenate( Effect(value: .initializeSDK), @@ -294,17 +302,17 @@ extension AppReducer { mnemonicSeedPhraseProvider: environment.mnemonicSeedPhraseProvider, scheduler: environment.scheduler, walletStorage: environment.walletStorage, - wrappedDerivationTool: environment.wrappedDerivationTool, - wrappedSDKSynchronizer: environment.wrappedSDKSynchronizer + derivationTool: environment.derivationTool, + SDKSynchronizer: environment.SDKSynchronizer ) } ) - private static let onboardingReducer: AppReducer = OnboardingReducer.default.pullback( + private static let onboardingReducer: AppReducer = OnboardingFlowReducer.default.pullback( state: \AppState.onboardingState, action: /AppAction.onboarding, environment: { environment in - OnboardingEnvironment( + OnboardingFlowEnvironment( mnemonicSeedPhraseProvider: environment.mnemonicSeedPhraseProvider, walletStorage: environment.walletStorage, zcashSDKEnvironment: environment.zcashSDKEnvironment @@ -312,33 +320,31 @@ extension AppReducer { } ) - private static let phraseValidationReducer: AppReducer = RecoveryPhraseValidationReducer.default.pullback( + private static let phraseValidationReducer: AppReducer = RecoveryPhraseValidationFlowReducer.default.pullback( state: \AppState.phraseValidationState, action: /AppAction.phraseValidation, - environment: { _ in BackupPhraseEnvironment.demo } + environment: { _ in RecoveryPhraseValidationFlowEnvironment.demo } ) private static let phraseDisplayReducer: AppReducer = RecoveryPhraseDisplayReducer.default.pullback( state: \AppState.phraseDisplayState, action: /AppAction.phraseDisplay, - environment: { _ in BackupPhraseEnvironment.demo } + environment: { _ in RecoveryPhraseDisplayEnvironment.demo } ) private static let sandboxReducer: AppReducer = SandboxReducer.default.pullback( state: \AppState.sandboxState, action: /AppAction.sandbox, - environment: { _ in } + environment: { _ in SandboxEnvironment() } ) private static let welcomeReducer: AppReducer = WelcomeReducer.default.pullback( state: \AppState.welcomeState, action: /AppAction.welcome, - environment: { _ in } + environment: { _ in WelcomeEnvironment() } ) } -// MARK: - AppReducer Helper Functions - extension AppReducer { static func walletInitializationState(_ environment: AppEnvironment) -> InitializationState { var keysPresent = false @@ -386,7 +392,7 @@ extension AppReducer { ) throws -> Initializer { do { let seedBytes = try environment.mnemonicSeedPhraseProvider.toSeed(seedPhrase) - let viewingKeys = try environment.wrappedDerivationTool.deriveUnifiedViewingKeysFromSeed(seedBytes, 1) + let viewingKeys = try environment.derivationTool.deriveUnifiedViewingKeysFromSeed(seedBytes, 1) let network = environment.zcashSDKEnvironment.network @@ -409,9 +415,24 @@ extension AppReducer { } } -// MARK: - AppStore +// MARK: Placeholders -typealias AppStore = Store +extension AppState { + static var placeholder: Self { + .init( + homeState: .placeholder, + onboardingState: .init( + importWalletState: .placeholder + ), + phraseValidationState: RecoveryPhraseValidationFlowState.placeholder, + phraseDisplayState: RecoveryPhraseDisplayState( + phrase: .placeholder + ), + sandboxState: .placeholder, + welcomeState: .placeholder + ) + } +} extension AppStore { static var placeholder: AppStore { @@ -422,29 +443,3 @@ extension AppStore { ) } } - -// MARK: - AppViewStore - -typealias AppViewStore = ViewStore - -extension AppViewStore { -} - -// MARK: PlaceHolders - -extension AppState { - static var placeholder: Self { - .init( - homeState: .placeholder, - onboardingState: .init( - importWalletState: .placeholder - ), - phraseValidationState: RecoveryPhraseValidationState.placeholder, - phraseDisplayState: RecoveryPhraseDisplayState( - phrase: .placeholder - ), - sandboxState: .placeholder, - welcomeState: .placeholder - ) - } -} diff --git a/secant/Features/App/Views/AppView.swift b/secant/Features/App/AppView.swift similarity index 71% rename from secant/Features/App/Views/AppView.swift rename to secant/Features/App/AppView.swift index 0faa469..d10bf6a 100644 --- a/secant/Features/App/Views/AppView.swift +++ b/secant/Features/App/AppView.swift @@ -43,13 +43,13 @@ struct AppView: View { case .startup: ZStack(alignment: .topTrailing) { - StartupView(sendAction: viewStore.send) + DebugView(sendAction: viewStore.send) .transition(.opacity) } case .phraseValidation: NavigationView { - RecoveryPhraseTestPreambleView( + RecoveryPhraseValidationFlowView( store: store.scope( state: \.phraseValidationState, action: AppAction.phraseValidation @@ -80,37 +80,41 @@ struct AppView: View { } } -private struct StartupView: View { - var sendAction: (AppAction) -> Void - - var body: some View { - List { - Section(header: Text("Navigation Stack Routes")) { - Button("Go To Sandbox (navigation proof)") { - sendAction(.updateRoute(.sandbox)) - } - - Button("Go To Onboarding") { - sendAction(.updateRoute(.onboarding)) - } - - Button("Go To Phrase Validation Demo") { - sendAction(.updateRoute(.phraseValidation)) - } - - Button("Restart the app") { - sendAction(.updateRoute(.welcome)) - } - - Button("[Be careful] Nuke Wallet") { - sendAction(.nukeWallet) +private extension AppView { + struct DebugView: View { + var sendAction: (AppAction) -> Void + + var body: some View { + List { + Section(header: Text("Navigation Stack Routes")) { + Button("Go To Sandbox (navigation proof)") { + sendAction(.updateRoute(.sandbox)) + } + + Button("Go To Onboarding") { + sendAction(.updateRoute(.onboarding)) + } + + Button("Go To Phrase Validation Demo") { + sendAction(.updateRoute(.phraseValidation)) + } + + Button("Restart the app") { + sendAction(.updateRoute(.welcome)) + } + + Button("[Be careful] Nuke Wallet") { + sendAction(.nukeWallet) + } } } + .navigationBarTitle("Startup") } - .navigationBarTitle("Startup") } } +// MARK: - Previews + struct AppView_Previews: PreviewProvider { static var previews: some View { NavigationView { diff --git a/secant/Features/Home/HomeStore.swift b/secant/Features/Home/HomeStore.swift index 7b1d636..2167552 100644 --- a/secant/Features/Home/HomeStore.swift +++ b/secant/Features/Home/HomeStore.swift @@ -2,6 +2,12 @@ import ComposableArchitecture import SwiftUI import ZcashLightClientKit +typealias HomeReducer = Reducer +typealias HomeStore = Store +typealias HomeViewStore = ViewStore + +// MARK: State + struct HomeState: Equatable { enum Route: Equatable { case profile @@ -15,24 +21,26 @@ struct HomeState: Equatable { var drawerOverlay: DrawerOverlay var profileState: ProfileState var requestState: RequestState - var sendState: SendState + var sendState: SendFlowState var scanState: ScanState var synchronizerStatus: String var totalBalance: Int64 - var transactionHistoryState: TransactionHistoryState + var transactionHistoryState: TransactionHistoryFlowState var verifiedBalance: Int64 } +// MARK: Action + enum HomeAction: Equatable { case debugMenuStartup case onAppear case onDisappear case profile(ProfileAction) case request(RequestAction) - case send(SendAction) + case send(SendFlowAction) case scan(ScanAction) case synchronizerStateChanged(WrappedSDKSynchronizerState) - case transactionHistory(TransactionHistoryAction) + case transactionHistory(TransactionHistoryFlowAction) case updateBalance(Balance) case updateDrawer(DrawerOverlay) case updateRoute(HomeState.Route?) @@ -40,21 +48,21 @@ enum HomeAction: Equatable { case updateTransactions([TransactionState]) } +// MARK: Environment + struct HomeEnvironment { - let mnemonicSeedPhraseProvider: MnemonicSeedPhraseProvider + let mnemonicSeedPhraseProvider: WrappedMnemonic let scheduler: AnySchedulerOf - let walletStorage: WalletStorageInteractor - let wrappedDerivationTool: WrappedDerivationTool - let wrappedSDKSynchronizer: WrappedSDKSynchronizer + let walletStorage: WrappedWalletStorage + let derivationTool: WrappedDerivationTool + let SDKSynchronizer: WrappedSDKSynchronizer } -// MARK: - HomeReducer - -private struct ListenerId: Hashable {} - -typealias HomeReducer = Reducer +// MARK: - Reducer extension HomeReducer { + private struct ListenerId: Hashable {} + static let `default` = HomeReducer.combine( [ homeReducer, @@ -67,7 +75,7 @@ extension HomeReducer { private static let homeReducer = HomeReducer { state, action, environment in switch action { case .onAppear: - return environment.wrappedSDKSynchronizer.stateChanged + return environment.SDKSynchronizer.stateChanged .map(HomeAction.synchronizerStateChanged) .eraseToEffect() .cancellable(id: ListenerId(), cancelInFlight: true) @@ -77,12 +85,12 @@ extension HomeReducer { case .synchronizerStateChanged(.synced): return .merge( - environment.wrappedSDKSynchronizer.getAllClearedTransactions() + environment.SDKSynchronizer.getAllClearedTransactions() .receive(on: environment.scheduler) .map(HomeAction.updateTransactions) .eraseToEffect(), - environment.wrappedSDKSynchronizer.getShieldedBalance() + environment.SDKSynchronizer.getShieldedBalance() .receive(on: environment.scheduler) .map({ Balance(verified: $0.verified, total: $0.total) }) .map(HomeAction.updateBalance) @@ -108,7 +116,7 @@ extension HomeReducer { return .none case .updateSynchronizerStatus: - state.synchronizerStatus = environment.wrappedSDKSynchronizer.status() + state.synchronizerStatus = environment.SDKSynchronizer.status() return .none case .updateRoute(let route): @@ -144,60 +152,36 @@ extension HomeReducer { } } - private static let historyReducer: HomeReducer = TransactionHistoryReducer.default.pullback( + private static let historyReducer: HomeReducer = TransactionHistoryFlowReducer.default.pullback( state: \HomeState.transactionHistoryState, action: /HomeAction.transactionHistory, environment: { environment in - TransactionHistoryEnvironment( + TransactionHistoryFlowEnvironment( scheduler: environment.scheduler, - wrappedSDKSynchronizer: environment.wrappedSDKSynchronizer + SDKSynchronizer: environment.SDKSynchronizer ) } ) - private static let sendReducer: HomeReducer = SendReducer.default.pullback( + private static let sendReducer: HomeReducer = SendFlowReducer.default.pullback( state: \HomeState.sendState, action: /HomeAction.send, environment: { environment in - SendEnvironment( + SendFlowEnvironment( mnemonicSeedPhraseProvider: environment.mnemonicSeedPhraseProvider, scheduler: environment.scheduler, walletStorage: environment.walletStorage, - wrappedDerivationTool: environment.wrappedDerivationTool, - wrappedSDKSynchronizer: environment.wrappedSDKSynchronizer + derivationTool: environment.derivationTool, + SDKSynchronizer: environment.SDKSynchronizer ) } ) } -// MARK: - HomeViewStore - -typealias HomeViewStore = ViewStore - -extension HomeViewStore { - func bindingForRoute(_ route: HomeState.Route) -> Binding { - self.binding( - get: { $0.route == route }, - send: { isActive in - return .updateRoute(isActive ? route : nil) - } - ) - } - - func bindingForDrawer() -> Binding { - self.binding( - get: { $0.drawerOverlay }, - send: { .updateDrawer($0) } - ) - } -} - -// MARK: - HomeStore - -typealias HomeStore = Store +// MARK: - Store extension HomeStore { - func historyStore() -> TransactionHistoryStore { + func historyStore() -> TransactionHistoryFlowStore { self.scope( state: \.transactionHistoryState, action: HomeAction.transactionHistory @@ -218,7 +202,7 @@ extension HomeStore { ) } - func sendStore() -> SendStore { + func sendStore() -> SendFlowStore { self.scope( state: \.sendState, action: HomeAction.send @@ -233,7 +217,27 @@ extension HomeStore { } } -// MARK: PlaceHolders +// MARK: - ViewStore + +extension HomeViewStore { + func bindingForRoute(_ route: HomeState.Route) -> Binding { + self.binding( + get: { $0.route == route }, + send: { isActive in + return .updateRoute(isActive ? route : nil) + } + ) + } + + func bindingForDrawer() -> Binding { + self.binding( + get: { $0.drawerOverlay }, + send: { .updateDrawer($0) } + ) + } +} + +// MARK: Placeholders extension HomeState { static var placeholder: Self { @@ -251,38 +255,18 @@ extension HomeState { } } -extension SDKSynchronizer { - static func textFor(state: SyncStatus) -> String { - switch state { - case .downloading(let progress): - return "Downloading \(progress.progressHeight)/\(progress.targetHeight)" - - case .enhancing(let enhanceProgress): - return "Enhancing tx \(enhanceProgress.enhancedTransactions) of \(enhanceProgress.totalTransactions)" - - case .fetching: - return "fetching UTXOs" - - case .scanning(let scanProgress): - return "Scanning: \(scanProgress.progressHeight)/\(scanProgress.targetHeight)" - - case .disconnected: - return "disconnected 💔" - - case .stopped: - return "Stopped 🚫" - - case .synced: - return "Synced 😎" - - case .unprepared: - return "Unprepared 😅" - - case .validating: - return "Validating" - - case .error(let err): - return "Error: \(err.localizedDescription)" - } +extension HomeStore { + static var placeholder: HomeStore { + HomeStore( + initialState: .placeholder, + reducer: .default.debug(), + environment: HomeEnvironment( + mnemonicSeedPhraseProvider: .live, + scheduler: DispatchQueue.main.eraseToAnyScheduler(), + walletStorage: .live(), + derivationTool: .live(), + SDKSynchronizer: LiveWrappedSDKSynchronizer() + ) + ) } } diff --git a/secant/Features/Home/Views/HomeView.swift b/secant/Features/Home/HomeView.swift similarity index 86% rename from secant/Features/Home/Views/HomeView.swift rename to secant/Features/Home/HomeView.swift index b5bb27a..cace642 100644 --- a/secant/Features/Home/Views/HomeView.swift +++ b/secant/Features/Home/HomeView.swift @@ -30,7 +30,7 @@ struct HomeView: View { if proxy.size.height > 0 { Drawer(overlay: viewStore.bindingForDrawer(), maxHeight: proxy.size.height) { VStack { - TransactionHistoryView(store: store.historyStore()) + TransactionHistoryFlowView(store: store.historyStore()) .padding(.top, 10) Spacer() @@ -93,7 +93,7 @@ extension HomeView { .navigationLink( isActive: viewStore.bindingForRoute(.send), destination: { - SendView(store: store.sendStore()) + SendFlowView(store: store.sendStore()) } ) @@ -126,22 +126,6 @@ extension HomeView { // MARK: - Previews -extension HomeStore { - static var placeholder: HomeStore { - HomeStore( - initialState: .placeholder, - reducer: .default.debug(), - environment: HomeEnvironment( - mnemonicSeedPhraseProvider: .live, - scheduler: DispatchQueue.main.eraseToAnyScheduler(), - walletStorage: .live(), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer() - ) - ) - } -} - struct HomeView_Previews: PreviewProvider { static var previews: some View { NavigationView { diff --git a/secant/Features/ImportWallet/ImportWalletStore.swift b/secant/Features/ImportWallet/ImportWalletStore.swift index 040ff03..e844994 100644 --- a/secant/Features/ImportWallet/ImportWalletStore.swift +++ b/secant/Features/ImportWallet/ImportWalletStore.swift @@ -8,13 +8,19 @@ import ComposableArchitecture import ZcashLightClientKit +typealias ImportWalletReducer = Reducer typealias ImportWalletStore = Store +typealias ImportWalletViewStore = ViewStore + +// MARK: - State struct ImportWalletState: Equatable { @BindableState var alert: AlertState? @BindableState var importedSeedPhrase: String = "" } +// MARK: - Action + enum ImportWalletAction: Equatable, BindableAction { case binding(BindingAction) case dismissAlert @@ -24,9 +30,11 @@ enum ImportWalletAction: Equatable, BindableAction { case successfullyRecovered } +// MARK: - Environment + struct ImportWalletEnvironment { - let mnemonicSeedPhraseProvider: MnemonicSeedPhraseProvider - let walletStorage: WalletStorageInteractor + let mnemonicSeedPhraseProvider: WrappedMnemonic + let walletStorage: WrappedWalletStorage let zcashSDKEnvironment: ZCashSDKEnvironment } @@ -44,7 +52,7 @@ extension ImportWalletEnvironment { ) } -typealias ImportWalletReducer = Reducer +// MARK: - Reducer extension ImportWalletReducer { static let `default` = ImportWalletReducer { state, action, environment in @@ -106,3 +114,19 @@ extension ImportWalletReducer { } .binding() } + +// MARK: - Placeholders + +extension ImportWalletState { + static let placeholder = ImportWalletState(importedSeedPhrase: "") + + static let live = ImportWalletState(importedSeedPhrase: "") +} + +extension ImportWalletStore { + static let demo = Store( + initialState: .placeholder, + reducer: .default, + environment: .demo + ) +} diff --git a/secant/Features/ImportWallet/Views/ImportWalletView.swift b/secant/Features/ImportWallet/ImportWalletView.swift similarity index 90% rename from secant/Features/ImportWallet/Views/ImportWalletView.swift rename to secant/Features/ImportWallet/ImportWalletView.swift index 9a31da3..afd6e06 100644 --- a/secant/Features/ImportWallet/Views/ImportWalletView.swift +++ b/secant/Features/ImportWallet/ImportWalletView.swift @@ -81,19 +81,7 @@ extension View { } } -extension ImportWalletStore { - static let demo = Store( - initialState: .placeholder, - reducer: .default, - environment: .demo - ) -} - -extension ImportWalletState { - static let placeholder = ImportWalletState(importedSeedPhrase: "") - - static let live = ImportWalletState(importedSeedPhrase: "") -} +// MARK: - Previews struct ImportWalletView_Previews: PreviewProvider { static var previews: some View { diff --git a/secant/Features/Onboarding/Views/Onboarding.swift b/secant/Features/Onboarding/Views/Onboarding.swift deleted file mode 100644 index 4a6b74e..0000000 --- a/secant/Features/Onboarding/Views/Onboarding.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// Onboarding.swift -// secant-testnet -// -// Created by Adam Stener on 10/12/21. -// - -import SwiftUI -import ComposableArchitecture - -struct OnboardingView: View { - let store: Store - - var body: some View { - WithViewStore(self.store) { viewStore in - VStack(spacing: 50) { - HStack(spacing: 50) { - Button("Back") { viewStore.send(.back) } - .disabled(viewStore.isInitialStep) - - Spacer() - Button("Next") { viewStore.send(.next) } - - Button("Skip") { viewStore.send(.skip) } - .disabled(viewStore.isFinalStep) - } - .frame(height: 100) - .padding(.horizontal, 50) - - Spacer() - - Text(viewStore.currentStep.title) - .frame(maxWidth: .infinity) - .offset(y: viewStore.offset) - .animation(.easeOut(duration: 0.4)) - - Spacer() - - VStack { - Text(viewStore.currentStep.description) - - ProgressView( - "Progress \(viewStore.progress)%", - value: Double(viewStore.index + 1), - total: Double(viewStore.steps.count) - ) - .padding(.horizontal, 25) - .padding(.vertical, 50) - } - .animation(.easeOut(duration: 0.2)) - } - } - } -} - -extension OnboardingState { - static let onboardingSteps = IdentifiedArray( - uniqueElements: [ - Step( - id: UUID(), - title: "onboarding.step1.title", - description: "onboarding.step1.description", - background: Asset.Assets.Backgrounds.callout1.image, - badge: .shield - ), - Step( - id: UUID(), - title: "onboarding.step2.title", - description: "onboarding.step2.description", - background: Asset.Assets.Backgrounds.callout2.image, - badge: .person - ), - Step( - id: UUID(), - title: "onboarding.step3.title", - description: "onboarding.step3.description", - background: Asset.Assets.Backgrounds.callout3.image, - badge: .list - ), - Step( - id: UUID(), - title: "onboarding.step4.title", - description: "onboarding.step4.description", - background: Asset.Assets.Backgrounds.callout4.image, - badge: .shield - ) - ] - ) -} - -struct Onboarding_Previews: PreviewProvider { - static var previews: some View { - Group { - OnboardingView( - store: Store( - initialState: OnboardingState( - importWalletState: .placeholder - ), - reducer: .default, - environment: (.demo) - ) - ) - } - } -} diff --git a/secant/Features/Onboarding/OnboardingStore.swift b/secant/Features/OnboardingFlow/OnboardingFlowStore.swift similarity index 56% rename from secant/Features/Onboarding/OnboardingStore.swift rename to secant/Features/OnboardingFlow/OnboardingFlowStore.swift index 9389f6d..0392bf7 100644 --- a/secant/Features/Onboarding/OnboardingStore.swift +++ b/secant/Features/OnboardingFlow/OnboardingFlowStore.swift @@ -9,9 +9,13 @@ import Foundation import SwiftUI import ComposableArchitecture -typealias OnboardingViewStore = ViewStore +typealias OnboardingFlowReducer = Reducer +typealias OnboardingFlowStore = Store +typealias OnboardingFlowViewStore = ViewStore -struct OnboardingState: Equatable { +// MARK: - State + +struct OnboardingFlowState: Equatable { enum Route: Equatable, CaseIterable { case createNewWallet case importExistingWallet @@ -45,58 +49,86 @@ struct OnboardingState: Equatable { var importWalletState: ImportWalletState } -extension OnboardingViewStore { - func bindingForRoute(_ route: OnboardingState.Route) -> Binding { - self.binding( - get: { $0.route == route }, - send: { isActive in - return .updateRoute(isActive ? route : nil) - } - ) - } +extension OnboardingFlowState { + static let onboardingSteps = IdentifiedArray( + uniqueElements: [ + Step( + id: UUID(), + title: "onboarding.step1.title", + description: "onboarding.step1.description", + background: Asset.Assets.Backgrounds.callout1.image, + badge: .shield + ), + Step( + id: UUID(), + title: "onboarding.step2.title", + description: "onboarding.step2.description", + background: Asset.Assets.Backgrounds.callout2.image, + badge: .person + ), + Step( + id: UUID(), + title: "onboarding.step3.title", + description: "onboarding.step3.description", + background: Asset.Assets.Backgrounds.callout3.image, + badge: .list + ), + Step( + id: UUID(), + title: "onboarding.step4.title", + description: "onboarding.step4.description", + background: Asset.Assets.Backgrounds.callout4.image, + badge: .shield + ) + ] + ) } -enum OnboardingAction: Equatable { +// MARK: - Action + +enum OnboardingFlowAction: Equatable { case next case back case skip - case updateRoute(OnboardingState.Route?) + case updateRoute(OnboardingFlowState.Route?) case createNewWallet case importExistingWallet case importWallet(ImportWalletAction) } -struct OnboardingEnvironment { - let mnemonicSeedPhraseProvider: MnemonicSeedPhraseProvider - let walletStorage: WalletStorageInteractor +// MARK: - Environment + +struct OnboardingFlowEnvironment { + let mnemonicSeedPhraseProvider: WrappedMnemonic + let walletStorage: WrappedWalletStorage let zcashSDKEnvironment: ZCashSDKEnvironment } -extension OnboardingEnvironment { - static let live = OnboardingEnvironment( +extension OnboardingFlowEnvironment { + static let live = OnboardingFlowEnvironment( mnemonicSeedPhraseProvider: .live, walletStorage: .live(), zcashSDKEnvironment: .mainnet ) - static let demo = OnboardingEnvironment( + static let demo = OnboardingFlowEnvironment( mnemonicSeedPhraseProvider: .mock, walletStorage: .live(), zcashSDKEnvironment: .testnet ) } -typealias OnboardingReducer = Reducer +// MARK: - Reducer -extension OnboardingReducer { - static let `default` = OnboardingReducer.combine( +extension OnboardingFlowReducer { + static let `default` = OnboardingFlowReducer.combine( [ onboardingReducer, importWalletReducer ] ) - private static let onboardingReducer = OnboardingReducer { state, action, _ in + private static let onboardingReducer = OnboardingFlowReducer { state, action, _ in switch action { case .back: guard state.index > 0 else { return .none } @@ -136,9 +168,9 @@ extension OnboardingReducer { } } - private static let importWalletReducer: OnboardingReducer = ImportWalletReducer.default.pullback( - state: \OnboardingState.importWalletState, - action: /OnboardingAction.importWallet, + private static let importWalletReducer: OnboardingFlowReducer = ImportWalletReducer.default.pullback( + state: \OnboardingFlowState.importWalletState, + action: /OnboardingFlowAction.importWallet, environment: { environment in ImportWalletEnvironment( mnemonicSeedPhraseProvider: environment.mnemonicSeedPhraseProvider, @@ -148,3 +180,16 @@ extension OnboardingReducer { } ) } + +// MARK: - ViewStore + +extension OnboardingFlowViewStore { + func bindingForRoute(_ route: OnboardingFlowState.Route) -> Binding { + self.binding( + get: { $0.route == route }, + send: { isActive in + return .updateRoute(isActive ? route : nil) + } + ) + } +} diff --git a/secant/Screens/Onboarding/OnboardingScreen.swift b/secant/Features/OnboardingFlow/OnboardingFlowView.swift similarity index 81% rename from secant/Screens/Onboarding/OnboardingScreen.swift rename to secant/Features/OnboardingFlow/OnboardingFlowView.swift index d7c6cee..8aea634 100644 --- a/secant/Screens/Onboarding/OnboardingScreen.swift +++ b/secant/Features/OnboardingFlow/OnboardingFlowView.swift @@ -9,7 +9,7 @@ import SwiftUI import ComposableArchitecture struct OnboardingScreen: View { - let store: Store + let store: Store var body: some View { GeometryReader { proxy in @@ -46,14 +46,16 @@ struct OnboardingScreen: View { } } +// MARK: - Previews + struct OnboardingScreen_Previews: PreviewProvider { static var previews: some View { OnboardingScreen( store: Store( - initialState: OnboardingState( + initialState: OnboardingFlowState( importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: (.demo) ) ) @@ -62,10 +64,10 @@ struct OnboardingScreen_Previews: PreviewProvider { OnboardingScreen( store: Store( - initialState: OnboardingState( + initialState: OnboardingFlowState( importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: (.demo) ) ) @@ -74,10 +76,10 @@ struct OnboardingScreen_Previews: PreviewProvider { OnboardingScreen( store: Store( - initialState: OnboardingState( + initialState: OnboardingFlowState( importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: (.demo) ) ) @@ -86,10 +88,10 @@ struct OnboardingScreen_Previews: PreviewProvider { OnboardingScreen( store: Store( - initialState: OnboardingState( + initialState: OnboardingFlowState( importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: (.demo) ) ) @@ -98,10 +100,10 @@ struct OnboardingScreen_Previews: PreviewProvider { OnboardingScreen( store: Store( - initialState: OnboardingState( + initialState: OnboardingFlowState( importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: (.demo) ) ) @@ -110,10 +112,10 @@ struct OnboardingScreen_Previews: PreviewProvider { OnboardingScreen( store: Store( - initialState: OnboardingState( + initialState: OnboardingFlowState( importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: (.demo) ) ) diff --git a/secant/Screens/Onboarding/OnboardingContentView.swift b/secant/Features/OnboardingFlow/Views/OnboardingContentView.swift similarity index 95% rename from secant/Screens/Onboarding/OnboardingContentView.swift rename to secant/Features/OnboardingFlow/Views/OnboardingContentView.swift index 05afa1d..dc94174 100644 --- a/secant/Screens/Onboarding/OnboardingContentView.swift +++ b/secant/Features/OnboardingFlow/Views/OnboardingContentView.swift @@ -9,7 +9,7 @@ import SwiftUI import ComposableArchitecture struct OnboardingContentView: View { - let store: Store + let store: Store let width: Double let height: Double @@ -127,11 +127,11 @@ extension OnboardingContentView { struct OnboardingContentView_Previews: PreviewProvider { static var previews: some View { let store = Store( - initialState: OnboardingState( + initialState: OnboardingFlowState( index: 0, importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: (.demo) ) @@ -146,8 +146,10 @@ struct OnboardingContentView_Previews: PreviewProvider { } } +// MARK: - Previews + extension OnboardingContentView_Previews { - static func example(_ store: Store) -> some View { + static func example(_ store: Store) -> some View { GeometryReader { proxy in ZStack { OnboardingHeaderView( diff --git a/secant/Screens/Onboarding/OnboardingFooterView.swift b/secant/Features/OnboardingFlow/Views/OnboardingFooterView.swift similarity index 91% rename from secant/Screens/Onboarding/OnboardingFooterView.swift rename to secant/Features/OnboardingFlow/Views/OnboardingFooterView.swift index b5d86c8..4b4c409 100644 --- a/secant/Screens/Onboarding/OnboardingFooterView.swift +++ b/secant/Features/OnboardingFlow/Views/OnboardingFooterView.swift @@ -9,7 +9,7 @@ import SwiftUI import ComposableArchitecture struct OnboardingFooterView: View { - let store: Store + let store: Store let animationDuration: CGFloat = 0.8 var body: some View { @@ -58,7 +58,7 @@ struct OnboardingFooterView: View { ImportWalletView( store: store.scope( state: \.importWalletState, - action: OnboardingAction.importWallet + action: OnboardingFlowAction.importWallet ) ) } @@ -83,14 +83,16 @@ extension View { } } +// MARK: - Previews + struct OnboardingFooterView_Previews: PreviewProvider { static var previews: some View { - let store = Store( - initialState: OnboardingState( + let store = Store( + initialState: OnboardingFlowState( index: 3, importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: (.demo) ) diff --git a/secant/Screens/Onboarding/OnboardingHeaderView.swift b/secant/Features/OnboardingFlow/Views/OnboardingHeaderView.swift similarity index 93% rename from secant/Screens/Onboarding/OnboardingHeaderView.swift rename to secant/Features/OnboardingFlow/Views/OnboardingHeaderView.swift index 9d5d94c..672692c 100644 --- a/secant/Screens/Onboarding/OnboardingHeaderView.swift +++ b/secant/Features/OnboardingFlow/Views/OnboardingHeaderView.swift @@ -60,14 +60,16 @@ struct OnboardingHeaderView: View { } } +// MARK: - Previews + struct OnboardingHeaderView_Previews: PreviewProvider { static var previews: some View { - let store = Store( - initialState: OnboardingState( + let store = Store( + initialState: OnboardingFlowState( index: 0, importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: (.demo) ) diff --git a/secant/Features/Profile/ProfileStore.swift b/secant/Features/Profile/ProfileStore.swift index 170e671..066f3d1 100644 --- a/secant/Features/Profile/ProfileStore.swift +++ b/secant/Features/Profile/ProfileStore.swift @@ -1,6 +1,12 @@ import ComposableArchitecture import SwiftUI +typealias ProfileReducer = Reducer +typealias ProfileStore = Store +typealias ProfileViewStore = ViewStore + +// MARK: - State + struct ProfileState: Equatable { enum Route { case settings @@ -12,16 +18,17 @@ struct ProfileState: Equatable { var route: Route? } +// MARK: - Action + enum ProfileAction: Equatable { case updateRoute(ProfileState.Route?) } -struct ProfileEnvironment { -} +// MARK: - Environment -// MARK: - ProfileReducer +struct ProfileEnvironment { } -typealias ProfileReducer = Reducer +// MARK: - Reducer extension ProfileReducer { static let `default` = ProfileReducer { state, action, _ in @@ -33,16 +40,7 @@ extension ProfileReducer { } } -// MARK: - ProfileStore - -typealias ProfileStore = Store - -extension ProfileStore { -} - -// MARK: - ProfileViewStore - -typealias ProfileViewStore = ViewStore +// MARK: - ViewStore extension ProfileViewStore { var routeBinding: Binding { @@ -67,7 +65,7 @@ extension ProfileViewStore { } } -// MARK: PlaceHolders +// MARK: Placeholders extension ProfileState { static var placeholder: Self { diff --git a/secant/Features/Profile/Views/ProfileView.swift b/secant/Features/Profile/ProfileView.swift similarity index 98% rename from secant/Features/Profile/Views/ProfileView.swift rename to secant/Features/Profile/ProfileView.swift index a0d573e..4dfc86f 100644 --- a/secant/Features/Profile/Views/ProfileView.swift +++ b/secant/Features/Profile/ProfileView.swift @@ -28,6 +28,8 @@ struct ProfileView: View { } } +// MARK: - Previews + struct ProfileView_Previews: PreviewProvider { static var previews: some View { NavigationView { diff --git a/secant/Features/BackupFlow/RecoveryPhraseDisplayStore.swift b/secant/Features/RecoveryPhraseDisplay/RecoveryPhraseDisplayStore.swift similarity index 52% rename from secant/Features/BackupFlow/RecoveryPhraseDisplayStore.swift rename to secant/Features/RecoveryPhraseDisplay/RecoveryPhraseDisplayStore.swift index c465c91..9c556db 100644 --- a/secant/Features/BackupFlow/RecoveryPhraseDisplayStore.swift +++ b/secant/Features/RecoveryPhraseDisplay/RecoveryPhraseDisplayStore.swift @@ -7,57 +7,37 @@ import Foundation import ComposableArchitecture -import UIKit -enum RecoveryPhraseError: Error { - /// This error is thrown then the Recovery Phrase can't be generated - case unableToGeneratePhrase +typealias RecoveryPhraseDisplayReducer = Reducer +typealias RecoveryPhraseDisplayStore = Store +typealias RecoveryPhraseDisplayViewStore = ViewStore + +// MARK: - State + +struct RecoveryPhraseDisplayState: Equatable { + var phrase: RecoveryPhrase? + var showCopyToBufferAlert = false } -struct FeedbackGenerator { - let generateFeedback: () -> Void +// MARK: - Action + +enum RecoveryPhraseDisplayAction: Equatable { + case createPhrase + case copyToBufferPressed + case finishedPressed + case phraseResponse(Result) } -extension FeedbackGenerator { - static let haptic = FeedbackGenerator( - generateFeedback: { UINotificationFeedbackGenerator().notificationOccurred(.error) } - ) - - static let silent = FeedbackGenerator( - generateFeedback: { } - ) -} +// MARK: - Environment -struct Pasteboard { - let setString: (String) -> Void - let getString: () -> String? -} - -extension Pasteboard { - private struct TestPasteboard { - static var general = TestPasteboard() - var string: String? - } - - static let live = Pasteboard( - setString: { UIPasteboard.general.string = $0 }, - getString: { UIPasteboard.general.string } - ) - - static let test = Pasteboard( - setString: { TestPasteboard.general.string = $0 }, - getString: { TestPasteboard.general.string } - ) -} - -struct BackupPhraseEnvironment { +struct RecoveryPhraseDisplayEnvironment { let mainQueue: AnySchedulerOf let newPhrase: () -> Effect - let pasteboard: Pasteboard - let feedbackGenerator: FeedbackGenerator + let pasteboard: WrappedPasteboard + let feedbackGenerator: WrappedFeedbackGenerator } -extension BackupPhraseEnvironment { +extension RecoveryPhraseDisplayEnvironment { private struct DemoPasteboard { static var general = Self() var string: String? @@ -78,51 +58,7 @@ extension BackupPhraseEnvironment { ) } -typealias RecoveryPhraseDisplayStore = Store - -struct RecoveryPhrase: Equatable { - struct Group: Hashable { - var startIndex: Int - var words: [String] - } - - let words: [String] - - private let groupSize = 6 - - func toGroups() -> [Group] { - let chunks = words.count / groupSize - return zip(0 ..< chunks, words.chunked(into: groupSize)).map { - Group(startIndex: $0 * groupSize + 1, words: $1) - } - } - - func toString() -> String { - words.joined(separator: " ") - } - - func words(fromMissingIndices indices: [Int]) -> [PhraseChip.Kind] { - assert((indices.count - 1) * groupSize <= self.words.count) - - return indices.enumerated().map { index, position in - .unassigned(word: self.words[(index * groupSize) + position]) - } - } -} - -struct RecoveryPhraseDisplayState: Equatable { - var phrase: RecoveryPhrase? - var showCopyToBufferAlert = false -} - -enum RecoveryPhraseDisplayAction: Equatable { - case createPhrase - case copyToBufferPressed - case finishedPressed - case phraseResponse(Result) -} - -typealias RecoveryPhraseDisplayReducer = Reducer +// MARK: - Reducer extension RecoveryPhraseDisplayReducer { static let `default` = RecoveryPhraseDisplayReducer { state, action, environment in @@ -148,11 +84,3 @@ extension RecoveryPhraseDisplayReducer { } } } - -extension Array { - func chunked(into size: Int) -> [[Element]] { - return stride(from: 0, to: count, by: size).map { - Array(self[$0 ..< Swift.min($0 + size, count)]) - } - } -} diff --git a/secant/Features/BackupFlow/Views/RecoveryPhraseDisplayView.swift b/secant/Features/RecoveryPhraseDisplay/RecoveryPhraseDisplayView.swift similarity index 100% rename from secant/Features/BackupFlow/Views/RecoveryPhraseDisplayView.swift rename to secant/Features/RecoveryPhraseDisplay/RecoveryPhraseDisplayView.swift diff --git a/secant/Features/BackupFlow/RecoveryPhraseValidation.swift b/secant/Features/RecoveryPhraseValidationFlow/RecoveryPhraseValidationFlowStore.swift similarity index 55% rename from secant/Features/BackupFlow/RecoveryPhraseValidation.swift rename to secant/Features/RecoveryPhraseValidationFlow/RecoveryPhraseValidationFlowStore.swift index ef2831b..2233e35 100644 --- a/secant/Features/BackupFlow/RecoveryPhraseValidation.swift +++ b/secant/Features/RecoveryPhraseValidationFlow/RecoveryPhraseValidationFlowStore.swift @@ -9,19 +9,17 @@ import Foundation import ComposableArchitecture import SwiftUI -typealias RecoveryPhraseValidationStore = Store -typealias RecoveryPhraseValidationViewStore = ViewStore - -/// Represents the data of a word that has been placed into an empty position, that will be used -/// to validate the completed phrase when all ValidationWords have been placed. -struct ValidationWord: Equatable { - var groupIndex: Int - var word: String -} +typealias RecoveryPhraseValidationFlowReducer = Reducer< + RecoveryPhraseValidationFlowState, + RecoveryPhraseValidationFlowAction, + RecoveryPhraseValidationFlowEnvironment +> +typealias RecoveryPhraseValidationFlowStore = Store +typealias RecoveryPhraseValidationFlowViewStore = ViewStore // MARK: - State -struct RecoveryPhraseValidationState: Equatable { +struct RecoveryPhraseValidationFlowState: Equatable { enum Route: Equatable, CaseIterable { case validation case success @@ -47,14 +45,14 @@ struct RecoveryPhraseValidationState: Equatable { } } -extension RecoveryPhraseValidationState { +extension RecoveryPhraseValidationFlowState { /// 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. static func random(phrase: RecoveryPhrase) -> Self { let missingIndices = Self.randomIndices() let missingWordChipKind = phrase.words(fromMissingIndices: missingIndices).shuffled() - return RecoveryPhraseValidationState( + return RecoveryPhraseValidationFlowState( phrase: phrase, missingIndices: missingIndices, missingWordChips: missingWordChipKind, @@ -63,7 +61,7 @@ extension RecoveryPhraseValidationState { } } -extension RecoveryPhraseValidationState { +extension RecoveryPhraseValidationFlowState { /// Given an array of RecoveryPhraseStepCompletion, missing indices, original phrase and the number of groups it was split into, /// assembly the resulting phrase. This comes up with the "proposed solution" for the recovery phrase validation challenge. /// - returns:an array of String containing the recovery phrase words ordered by the original phrase order, or `nil` @@ -121,8 +119,8 @@ extension RecoveryPhrase.Group { // MARK: - Action -enum RecoveryPhraseValidationAction: Equatable { - case updateRoute(RecoveryPhraseValidationState.Route?) +enum RecoveryPhraseValidationFlowAction: Equatable { + case updateRoute(RecoveryPhraseValidationFlowState.Route?) case reset case move(wordChip: PhraseChip.Kind, intoGroup: Int) case succeed @@ -132,15 +130,43 @@ enum RecoveryPhraseValidationAction: Equatable { case displayBackedUpPhrase } +// MARK: - Environment + +struct RecoveryPhraseValidationFlowEnvironment { + let mainQueue: AnySchedulerOf + let newPhrase: () -> Effect + let pasteboard: WrappedPasteboard + let feedbackGenerator: WrappedFeedbackGenerator +} + +extension RecoveryPhraseValidationFlowEnvironment { + private struct DemoPasteboard { + static var general = Self() + var string: String? + } + + static let demo = Self( + mainQueue: DispatchQueue.main.eraseToAnyScheduler(), + newPhrase: { Effect(value: .init(words: RecoveryPhrase.placeholder.words)) }, + pasteboard: .test, + feedbackGenerator: .silent + ) + + static let live = Self( + mainQueue: DispatchQueue.main.eraseToAnyScheduler(), + newPhrase: { Effect(value: .init(words: RecoveryPhrase.placeholder.words)) }, + pasteboard: .live, + feedbackGenerator: .haptic + ) +} + // MARK: - Reducer -typealias RecoveryPhraseValidationReducer = Reducer - -extension RecoveryPhraseValidationReducer { - static let `default` = RecoveryPhraseValidationReducer { state, action, environment in +extension RecoveryPhraseValidationFlowReducer { + static let `default` = RecoveryPhraseValidationFlowReducer { state, action, environment in switch action { case .reset: - state = RecoveryPhraseValidationState.random(phrase: state.phrase) + state = RecoveryPhraseValidationFlowState.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) @@ -154,8 +180,8 @@ extension RecoveryPhraseValidationReducer { state.validationWords.append(ValidationWord(groupIndex: group, word: word)) if state.isComplete { - let value: RecoveryPhraseValidationAction = state.isValid ? .succeed : .fail - let effect = Effect(value: value) + let value: RecoveryPhraseValidationFlowAction = state.isValid ? .succeed : .fail + let effect = Effect(value: value) .delay(for: 1, scheduler: environment.mainQueue) .eraseToEffect() @@ -181,7 +207,7 @@ extension RecoveryPhraseValidationReducer { case .updateRoute(let route): guard let route = route else { - state = RecoveryPhraseValidationState.random(phrase: state.phrase) + state = RecoveryPhraseValidationFlowState.random(phrase: state.phrase) return .none } state.route = route @@ -198,8 +224,8 @@ extension RecoveryPhraseValidationReducer { // MARK: - ViewStore -extension RecoveryPhraseValidationViewStore { - func bindingForRoute(_ route: RecoveryPhraseValidationState.Route) -> Binding { +extension RecoveryPhraseValidationFlowViewStore { + func bindingForRoute(_ route: RecoveryPhraseValidationFlowState.Route) -> Binding { self.binding( get: { $0.route == route }, send: { isActive in @@ -209,7 +235,7 @@ extension RecoveryPhraseValidationViewStore { } } -extension RecoveryPhraseValidationViewStore { +extension RecoveryPhraseValidationFlowViewStore { var bindingForValidation: Binding { self.binding( get: { $0.route != nil }, @@ -237,3 +263,109 @@ extension RecoveryPhraseValidationViewStore { ) } } + +// MARK: - Placeholders + +extension RecoveryPhraseValidationFlowState { + static let placeholder = RecoveryPhraseValidationFlowState.random(phrase: .placeholder) + + static let placeholderStep1 = RecoveryPhraseValidationFlowState( + phrase: .placeholder, + missingIndices: [2, 0, 3, 5], + missingWordChips: [ + .unassigned(word: "thank"), + .empty, + .unassigned(word: "boil"), + .unassigned(word: "garlic") + ], + validationWords: [ + .init(groupIndex: 2, word: "morning") + ], + route: nil + ) + + static let placeholderStep2 = RecoveryPhraseValidationFlowState( + phrase: .placeholder, + missingIndices: [2, 0, 3, 5], + missingWordChips: [ + .empty, + .empty, + .unassigned(word: "boil"), + .unassigned(word: "garlic") + ], + validationWords: [ + .init(groupIndex: 2, word: "morning"), + .init(groupIndex: 0, word: "thank") + ], + route: nil + ) + + static let placeholderStep3 = RecoveryPhraseValidationFlowState( + phrase: .placeholder, + missingIndices: [2, 0, 3, 5], + missingWordChips: [ + .empty, + .empty, + .unassigned(word: "boil"), + .empty + ], + validationWords: [ + .init(groupIndex: 2, word: "morning"), + .init(groupIndex: 0, word: "thank"), + .init(groupIndex: 3, word: "garlic") + ], + route: nil + ) + + static let placeholderStep4 = RecoveryPhraseValidationFlowState( + phrase: .placeholder, + missingIndices: [2, 0, 3, 5], + missingWordChips: [ + .empty, + .empty, + .empty, + .empty + ], + validationWords: [ + .init(groupIndex: 2, word: "morning"), + .init(groupIndex: 0, word: "thank"), + .init(groupIndex: 3, word: "garlic"), + .init(groupIndex: 1, word: "boil") + ], + route: nil + ) +} + +extension RecoveryPhraseValidationFlowStore { + private static let scheduler = DispatchQueue.main + + static let demo = Store( + initialState: .placeholder, + reducer: .default, + environment: .demo + ) + + static let demoStep1 = Store( + initialState: .placeholderStep1, + reducer: .default, + environment: .demo + ) + + static let demoStep2 = Store( + initialState: .placeholderStep1, + reducer: .default, + environment: .demo + ) + + static let demoStep3 = Store( + initialState: .placeholderStep3, + reducer: .default, + environment: .demo + ) + + static let demoStep4 = Store( + initialState: .placeholderStep4, + reducer: .default, + environment: .demo + ) +} diff --git a/secant/Features/BackupFlow/Preamble/RecoveryPhraseTestPreambleView.swift b/secant/Features/RecoveryPhraseValidationFlow/RecoveryPhraseValidationFlowView.swift similarity index 89% rename from secant/Features/BackupFlow/Preamble/RecoveryPhraseTestPreambleView.swift rename to secant/Features/RecoveryPhraseValidationFlow/RecoveryPhraseValidationFlowView.swift index e311952..6dc1a72 100644 --- a/secant/Features/BackupFlow/Preamble/RecoveryPhraseTestPreambleView.swift +++ b/secant/Features/RecoveryPhraseValidationFlow/RecoveryPhraseValidationFlowView.swift @@ -1,5 +1,5 @@ // -// RecoveryPhraseTestPreambleView.swift +// RecoveryPhraseValidationFlowView.swift // secant-testnet // // Created by Lukáš Korba on 03/01/22. @@ -8,8 +8,8 @@ import SwiftUI import ComposableArchitecture -struct RecoveryPhraseTestPreambleView: View { - var store: RecoveryPhraseValidationStore +struct RecoveryPhraseValidationFlowView: View { + var store: RecoveryPhraseValidationFlowStore var body: some View { WithViewStore(store) { viewStore in @@ -77,7 +77,7 @@ struct RecoveryPhraseTestPreambleView: View { .navigationLinkEmpty( isActive: viewStore.bindingForValidation, destination: { - RecoveryPhraseBackupValidationView(store: store) + RecoveryPhraseBackupView(store: store) } ) } @@ -91,7 +91,7 @@ struct RecoveryPhraseTestPreambleView: View { /// 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 { +extension RecoveryPhraseValidationFlowView { func circularFrameUniformSize(width: CGFloat, height: CGFloat) -> CGFloat { var deviceMultiplier = 1.0 @@ -108,16 +108,16 @@ struct RecoveryPhraseTestPreambleView_Previews: PreviewProvider { static var previews: some View { Group { NavigationView { - RecoveryPhraseTestPreambleView(store: .demo) + RecoveryPhraseValidationFlowView(store: .demo) } - RecoveryPhraseTestPreambleView(store: .demo) + RecoveryPhraseValidationFlowView(store: .demo) .preferredColorScheme(.dark) - RecoveryPhraseTestPreambleView(store: .demo) + RecoveryPhraseValidationFlowView(store: .demo) .previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)")) - RecoveryPhraseTestPreambleView(store: .demo) + RecoveryPhraseValidationFlowView(store: .demo) .environment(\.sizeCategory, .accessibilityLarge) } } diff --git a/secant/Features/BackupFlow/Views/ValidationFailedView.swift b/secant/Features/RecoveryPhraseValidationFlow/Views/RecoveryPhraseBackupFailedView.swift similarity index 88% rename from secant/Features/BackupFlow/Views/ValidationFailedView.swift rename to secant/Features/RecoveryPhraseValidationFlow/Views/RecoveryPhraseBackupFailedView.swift index c236be8..8184c0f 100644 --- a/secant/Features/BackupFlow/Views/ValidationFailedView.swift +++ b/secant/Features/RecoveryPhraseValidationFlow/Views/RecoveryPhraseBackupFailedView.swift @@ -8,10 +8,10 @@ import SwiftUI import ComposableArchitecture -struct ValidationFailedView: View { +struct RecoveryPhraseBackupFailedView: View { @Environment(\.presentationMode) var presentationMode - var store: RecoveryPhraseValidationStore + var store: RecoveryPhraseValidationFlowStore var body: some View { WithViewStore(store) { viewStore in @@ -82,7 +82,7 @@ struct ValidationFailedView: View { /// 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 ValidationFailedView { +extension RecoveryPhraseBackupFailedView { func circularFrameUniformSize(width: CGFloat, height: CGFloat) -> CGFloat { var deviceMultiplier = 1.0 @@ -95,23 +95,25 @@ extension ValidationFailedView { } } -struct ValidationFailed_Previews: PreviewProvider { +// MARK: - Previews + +struct RecoveryPhraseBackupValidationFailedView_Previews: PreviewProvider { static var previews: some View { Group { NavigationView { - ValidationFailedView(store: .demo) + RecoveryPhraseBackupFailedView(store: .demo) } - ValidationFailedView(store: .demo) + RecoveryPhraseBackupFailedView(store: .demo) .preferredColorScheme(.dark) - ValidationFailedView(store: .demo) + RecoveryPhraseBackupFailedView(store: .demo) .previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)")) - ValidationFailedView(store: .demo) + RecoveryPhraseBackupFailedView(store: .demo) .environment(\.sizeCategory, .accessibilityLarge) - ValidationFailedView(store: .demo) + RecoveryPhraseBackupFailedView(store: .demo) .environment(\.sizeCategory, .accessibilityLarge) .previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)")) } diff --git a/secant/Features/BackupFlow/Views/ValidationSucceededView.swift b/secant/Features/RecoveryPhraseValidationFlow/Views/RecoveryPhraseBackupSucceededView.swift similarity index 81% rename from secant/Features/BackupFlow/Views/ValidationSucceededView.swift rename to secant/Features/RecoveryPhraseValidationFlow/Views/RecoveryPhraseBackupSucceededView.swift index 2e56c6e..4df0e93 100644 --- a/secant/Features/BackupFlow/Views/ValidationSucceededView.swift +++ b/secant/Features/RecoveryPhraseValidationFlow/Views/RecoveryPhraseBackupSucceededView.swift @@ -8,8 +8,8 @@ import SwiftUI import ComposableArchitecture -struct ValidationSucceededView: View { - var store: RecoveryPhraseValidationStore +struct RecoveryPhraseBackupSucceededView: View { + var store: RecoveryPhraseValidationFlowStore var body: some View { WithViewStore(store) { viewStore in @@ -53,7 +53,7 @@ struct ValidationSucceededView: View { } ) .activeButtonStyle - .validationSucceededViewLayout() + .recoveryPhraseBackupValidationSucceededViewLayout() Button( action: { @@ -68,7 +68,7 @@ struct ValidationSucceededView: View { } ) .secondaryButtonStyle - .validationSucceededViewLayout() + .recoveryPhraseBackupValidationSucceededViewLayout() } } .padding(.horizontal) @@ -83,7 +83,7 @@ struct ValidationSucceededView: View { /// 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 ValidationSucceededView { +extension RecoveryPhraseBackupSucceededView { func circularFrameUniformSize(width: CGFloat, height: CGFloat) -> CGFloat { var deviceMultiplier = 1.0 @@ -96,8 +96,8 @@ extension ValidationSucceededView { } } -// swiftlint:disable:next private_over_fileprivate strict_fileprivate -fileprivate struct ValidationSucceededViewLayout: ViewModifier { +// swiftlint:disable:next private_over_fileprivate strict_fileprivate type_name +fileprivate struct RecoveryPhraseBackupValidationSucceededViewLayout: ViewModifier { func body(content: Content) -> some View { content .frame( @@ -114,28 +114,31 @@ fileprivate struct ValidationSucceededViewLayout: ViewModifier { } extension View { - func validationSucceededViewLayout() -> some View { - modifier(ValidationSucceededViewLayout()) + func recoveryPhraseBackupValidationSucceededViewLayout() -> some View { + modifier(RecoveryPhraseBackupValidationSucceededViewLayout()) } } -struct ValidationSuccededView_Previews: PreviewProvider { +// MARK: - Previews + +// swiftlint:disable:next type_name +struct RecoveryPhraseBackupValidationSucceededView_Previews: PreviewProvider { static var previews: some View { Group { NavigationView { - ValidationSucceededView(store: .demo) + RecoveryPhraseBackupSucceededView(store: .demo) } - ValidationSucceededView(store: .demo) + RecoveryPhraseBackupSucceededView(store: .demo) .preferredColorScheme(.dark) - ValidationSucceededView(store: .demo) + RecoveryPhraseBackupSucceededView(store: .demo) .previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)")) - ValidationSucceededView(store: .demo) + RecoveryPhraseBackupSucceededView(store: .demo) .environment(\.sizeCategory, .accessibilityLarge) - ValidationSucceededView(store: .demo) + RecoveryPhraseBackupSucceededView(store: .demo) .environment(\.sizeCategory, .accessibilityLarge) .previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)")) } diff --git a/secant/Features/BackupFlow/Views/RecoveryPhraseBackupValidationView.swift b/secant/Features/RecoveryPhraseValidationFlow/Views/RecoveryPhraseBackupView.swift similarity index 58% rename from secant/Features/BackupFlow/Views/RecoveryPhraseBackupValidationView.swift rename to secant/Features/RecoveryPhraseValidationFlow/Views/RecoveryPhraseBackupView.swift index 1a15b88..195da58 100644 --- a/secant/Features/BackupFlow/Views/RecoveryPhraseBackupValidationView.swift +++ b/secant/Features/RecoveryPhraseValidationFlow/Views/RecoveryPhraseBackupView.swift @@ -8,10 +8,10 @@ import SwiftUI import ComposableArchitecture -struct RecoveryPhraseBackupValidationView: View { - let store: RecoveryPhraseValidationStore +struct RecoveryPhraseBackupView: View { + let store: RecoveryPhraseValidationFlowStore - var viewStore: RecoveryPhraseValidationViewStore { + var viewStore: RecoveryPhraseValidationFlowViewStore { ViewStore(store) } @@ -52,11 +52,11 @@ struct RecoveryPhraseBackupValidationView: View { .padding(.top, 0) .navigationLinkEmpty( isActive: viewStore.bindingForSuccess, - destination: { ValidationSucceededView(store: store) } + destination: { RecoveryPhraseBackupSucceededView(store: store) } ) .navigationLinkEmpty( isActive: viewStore.bindingForFailure, - destination: { ValidationFailedView(store: store) } + destination: { RecoveryPhraseBackupFailedView(store: store) } ) } .frame(alignment: .top) @@ -66,8 +66,10 @@ struct RecoveryPhraseBackupValidationView: View { .navigationBarTitleDisplayMode(.inline) .navigationTitle(Text("recoveryPhraseBackupValidation.title")) } +} - @ViewBuilder func header(for viewStore: RecoveryPhraseValidationViewStore) -> some View { +private extension RecoveryPhraseBackupView { + @ViewBuilder func header(for viewStore: RecoveryPhraseValidationFlowViewStore) -> some View { VStack { if viewStore.isComplete { completeHeader(for: viewStore.state) @@ -81,7 +83,7 @@ struct RecoveryPhraseBackupValidationView: View { .padding(.horizontal, 30) } - @ViewBuilder func completeHeader(for state: RecoveryPhraseValidationState) -> some View { + @ViewBuilder func completeHeader(for state: RecoveryPhraseValidationFlowState) -> some View { if state.isValid { Text("recoveryPhraseBackupValidation.successResult") .bodyText() @@ -92,7 +94,7 @@ struct RecoveryPhraseBackupValidationView: View { } } -private extension RecoveryPhraseValidationState { +private extension RecoveryPhraseValidationFlowState { @ViewBuilder func missingWordGrid() -> some View { let columns = Array( repeating: GridItem(.flexible(minimum: 100, maximum: 120), spacing: 20), @@ -114,7 +116,7 @@ private extension RecoveryPhraseValidationState { } } -extension RecoveryPhraseValidationState { +extension RecoveryPhraseValidationFlowState { func wordsChips( for groupIndex: Int, groupSize: Int, @@ -136,120 +138,16 @@ extension RecoveryPhraseValidationState { } } -extension RecoveryPhraseValidationState { - static let placeholder = RecoveryPhraseValidationState.random(phrase: .placeholder) - - static let placeholderStep1 = RecoveryPhraseValidationState( - phrase: .placeholder, - missingIndices: [2, 0, 3, 5], - missingWordChips: [ - .unassigned(word: "thank"), - .empty, - .unassigned(word: "boil"), - .unassigned(word: "garlic") - ], - validationWords: [ - .init(groupIndex: 2, word: "morning") - ], - route: nil - ) - - static let placeholderStep2 = RecoveryPhraseValidationState( - phrase: .placeholder, - missingIndices: [2, 0, 3, 5], - missingWordChips: [ - .empty, - .empty, - .unassigned(word: "boil"), - .unassigned(word: "garlic") - ], - validationWords: [ - .init(groupIndex: 2, word: "morning"), - .init(groupIndex: 0, word: "thank") - ], - route: nil - ) - - static let placeholderStep3 = RecoveryPhraseValidationState( - phrase: .placeholder, - missingIndices: [2, 0, 3, 5], - missingWordChips: [ - .empty, - .empty, - .unassigned(word: "boil"), - .empty - ], - validationWords: [ - .init(groupIndex: 2, word: "morning"), - .init(groupIndex: 0, word: "thank"), - .init(groupIndex: 3, word: "garlic") - ], - route: nil - ) - - static let placeholderStep4 = RecoveryPhraseValidationState( - phrase: .placeholder, - missingIndices: [2, 0, 3, 5], - missingWordChips: [ - .empty, - .empty, - .empty, - .empty - ], - validationWords: [ - .init(groupIndex: 2, word: "morning"), - .init(groupIndex: 0, word: "thank"), - .init(groupIndex: 3, word: "garlic"), - .init(groupIndex: 1, word: "boil") - ], - route: nil - ) -} - -extension RecoveryPhraseValidationStore { - private static let scheduler = DispatchQueue.main - - static let demo = Store( - initialState: .placeholder, - reducer: .default, - environment: .demo - ) - - static let demoStep1 = Store( - initialState: .placeholderStep1, - reducer: .default, - environment: .demo - ) - - static let demoStep2 = Store( - initialState: .placeholderStep1, - reducer: .default, - environment: .demo - ) - - static let demoStep3 = Store( - initialState: .placeholderStep3, - reducer: .default, - environment: .demo - ) - - static let demoStep4 = Store( - initialState: .placeholderStep4, - reducer: .default, - environment: .demo - ) -} - private extension WordChipGrid { init( - state: RecoveryPhraseValidationState, + state: RecoveryPhraseValidationFlowState, groupIndex: Int, wordGroup: RecoveryPhrase.Group, misingIndex: Int ) { let chips = state.wordsChips( for: groupIndex, - groupSize: RecoveryPhraseValidationState.wordGroupSize, + groupSize: RecoveryPhraseValidationFlowState.wordGroupSize, from: wordGroup ) @@ -257,7 +155,7 @@ private extension WordChipGrid { } } -private extension RecoveryPhraseValidationState { +private extension RecoveryPhraseValidationFlowState { var coloredChipColor: Color { if self.isComplete { return isValid ? Asset.Colors.Buttons.activeButton.color : Asset.Colors.BackgroundColors.red.color @@ -267,18 +165,20 @@ private extension RecoveryPhraseValidationState { } } +// MARK: - Previews + struct RecoveryPhraseBackupView_Previews: PreviewProvider { static var previews: some View { NavigationView { - RecoveryPhraseBackupValidationView(store: .demoStep4) + RecoveryPhraseValidationFlowView(store: .demoStep4) } NavigationView { - RecoveryPhraseBackupValidationView(store: .demoStep1) + RecoveryPhraseValidationFlowView(store: .demoStep1) } NavigationView { - RecoveryPhraseBackupValidationView(store: .demoStep1) + RecoveryPhraseValidationFlowView(store: .demoStep1) } .preferredColorScheme(.dark) } diff --git a/secant/Features/Request/RequestStore.swift b/secant/Features/Request/RequestStore.swift index 44bbf0c..9a48275 100644 --- a/secant/Features/Request/RequestStore.swift +++ b/secant/Features/Request/RequestStore.swift @@ -1,18 +1,25 @@ import ComposableArchitecture +typealias RequestReducer = Reducer +typealias RequestStore = Store +typealias RequestViewStore = ViewStore + +// MARK: - State + struct RequestState: Equatable { } +// MARK: - Action + enum RequestAction: Equatable { case noOp } -struct RequestEnvironment { -} +// MARK: - Environment -// MARK: - RequestReducer +struct RequestEnvironment { } -typealias RequestReducer = Reducer +// MARK: - Reducer extension RequestReducer { static let `default` = RequestReducer { _, action, _ in @@ -23,9 +30,13 @@ extension RequestReducer { } } -// MARK: - RequestStore +// MARK: Placeholders -typealias RequestStore = Store +extension RequestState { + static var placeholder: Self { + .init() + } +} extension RequestStore { static let placeholder = RequestStore( @@ -34,18 +45,3 @@ extension RequestStore { environment: RequestEnvironment() ) } - -// MARK: - RequestViewStore - -typealias RequestViewStore = ViewStore - -extension RequestViewStore { -} - -// MARK: PlaceHolders - -extension RequestState { - static var placeholder: Self { - .init() - } -} diff --git a/secant/Features/Request/Views/RequestView.swift b/secant/Features/Request/RequestView.swift similarity index 94% rename from secant/Features/Request/Views/RequestView.swift rename to secant/Features/Request/RequestView.swift index 81c8bad..8c1ad1e 100644 --- a/secant/Features/Request/Views/RequestView.swift +++ b/secant/Features/Request/RequestView.swift @@ -11,6 +11,8 @@ struct RequestView: View { } } +// MARK: - Previews + struct RequestView_Previews: PreviewProvider { static var previews: some View { RequestView(store: .placeholder) diff --git a/secant/Features/Sandbox/SandboxStore.swift b/secant/Features/Sandbox/SandboxStore.swift index f058691..42b3750 100644 --- a/secant/Features/Sandbox/SandboxStore.swift +++ b/secant/Features/Sandbox/SandboxStore.swift @@ -1,6 +1,12 @@ import ComposableArchitecture import SwiftUI +typealias SandboxReducer = Reducer +typealias SandboxStore = Store +typealias SandboxViewStore = ViewStore + +// MARK: - State + struct SandboxState: Equatable { enum Route: Equatable, CaseIterable { case history @@ -10,21 +16,25 @@ struct SandboxState: Equatable { case scan case request } - var transactionHistoryState: TransactionHistoryState + var transactionHistoryState: TransactionHistoryFlowState var profileState: ProfileState var route: Route? } +// MARK: - Action + enum SandboxAction: Equatable { case updateRoute(SandboxState.Route?) - case transactionHistory(TransactionHistoryAction) + case transactionHistory(TransactionHistoryFlowAction) case profile(ProfileAction) case reset } -// MARK: - SandboxReducer +// MARK: - Environment -typealias SandboxReducer = Reducer +struct SandboxEnvironment { } + +// MARK: - Reducer extension SandboxReducer { static let `default` = SandboxReducer { state, action, environment in @@ -33,14 +43,14 @@ extension SandboxReducer { state.route = route return .none case let .transactionHistory(transactionHistoryAction): - return TransactionHistoryReducer + return TransactionHistoryFlowReducer .default .run( &state.transactionHistoryState, transactionHistoryAction, - TransactionHistoryEnvironment( + TransactionHistoryFlowEnvironment( scheduler: DispatchQueue.main.eraseToAnyScheduler(), - wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer() + SDKSynchronizer: LiveWrappedSDKSynchronizer() ) ) .map(SandboxAction.transactionHistory) @@ -61,12 +71,10 @@ extension SandboxReducer { } } -// MARK: - SandboxStore - -typealias SandboxStore = Store +// MARK: - Store extension SandboxStore { - func historyStore() -> TransactionHistoryStore { + func historyStore() -> TransactionHistoryFlowStore { self.scope( state: \.transactionHistoryState, action: SandboxAction.transactionHistory @@ -81,22 +89,20 @@ extension SandboxStore { } } -// MARK: - SandboxViewStore - -typealias SandboxViewStore = ViewStore +// MARK: - ViewStore extension SandboxViewStore { func toggleSelectedTransaction() { let isAlreadySelected = (self.selectedTranactionID != nil) let transcation = self.transactionHistoryState.transactions[5] - let newRoute = isAlreadySelected ? nil : TransactionHistoryState.Route.showTransaction(transcation) + let newRoute = isAlreadySelected ? nil : TransactionHistoryFlowState.Route.showTransaction(transcation) send(.transactionHistory(.updateRoute(newRoute))) } var selectedTranactionID: String? { self.transactionHistoryState .route - .flatMap(/TransactionHistoryState.Route.showTransaction) + .flatMap(/TransactionHistoryFlowState.Route.showTransaction) .map(\.id) } @@ -110,7 +116,8 @@ extension SandboxViewStore { } } -// MARK: PlaceHolders +// MARK: - PlaceHolders + extension SandboxState { static var placeholder: Self { .init( @@ -120,3 +127,17 @@ extension SandboxState { ) } } + +extension SandboxStore { + static var placeholder: SandboxStore { + SandboxStore( + initialState: SandboxState( + transactionHistoryState: .placeHolder, + profileState: .placeholder, + route: nil + ), + reducer: .default.debug(), + environment: SandboxEnvironment() + ) + } +} diff --git a/secant/Features/Sandbox/Views/SandboxView.swift b/secant/Features/Sandbox/SandboxView.swift similarity index 80% rename from secant/Features/Sandbox/Views/SandboxView.swift rename to secant/Features/Sandbox/SandboxView.swift index f5a1041..605fa39 100644 --- a/secant/Features/Sandbox/Views/SandboxView.swift +++ b/secant/Features/Sandbox/SandboxView.swift @@ -2,7 +2,12 @@ import SwiftUI import ComposableArchitecture struct SandboxView: View { - let store: Store + struct SandboxRouteValue: Identifiable { + let id: Int + let route: SandboxState.Route + } + + let store: SandboxStore var navigationRouteValues: [SandboxRouteValue] = SandboxState.Route.allCases .enumerated() @@ -17,21 +22,21 @@ struct SandboxView: View { @ViewBuilder func view(for route: SandboxState.Route) -> some View { switch route { case .history: - TransactionHistoryView(store: store.historyStore()) + TransactionHistoryFlowView(store: store.historyStore()) case .send: - SendView( + SendFlowView( store: .init( initialState: .placeholder, - reducer: SendReducer.default( + reducer: SendFlowReducer.default( whenDone: { SandboxViewStore(store).send(.updateRoute(nil)) } ) .debug(), - environment: SendEnvironment( + environment: SendFlowEnvironment( mnemonicSeedPhraseProvider: .live, scheduler: DispatchQueue.main.eraseToAnyScheduler(), walletStorage: .live(), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer() + derivationTool: .live(), + SDKSynchronizer: LiveWrappedSDKSynchronizer() ) ) ) @@ -88,7 +93,7 @@ struct SandboxView: View { isPresented: viewStore.bindingForRoute(.history), content: { NavigationView { - TransactionHistoryView(store: store.historyStore()) + TransactionHistoryFlowView(store: store.historyStore()) .toolbar { ToolbarItem { Button("Done") { viewStore.send(.updateRoute(nil)) } @@ -102,27 +107,8 @@ struct SandboxView: View { } } -struct SandboxRouteValue: Identifiable { - let id: Int - let route: SandboxState.Route -} - // MARK: - Previews -extension SandboxStore { - static var placeholder: SandboxStore { - SandboxStore( - initialState: SandboxState( - transactionHistoryState: .placeHolder, - profileState: .placeholder, - route: nil - ), - reducer: .default.debug(), - environment: () - ) - } -} - struct SandboxView_Previews: PreviewProvider { static var previews: some View { NavigationView { diff --git a/secant/Features/Scan/ScanStore.swift b/secant/Features/Scan/ScanStore.swift index 596af0e..fd5573f 100644 --- a/secant/Features/Scan/ScanStore.swift +++ b/secant/Features/Scan/ScanStore.swift @@ -1,18 +1,25 @@ import ComposableArchitecture +typealias ScanReducer = Reducer +typealias ScanStore = Store +typealias ScanViewStore = ViewStore + +// MARK: - State + struct ScanState: Equatable { } +// MARK: - Action + enum ScanAction: Equatable { case noOp } -struct ScanEnvironment { -} +// MARK: - Environment -// MARK: - ScanReducer +struct ScanEnvironment { } -typealias ScanReducer = Reducer +// MARK: - Reducer extension ScanReducer { static let `default` = ScanReducer { _, action, _ in @@ -23,9 +30,13 @@ extension ScanReducer { } } -// MARK: - ScanStore +// MARK: Placeholders -typealias ScanStore = Store +extension ScanState { + static var placeholder: Self { + .init() + } +} extension ScanStore { static let placeholder = ScanStore( @@ -34,18 +45,3 @@ extension ScanStore { environment: ScanEnvironment() ) } - -// MARK: - ScanViewStore - -typealias ScanViewStore = ViewStore - -extension ScanViewStore { -} - -// MARK: PlaceHolders - -extension ScanState { - static var placeholder: Self { - .init() - } -} diff --git a/secant/Features/Scan/Views/ScanView.swift b/secant/Features/Scan/ScanView.swift similarity index 94% rename from secant/Features/Scan/Views/ScanView.swift rename to secant/Features/Scan/ScanView.swift index c1dbe01..a138e5b 100644 --- a/secant/Features/Scan/Views/ScanView.swift +++ b/secant/Features/Scan/ScanView.swift @@ -11,6 +11,8 @@ struct ScanView: View { } } +// MARK: - Previews + struct ScanView_Previews: PreviewProvider { static var previews: some View { ScanView(store: .placeholder) diff --git a/secant/Features/Send/SendStore.swift b/secant/Features/SendFlow/SendFlowStore.swift similarity index 60% rename from secant/Features/Send/SendStore.swift rename to secant/Features/SendFlow/SendFlowStore.swift index 54e7eb5..e13a734 100644 --- a/secant/Features/Send/SendStore.swift +++ b/secant/Features/SendFlow/SendFlowStore.swift @@ -1,24 +1,21 @@ +// +// SendFlowStore.swift +// secant-testnet +// +// Created by Lukáš Korba on 04/25/2022. +// + import SwiftUI import ComposableArchitecture import ZcashLightClientKit -struct Transaction: Equatable { - var amount: Int64 - var memo: String - var toAddress: String -} +typealias SendFlowReducer = Reducer +typealias SendFlowStore = Store +typealias SendFlowViewStore = ViewStore -extension Transaction { - static var placeholder: Self { - .init( - amount: 0, - memo: "", - toAddress: "" - ) - } -} +// MARK: - State -struct SendState: Equatable { +struct SendFlowState: Equatable { enum Route: Equatable { case confirmation case success @@ -31,9 +28,9 @@ struct SendState: Equatable { var isSendingTransaction = false var memo = "" var totalBalance: Int64 = 0 - var transaction: Transaction - var transactionAddressInputState: TransactionAddressInputState - var transactionAmountInputState: TransactionAmountInputState + var transaction: SendFlowTransaction + var transactionAddressInputState: TransactionAddressTextFieldState + var transactionAmountInputState: TransactionAmountTextFieldState var isInvalidAddressFormat: Bool { !transactionAddressInputState.isValidAddress @@ -60,38 +57,38 @@ struct SendState: Equatable { } } -enum SendAction: Equatable { +// MARK: - Action + +enum SendFlowAction: Equatable { case onAppear case onDisappear case sendConfirmationPressed case sendTransactionResult(Result) case synchronizerStateChanged(WrappedSDKSynchronizerState) - case transactionAddressInput(TransactionAddressInputAction) - case transactionAmountInput(TransactionAmountInputAction) + case transactionAddressInput(TransactionAddressTextFieldAction) + case transactionAmountInput(TransactionAmountTextFieldAction) case updateBalance(Int64) case updateMemo(String) - case updateTransaction(Transaction) - case updateRoute(SendState.Route?) + case updateTransaction(SendFlowTransaction) + case updateRoute(SendFlowState.Route?) } -struct SendEnvironment { - let mnemonicSeedPhraseProvider: MnemonicSeedPhraseProvider +// MARK: - Environment + +struct SendFlowEnvironment { + let mnemonicSeedPhraseProvider: WrappedMnemonic let scheduler: AnySchedulerOf - let walletStorage: WalletStorageInteractor - let wrappedDerivationTool: WrappedDerivationTool - let wrappedSDKSynchronizer: WrappedSDKSynchronizer + let walletStorage: WrappedWalletStorage + let derivationTool: WrappedDerivationTool + let SDKSynchronizer: WrappedSDKSynchronizer } -// MARK: - SendReducer +// MARK: - Reducer -private struct ListenerId: Hashable {} - -typealias SendReducer = Reducer - -extension SendReducer { +extension SendFlowReducer { private struct SyncStatusUpdatesID: Hashable {} - static let `default` = SendReducer.combine( + static let `default` = SendFlowReducer.combine( [ sendReducer, transactionAddressInputReducer, @@ -100,7 +97,7 @@ extension SendReducer { ) .debug() - private static let sendReducer = SendReducer { state, action, environment in + private static let sendReducer = SendFlowReducer { state, action, environment in switch action { case let .updateTransaction(transaction): state.transaction = transaction @@ -128,13 +125,13 @@ extension SendReducer { do { let storedWallet = try environment.walletStorage.exportWallet() let seedBytes = try environment.mnemonicSeedPhraseProvider.toSeed(storedWallet.seedPhrase) - guard let spendingKey = try environment.wrappedDerivationTool.deriveSpendingKeys(seedBytes, 1).first else { + guard let spendingKey = try environment.derivationTool.deriveSpendingKeys(seedBytes, 1).first else { return Effect(value: .updateRoute(.failure)) } state.isSendingTransaction = true - return environment.wrappedSDKSynchronizer.sendTransaction( + return environment.SDKSynchronizer.sendTransaction( with: spendingKey, zatoshi: Int64(state.transaction.amount), to: state.transaction.toAddress, @@ -142,7 +139,7 @@ extension SendReducer { from: 0 ) .receive(on: environment.scheduler) - .map(SendAction.sendTransactionResult) + .map(SendFlowAction.sendTransactionResult) .eraseToEffect() } catch { return Effect(value: .updateRoute(.failure)) @@ -164,19 +161,19 @@ extension SendReducer { return .none case .onAppear: - return environment.wrappedSDKSynchronizer.stateChanged - .map(SendAction.synchronizerStateChanged) + return environment.SDKSynchronizer.stateChanged + .map(SendFlowAction.synchronizerStateChanged) .eraseToEffect() - .cancellable(id: ListenerId(), cancelInFlight: true) + .cancellable(id: SyncStatusUpdatesID(), cancelInFlight: true) case .onDisappear: - return Effect.cancel(id: ListenerId()) + return Effect.cancel(id: SyncStatusUpdatesID()) case .synchronizerStateChanged(.synced): - return environment.wrappedSDKSynchronizer.getShieldedBalance() + return environment.SDKSynchronizer.getShieldedBalance() .receive(on: environment.scheduler) .map({ $0.total }) - .map(SendAction.updateBalance) + .map(SendFlowAction.updateBalance) .eraseToEffect() case .synchronizerStateChanged(let synchronizerState): @@ -193,24 +190,24 @@ extension SendReducer { } } - private static let transactionAddressInputReducer: SendReducer = TransactionAddressInputReducer.default.pullback( - state: \SendState.transactionAddressInputState, - action: /SendAction.transactionAddressInput, + private static let transactionAddressInputReducer: SendFlowReducer = TransactionAddressTextFieldReducer.default.pullback( + state: \SendFlowState.transactionAddressInputState, + action: /SendFlowAction.transactionAddressInput, environment: { environment in - TransactionAddressInputEnvironment( - wrappedDerivationTool: environment.wrappedDerivationTool + TransactionAddressTextFieldEnvironment( + derivationTool: environment.derivationTool ) } ) - private static let transactionAmountInputReducer: SendReducer = TransactionAmountInputReducer.default.pullback( - state: \SendState.transactionAmountInputState, - action: /SendAction.transactionAmountInput, - environment: { _ in TransactionAmountInputEnvironment() } + private static let transactionAmountInputReducer: SendFlowReducer = TransactionAmountTextFieldReducer.default.pullback( + state: \SendFlowState.transactionAmountInputState, + action: /SendFlowAction.transactionAmountInput, + environment: { _ in TransactionAmountTextFieldEnvironment() } ) - static func `default`(whenDone: @escaping () -> Void) -> SendReducer { - SendReducer { state, action, environment in + static func `default`(whenDone: @escaping () -> Void) -> SendFlowReducer { + SendFlowReducer { state, action, environment in switch action { case let .updateRoute(route) where route == .done: return Effect.fireAndForget(whenDone) @@ -221,68 +218,62 @@ extension SendReducer { } } -// MARK: - SendStore +// MARK: - ViewStore -typealias SendStore = Store - -// MARK: - SendViewStore - -typealias SendViewStore = ViewStore - -extension SendViewStore { - var bindingForTransaction: Binding { +extension SendFlowViewStore { + var bindingForTransaction: Binding { self.binding( get: \.transaction, - send: SendAction.updateTransaction + send: SendFlowAction.updateTransaction ) } - var routeBinding: Binding { + var routeBinding: Binding { self.binding( get: \.route, - send: SendAction.updateRoute + send: SendFlowAction.updateRoute ) } var bindingForConfirmation: Binding { self.routeBinding.map( extract: { $0 == .confirmation || self.bindingForSuccess.wrappedValue || self.bindingForFailure.wrappedValue }, - embed: { $0 ? SendState.Route.confirmation : nil } + embed: { $0 ? SendFlowState.Route.confirmation : nil } ) } var bindingForSuccess: Binding { self.routeBinding.map( extract: { $0 == .success || self.bindingForDone.wrappedValue }, - embed: { $0 ? SendState.Route.success : SendState.Route.confirmation } + embed: { $0 ? SendFlowState.Route.success : SendFlowState.Route.confirmation } ) } var bindingForFailure: Binding { self.routeBinding.map( extract: { $0 == .failure || self.bindingForDone.wrappedValue }, - embed: { $0 ? SendState.Route.failure : SendState.Route.confirmation } + embed: { $0 ? SendFlowState.Route.failure : SendFlowState.Route.confirmation } ) } var bindingForDone: Binding { self.routeBinding.map( extract: { $0 == .done }, - embed: { $0 ? SendState.Route.done : SendState.Route.confirmation } + embed: { $0 ? SendFlowState.Route.done : SendFlowState.Route.confirmation } ) } var bindingForMemo: Binding { self.binding( get: \.memo, - send: SendAction.updateMemo + send: SendFlowAction.updateMemo ) } } -// MARK: PlaceHolders +// MARK: Placeholders -extension SendState { +extension SendFlowState { static var placeholder: Self { .init( route: nil, @@ -305,3 +296,26 @@ extension SendState { ) } } + +// #if DEBUG // FIX: Issue #306 - Release build is broken +extension SendFlowStore { + static var placeholder: SendFlowStore { + return SendFlowStore( + initialState: .init( + route: nil, + transaction: .placeholder, + transactionAddressInputState: .placeholder, + transactionAmountInputState: .placeholder + ), + reducer: .default, + environment: SendFlowEnvironment( + mnemonicSeedPhraseProvider: .live, + scheduler: DispatchQueue.main.eraseToAnyScheduler(), + walletStorage: .live(), + derivationTool: .live(), + SDKSynchronizer: LiveWrappedSDKSynchronizer() + ) + ) + } +} +// #endif diff --git a/secant/Features/Send/Views/SendView.swift b/secant/Features/SendFlow/SendFlowView.swift similarity index 75% rename from secant/Features/Send/Views/SendView.swift rename to secant/Features/SendFlow/SendFlowView.swift index 3ab38d0..2f739dc 100644 --- a/secant/Features/Send/Views/SendView.swift +++ b/secant/Features/SendFlow/SendFlowView.swift @@ -1,8 +1,15 @@ +// +// SendFlowView.swift +// secant-testnet +// +// Created by Lukáš Korba on 04/25/2022. +// + import SwiftUI import ComposableArchitecture -struct SendView: View { - let store: SendStore +struct SendFlowView: View { + let store: SendFlowStore var body: some View { WithViewStore(store) { viewStore in @@ -19,10 +26,12 @@ struct SendView: View { } } -struct SendView_Previews: PreviewProvider { +// MARK: - Previews + +struct SendFLowView_Previews: PreviewProvider { static var previews: some View { NavigationView { - SendView( + SendFlowView( store: .init( initialState: .init( route: nil, @@ -31,12 +40,12 @@ struct SendView_Previews: PreviewProvider { transactionAmountInputState: .placeholder ), reducer: .default, - environment: SendEnvironment( + environment: SendFlowEnvironment( mnemonicSeedPhraseProvider: .live, scheduler: DispatchQueue.main.eraseToAnyScheduler(), walletStorage: .live(), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer() + derivationTool: .live(), + SDKSynchronizer: LiveWrappedSDKSynchronizer() ) ) ) diff --git a/secant/Features/Send/Views/CreateTransactionView.swift b/secant/Features/SendFlow/Views/CreateTransactionView.swift similarity index 78% rename from secant/Features/Send/Views/CreateTransactionView.swift rename to secant/Features/SendFlow/Views/CreateTransactionView.swift index dd9d1b4..9ea5463 100644 --- a/secant/Features/Send/Views/CreateTransactionView.swift +++ b/secant/Features/SendFlow/Views/CreateTransactionView.swift @@ -2,7 +2,7 @@ import SwiftUI import ComposableArchitecture struct CreateTransaction: View { - let store: SendStore + let store: SendFlowStore var body: some View { UITextView.appearance().backgroundColor = .clear @@ -21,7 +21,7 @@ struct CreateTransaction: View { TransactionAmountTextField( store: store.scope( state: \.transactionAmountInputState, - action: SendAction.transactionAmountInput + action: SendFlowAction.transactionAmountInput ) ) @@ -49,7 +49,7 @@ struct CreateTransaction: View { TransactionAddressTextField( store: store.scope( state: \.transactionAddressInputState, - action: SendAction.transactionAddressInput + action: SendFlowAction.transactionAddressInput ) ) @@ -106,26 +106,3 @@ struct Create_Previews: PreviewProvider { } } } - -// #if DEBUG // FIX: Issue #306 - Release build is broken -extension SendStore { - static var placeholder: SendStore { - return SendStore( - initialState: .init( - route: nil, - transaction: .placeholder, - transactionAddressInputState: .placeholder, - transactionAmountInputState: .placeholder - ), - reducer: .default, - environment: SendEnvironment( - mnemonicSeedPhraseProvider: .live, - scheduler: DispatchQueue.main.eraseToAnyScheduler(), - walletStorage: .live(), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer() - ) - ) - } -} -// #endif diff --git a/secant/Features/Send/Views/TransactionConfirmationView.swift b/secant/Features/SendFlow/Views/TransactionConfirmationView.swift similarity index 92% rename from secant/Features/Send/Views/TransactionConfirmationView.swift rename to secant/Features/SendFlow/Views/TransactionConfirmationView.swift index e64f21b..fa77aab 100644 --- a/secant/Features/Send/Views/TransactionConfirmationView.swift +++ b/secant/Features/SendFlow/Views/TransactionConfirmationView.swift @@ -2,7 +2,7 @@ import SwiftUI import ComposableArchitecture struct TransactionConfirmation: View { - let viewStore: SendViewStore + let viewStore: SendFlowViewStore var body: some View { VStack { @@ -36,12 +36,14 @@ struct TransactionConfirmation: View { } } +// MARK: - Previews + struct Confirmation_Previews: PreviewProvider { static var previews: some View { NavigationView { StateContainer( initialState: ( - Transaction.placeholder, + SendFlowTransaction.placeholder, false ) ) { _ in diff --git a/secant/Features/Send/Views/TransactionFailedView.swift b/secant/Features/SendFlow/Views/TransactionFailedView.swift similarity index 92% rename from secant/Features/Send/Views/TransactionFailedView.swift rename to secant/Features/SendFlow/Views/TransactionFailedView.swift index 022d56a..7fb77e8 100644 --- a/secant/Features/Send/Views/TransactionFailedView.swift +++ b/secant/Features/SendFlow/Views/TransactionFailedView.swift @@ -2,7 +2,7 @@ import SwiftUI import ComposableArchitecture struct TransactionFailed: View { - let viewStore: SendViewStore + let viewStore: SendFlowViewStore var body: some View { VStack { @@ -25,6 +25,8 @@ struct TransactionFailed: View { } } +// MARK: - Previews + struct TransactionFailed_Previews: PreviewProvider { static var previews: some View { TransactionFailed(viewStore: ViewStore(.placeholder)) diff --git a/secant/Features/Send/Views/TransactionSentView.swift b/secant/Features/SendFlow/Views/TransactionSentView.swift similarity index 93% rename from secant/Features/Send/Views/TransactionSentView.swift rename to secant/Features/SendFlow/Views/TransactionSentView.swift index 2a916df..a4bf102 100644 --- a/secant/Features/Send/Views/TransactionSentView.swift +++ b/secant/Features/SendFlow/Views/TransactionSentView.swift @@ -2,7 +2,7 @@ import SwiftUI import ComposableArchitecture struct TransactionSent: View { - let viewStore: SendViewStore + let viewStore: SendFlowViewStore var body: some View { VStack { @@ -27,6 +27,8 @@ struct TransactionSent: View { } } +// MARK: - Previews + struct TransactionSent_Previews: PreviewProvider { static var previews: some View { TransactionSent(viewStore: ViewStore(.placeholder)) diff --git a/secant/Features/Settings/Settings.swift b/secant/Features/Settings/SettingsStore.swift similarity index 73% rename from secant/Features/Settings/Settings.swift rename to secant/Features/Settings/SettingsStore.swift index e24750b..9616d58 100644 --- a/secant/Features/Settings/Settings.swift +++ b/secant/Features/Settings/SettingsStore.swift @@ -1,18 +1,25 @@ import ComposableArchitecture +typealias SettingsReducer = Reducer +typealias SettingsStore = Store +typealias SettingsViewStore = ViewStore + +// MARK: - State + struct SettingsState: Equatable { } +// MARK: - Action + enum SettingsAction: Equatable { case noOp } -struct SettingsEnvironment: Equatable { -} +// MARK: - Environment -// MARK: - SettingsStateReducer +struct SettingsEnvironment: Equatable { } -typealias SettingsReducer = Reducer +// MARK: - Reducer extension SettingsReducer { static let `default` = SettingsReducer { _, action, _ in @@ -22,17 +29,3 @@ extension SettingsReducer { } } } - -// MARK: - SettingsStore - -typealias SettingsStore = Store - -extension SettingsStore { -} - -// MARK: - SettingsViewStore - -typealias SettingsViewStore = ViewStore - -extension SettingsViewStore { -} diff --git a/secant/Features/Settings/Views/SettingsView.swift b/secant/Features/Settings/SettingsView.swift similarity index 92% rename from secant/Features/Settings/Views/SettingsView.swift rename to secant/Features/Settings/SettingsView.swift index 4fcecea..748fed7 100644 --- a/secant/Features/Settings/Views/SettingsView.swift +++ b/secant/Features/Settings/SettingsView.swift @@ -6,6 +6,8 @@ struct SettingsView: View { } } +// MARK: - Previews + struct SettingsView_Previews: PreviewProvider { static var previews: some View { SettingsView() diff --git a/secant/Features/TransactionHistory/TransactionHistoryStore.swift b/secant/Features/TransactionHistoryFlow/TransactionHistoryFlowStore.swift similarity index 58% rename from secant/Features/TransactionHistory/TransactionHistoryStore.swift rename to secant/Features/TransactionHistoryFlow/TransactionHistoryFlowStore.swift index 9889d93..689c6cc 100644 --- a/secant/Features/TransactionHistory/TransactionHistoryStore.swift +++ b/secant/Features/TransactionHistoryFlow/TransactionHistoryFlowStore.swift @@ -1,18 +1,13 @@ import ComposableArchitecture import SwiftUI -extension Date { - func asHumanReadable() -> String { - let dateFormatter = DateFormatter() - - dateFormatter.dateStyle = .short - dateFormatter.timeStyle = .short - - return dateFormatter.string(from: self) - } -} +typealias TransactionHistoryFlowReducer = Reducer +typealias TransactionHistoryFlowStore = Store +typealias TransactionHistoryFlowViewStore = ViewStore -struct TransactionHistoryState: Equatable { +// MARK: - State + +struct TransactionHistoryFlowState: Equatable { enum Route: Equatable { case latest case all @@ -25,31 +20,33 @@ struct TransactionHistoryState: Equatable { var transactions: IdentifiedArrayOf } -enum TransactionHistoryAction: Equatable { +// MARK: - Action + +enum TransactionHistoryFlowAction: Equatable { case onAppear case onDisappear - case updateRoute(TransactionHistoryState.Route?) + case updateRoute(TransactionHistoryFlowState.Route?) case synchronizerStateChanged(WrappedSDKSynchronizerState) case updateTransactions([TransactionState]) } -struct TransactionHistoryEnvironment { +// MARK: - Environment + +struct TransactionHistoryFlowEnvironment { let scheduler: AnySchedulerOf - let wrappedSDKSynchronizer: WrappedSDKSynchronizer + let SDKSynchronizer: WrappedSDKSynchronizer } -// MARK: - TransactionHistoryReducer +// MARK: - Reducer -private struct ListenerId: Hashable {} - -typealias TransactionHistoryReducer = Reducer - -extension TransactionHistoryReducer { - static let `default` = TransactionHistoryReducer { state, action, environment in +extension TransactionHistoryFlowReducer { + private struct ListenerId: Hashable {} + + static let `default` = TransactionHistoryFlowReducer { state, action, environment in switch action { case .onAppear: - return environment.wrappedSDKSynchronizer.stateChanged - .map(TransactionHistoryAction.synchronizerStateChanged) + return environment.SDKSynchronizer.stateChanged + .map(TransactionHistoryFlowAction.synchronizerStateChanged) .eraseToEffect() .cancellable(id: ListenerId(), cancelInFlight: true) @@ -57,9 +54,9 @@ extension TransactionHistoryReducer { return Effect.cancel(id: ListenerId()) case .synchronizerStateChanged(.synced): - return environment.wrappedSDKSynchronizer.getAllTransactions() + return environment.SDKSynchronizer.getAllTransactions() .receive(on: environment.scheduler) - .map(TransactionHistoryAction.updateTransactions) + .map(TransactionHistoryFlowAction.updateTransactions) .eraseToEffect() case .synchronizerStateChanged(let synchronizerState): @@ -80,28 +77,22 @@ extension TransactionHistoryReducer { } } -// MARK: - TransactionHistoryStore +// MARK: - ViewStore -typealias TransactionHistoryStore = Store - -// MARK: - TransactionHistoryViewStore - -typealias TransactionHistoryViewStore = ViewStore - -extension TransactionHistoryViewStore { - private typealias Route = TransactionHistoryState.Route +extension TransactionHistoryFlowViewStore { + private typealias Route = TransactionHistoryFlowState.Route func bindingForSelectingTransaction(_ transaction: TransactionState) -> Binding { self.binding( - get: { $0.route.map(/TransactionHistoryState.Route.showTransaction) == transaction }, + get: { $0.route.map(/TransactionHistoryFlowState.Route.showTransaction) == transaction }, send: { isActive in - TransactionHistoryAction.updateRoute( isActive ? TransactionHistoryState.Route.showTransaction(transaction) : nil) + TransactionHistoryFlowAction.updateRoute( isActive ? TransactionHistoryFlowState.Route.showTransaction(transaction) : nil) } ) } } -// MARK: PlaceHolders +// MARK: Placeholders extension TransactionState { static var placeholder: Self { @@ -115,7 +106,7 @@ extension TransactionState { } } -extension TransactionHistoryState { +extension TransactionHistoryFlowState { static var placeHolder: Self { .init(transactions: .placeholder) } @@ -125,29 +116,29 @@ extension TransactionHistoryState { } } -extension TransactionHistoryStore { - static var placeholder: Store { +extension TransactionHistoryFlowStore { + static var placeholder: Store { return Store( initialState: .placeHolder, reducer: .default, - environment: TransactionHistoryEnvironment( + environment: TransactionHistoryFlowEnvironment( scheduler: DispatchQueue.main.eraseToAnyScheduler(), - wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer() + SDKSynchronizer: LiveWrappedSDKSynchronizer() ) ) } - static var demoWithSelectedTransaction: Store { + static var demoWithSelectedTransaction: Store { let transactions = IdentifiedArrayOf.placeholder return Store( - initialState: TransactionHistoryState( + initialState: TransactionHistoryFlowState( route: .showTransaction(transactions[3]), transactions: transactions ), reducer: .default.debug(), - environment: TransactionHistoryEnvironment( + environment: TransactionHistoryFlowEnvironment( scheduler: DispatchQueue.main.eraseToAnyScheduler(), - wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer() + SDKSynchronizer: LiveWrappedSDKSynchronizer() ) ) } diff --git a/secant/Features/TransactionHistory/Views/TransactionHistoryView.swift b/secant/Features/TransactionHistoryFlow/TransactionHistoryFlowView.swift similarity index 89% rename from secant/Features/TransactionHistory/Views/TransactionHistoryView.swift rename to secant/Features/TransactionHistoryFlow/TransactionHistoryFlowView.swift index 0869e07..80660cb 100644 --- a/secant/Features/TransactionHistory/Views/TransactionHistoryView.swift +++ b/secant/Features/TransactionHistoryFlow/TransactionHistoryFlowView.swift @@ -1,8 +1,8 @@ import SwiftUI import ComposableArchitecture -struct TransactionHistoryView: View { - let store: Store +struct TransactionHistoryFlowView: View { + let store: TransactionHistoryFlowStore var body: some View { UITableView.appearance().backgroundColor = .clear @@ -28,8 +28,8 @@ struct TransactionHistoryView: View { } } -extension TransactionHistoryView { - func transactionsList(with viewStore: TransactionHistoryViewStore) -> some View { +extension TransactionHistoryFlowView { + func transactionsList(with viewStore: TransactionHistoryFlowViewStore) -> some View { ForEach(viewStore.transactions) { transaction in WithStateBinding(binding: viewStore.bindingForSelectingTransaction(transaction)) { active in VStack { @@ -65,7 +65,7 @@ extension TransactionHistoryView { } } - func header(with viewStore: TransactionHistoryViewStore) -> some View { + func header(with viewStore: TransactionHistoryFlowViewStore) -> some View { HStack(spacing: 0) { VStack { Button("Latest") { @@ -90,10 +90,12 @@ extension TransactionHistoryView { } } +// MARK: - Previews + struct TransactionView_Previews: PreviewProvider { static var previews: some View { NavigationView { - TransactionHistoryView(store: .placeholder) + TransactionHistoryFlowView(store: .placeholder) .preferredColorScheme(.dark) } } diff --git a/secant/Features/TransactionHistory/Views/TransactionDetailView.swift b/secant/Features/TransactionHistoryFlow/Views/TransactionDetailView.swift similarity index 95% rename from secant/Features/TransactionHistory/Views/TransactionDetailView.swift rename to secant/Features/TransactionHistoryFlow/Views/TransactionDetailView.swift index d6fcae1..bb6bacb 100644 --- a/secant/Features/TransactionHistory/Views/TransactionDetailView.swift +++ b/secant/Features/TransactionHistoryFlow/Views/TransactionDetailView.swift @@ -9,6 +9,8 @@ struct TransactionDetailView: View { } } +// MARK: - Previews + struct TransactionDetail_Previews: PreviewProvider { static var previews: some View { NavigationView { diff --git a/secant/Features/WalletInfo/WalletInfo.swift b/secant/Features/WalletInfo/WalletInfoStore.swift similarity index 79% rename from secant/Features/WalletInfo/WalletInfo.swift rename to secant/Features/WalletInfo/WalletInfoStore.swift index 9a9a413..51fedc9 100644 --- a/secant/Features/WalletInfo/WalletInfo.swift +++ b/secant/Features/WalletInfo/WalletInfoStore.swift @@ -1,18 +1,26 @@ import ComposableArchitecture +typealias WalletInfoReducer = Reducer +typealias WalletInfoStore = Store +typealias WalletInfoViewStore = ViewStore + +// MARK: - State + struct WalletInfoState: Equatable { } +// MARK: - Action + enum WalletInfoAction: Equatable { case noOp } +// MARK: - Environment + struct WalletInfoEnvironment: Equatable { } -// MARK: - WalletInfoReducer - -typealias WalletInfoReducer = Reducer +// MARK: - Reducer extension WalletInfoReducer { static let `default` = WalletInfoReducer { _, action, _ in @@ -22,17 +30,3 @@ extension WalletInfoReducer { } } } - -// MARK: - WalletInfoStore - -typealias WalletInfoStore = Store - -extension WalletInfoStore { -} - -// MARK: - WalletInfoViewStore - -typealias WalletInfoViewStore = ViewStore - -extension WalletInfoViewStore { -} diff --git a/secant/Features/WalletInfo/Views/WalletInfoView.swift b/secant/Features/WalletInfo/WalletInfoView.swift similarity index 92% rename from secant/Features/WalletInfo/Views/WalletInfoView.swift rename to secant/Features/WalletInfo/WalletInfoView.swift index b2d2335..838ac5e 100644 --- a/secant/Features/WalletInfo/Views/WalletInfoView.swift +++ b/secant/Features/WalletInfo/WalletInfoView.swift @@ -6,6 +6,8 @@ struct WalletInfoView: View { } } +// MARK: - Previews + struct WalletInfoView_Previews: PreviewProvider { static var previews: some View { WalletInfoView() diff --git a/secant/Features/Welcome/Welcome.swift b/secant/Features/Welcome/WelcomeStore.swift similarity index 70% rename from secant/Features/Welcome/Welcome.swift rename to secant/Features/Welcome/WelcomeStore.swift index 8107fd3..42458a7 100644 --- a/secant/Features/Welcome/Welcome.swift +++ b/secant/Features/Welcome/WelcomeStore.swift @@ -8,17 +8,25 @@ import Foundation import ComposableArchitecture +typealias WelcomeReducer = Reducer +typealias WelcomeStore = Store +typealias WelcomeViewStore = ViewStore + +// MARK: - State + struct WelcomeState: Equatable {} -extension WelcomeState { - static let placeholder = WelcomeState() -} +// MARK: - Action enum WelcomeAction: Equatable { case debugMenuStartup } -typealias WelcomeReducer = Reducer +// MARK: - Environment + +struct WelcomeEnvironment { } + +// MARK: - Reducer extension WelcomeReducer { static let `default` = WelcomeReducer { _, _, _ in @@ -26,12 +34,18 @@ extension WelcomeReducer { } } -typealias WelcomeStore = Store +// MARK: - Store extension WelcomeStore { static var demo = WelcomeStore( initialState: .placeholder, reducer: .default, - environment: () + environment: WelcomeEnvironment() ) } + +// MARK: - Placeholders + +extension WelcomeState { + static let placeholder = WelcomeState() +} diff --git a/secant/Features/Welcome/WelcomeView.swift b/secant/Features/Welcome/WelcomeView.swift index 8d73a7a..09663f8 100644 --- a/secant/Features/Welcome/WelcomeView.swift +++ b/secant/Features/Welcome/WelcomeView.swift @@ -41,57 +41,7 @@ struct WelcomeView: View { } } -struct ZcashBadge: View { - @Environment(\.colorScheme) var colorScheme - - var body: some View { - ZStack { - GeometryReader { proxy in - let outterPadding = proxy.size.height * 0.015 - let firstPadding = proxy.size.height * 0.075 + outterPadding - let secondRingPadding = firstPadding * 1.5 - let outerShadowDrop = proxy.size.height * 0.14 - let outerShadowOffset = proxy.size.height * 0.055 - - Circle() - .fill( - LinearGradient( - colors: [ - Asset.Colors.ZcashBadge.outerRingGradientStart.color, - Asset.Colors.ZcashBadge.outerRingGradientEnd.color - ], - startPoint: UnitPoint(x: 0.5, y: 0), - endPoint: UnitPoint(x: 0.5, y: 1) - ) - ) - .if(colorScheme == .light) { view in - view.shadow( - color: Asset.Colors.ZcashBadge.shadowColor.color, - radius: outerShadowDrop, - x: outerShadowOffset, - y: outerShadowOffset - ) - } - - Circle() - .foregroundColor(Asset.Colors.ZcashBadge.thickRing.color) - .padding(outterPadding) - - Circle() - .foregroundColor(Asset.Colors.ZcashBadge.thinRing.color) - .padding(firstPadding) - - Circle() - .foregroundColor(Asset.Colors.ZcashBadge.innerCircle.color) - .padding(secondRingPadding) - - ZcashSymbol() - .fill(Asset.Colors.ZcashBadge.zcashLogoFill.color) - .padding(firstPadding + secondRingPadding) - } - } - } -} +// MARK: - Previews struct WelcomeView_Previews: PreviewProvider { static let squarePreviewSize: CGFloat = 360 diff --git a/secant/MockedDependencies/Services.swift b/secant/MockedDependencies/Services.swift deleted file mode 100644 index 7ca873e..0000000 --- a/secant/MockedDependencies/Services.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// Services.swift -// secant -// -// Created by Francisco Gindre on 8/6/21. -// - -import Foundation -import ZcashLightClientKit - -protocol Services { - var networkProvider: ZcashNetworkProvider { get } - var seedHandler: MnemonicSeedPhraseProvider { get } - var walletStorage: WalletStorageInteractor { get } -} - -protocol ZcashNetworkProvider { - func currentNetwork() -> ZcashNetwork -} diff --git a/secant/Features/App/AppDelegate.swift b/secant/Models/AppDelegate.swift similarity index 100% rename from secant/Features/App/AppDelegate.swift rename to secant/Models/AppDelegate.swift diff --git a/secant/Models/Balance.swift b/secant/Models/Balance.swift deleted file mode 100644 index c4bb4f3..0000000 --- a/secant/Models/Balance.swift +++ /dev/null @@ -1,108 +0,0 @@ -// -// Balance.swift -// secant -// -// Created by Francisco Gindre on 8/9/21. -// - -import Foundation - -/** -Funds Expressed in Zatoshis -*/ -protocol Funds { - /** - Confirmed, spendable funds - */ - var confirmed: Int64 { get } - - /** - Unconfirmed, not yet spendable funds. - */ - var unconfirmed: Int64 { get } - - /** - Represents a null value of Funds with Zero confirmed and Zero unconfirmed funds. - */ - static var noFunds: Funds { get } -} - -/** -Wallet Balance that condenses transparent, Sapling and Orchard funds -*/ -protocol WalletBalance { - /** - Transparent funds. This is the sum of the UTXOs of the user found at a given time - */ - var transparent: Funds { get } - - /** - Funds on the Sapling shielded pool for a given user. - */ - var sapling: Funds { get } - - /** - Funds on the Orchard shielded pool for a given user. - */ - var orchard: Funds { get } - - /** - The sum of all confirmed funds, transparent, sapling and orchard funds (calculated) - */ - var totalAvailableBalance: Int64 { get } - - /** - The sum of all unconfirmed funds: transparent, sapling, and orchard funds (calculated - */ - var totalUnconfirmedBalance: Int64 { get } - - /** - The sum of all funds confirmed and unconfirmed of all pools (transparent, sapling and orchard). - */ - var totalBalance: Int64 { get } - - /** - Represents a the value of Zero funds. - */ - static var nullBalance: WalletBalance { get } -} - -extension WalletBalance { - static var nullBalance: WalletBalance { - Balance( - transparent: ZcashFunds.noFunds, - sapling: ZcashFunds.noFunds, - orchard: ZcashFunds.noFunds - ) - } - - var totalAvailableBalance: Int64 { - transparent.confirmed + sapling.confirmed + orchard.confirmed - } - - var totalUnconfirmedBalance: Int64 { - transparent.unconfirmed + sapling.unconfirmed + orchard.unconfirmed - } - - var totalBalance: Int64 { - totalAvailableBalance + totalUnconfirmedBalance - } -} - -/** -Concrete Wallet Balance. -*/ -struct Balance: WalletBalance { - var transparent: Funds - var sapling: Funds - var orchard: Funds -} - -struct ZcashFunds: Funds { - static var noFunds: Funds { - ZcashFunds(confirmed: 0, unconfirmed: 0) - } - - var confirmed: Int64 - var unconfirmed: Int64 -} diff --git a/secant/Features/BackupFlow/DropDelegate.swift b/secant/Models/DropDelegate.swift similarity index 96% rename from secant/Features/BackupFlow/DropDelegate.swift rename to secant/Models/DropDelegate.swift index 9d9a454..08c8ac7 100644 --- a/secant/Features/BackupFlow/DropDelegate.swift +++ b/secant/Models/DropDelegate.swift @@ -36,7 +36,7 @@ struct WordChipDropDelegate: DropDelegate { } } -extension RecoveryPhraseValidationState { +extension RecoveryPhraseValidationFlowState { func groupCompleted(index: Int) -> Bool { validationWords.first(where: { $0.groupIndex == index }) != nil } diff --git a/secant/Util/InitializationState.swift b/secant/Models/InitializationState.swift similarity index 100% rename from secant/Util/InitializationState.swift rename to secant/Models/InitializationState.swift diff --git a/secant/Models/RecoveryPhrase.swift b/secant/Models/RecoveryPhrase.swift new file mode 100644 index 0000000..98c9e51 --- /dev/null +++ b/secant/Models/RecoveryPhrase.swift @@ -0,0 +1,43 @@ +// +// RecoveryPhrase.swift +// secant-testnet +// +// Created by Lukáš Korba on 12.05.2022. +// + +import Foundation + +enum RecoveryPhraseError: Error { + /// This error is thrown then the Recovery Phrase can't be generated + case unableToGeneratePhrase +} + +struct RecoveryPhrase: Equatable { + struct Group: Hashable { + var startIndex: Int + var words: [String] + } + + let words: [String] + + private let groupSize = 6 + + func toGroups() -> [Group] { + let chunks = words.count / groupSize + return zip(0 ..< chunks, words.chunked(into: groupSize)).map { + Group(startIndex: $0 * groupSize + 1, words: $1) + } + } + + func toString() -> String { + words.joined(separator: " ") + } + + func words(fromMissingIndices indices: [Int]) -> [PhraseChip.Kind] { + assert((indices.count - 1) * groupSize <= self.words.count) + + return indices.enumerated().map { index, position in + .unassigned(word: self.words[(index * groupSize) + position]) + } + } +} diff --git a/secant/Models/SendFlowTransaction.swift b/secant/Models/SendFlowTransaction.swift new file mode 100644 index 0000000..683cc26 --- /dev/null +++ b/secant/Models/SendFlowTransaction.swift @@ -0,0 +1,25 @@ +// +// Transaction.swift +// secant-testnet +// +// Created by Lukáš Korba on 12.05.2022. +// + +import Foundation + +/// Simple model that holds data throughout the `SendFlow` feature +struct SendFlowTransaction: Equatable { + var amount: Int64 + var memo: String + var toAddress: String +} + +extension SendFlowTransaction { + static var placeholder: Self { + .init( + amount: 0, + memo: "", + toAddress: "" + ) + } +} diff --git a/secant/Models/StoredWallet.swift b/secant/Models/StoredWallet.swift new file mode 100644 index 0000000..69fac12 --- /dev/null +++ b/secant/Models/StoredWallet.swift @@ -0,0 +1,20 @@ +// +// StoredWallet.swift +// secant-testnet +// +// Created by Lukáš Korba on 13.05.2022. +// + +import Foundation +import ZcashLightClientKit +import MnemonicSwift + +/// Representation of the wallet stored in the persistent storage (typically keychain, handled by `WalletStorage`). +struct StoredWallet: Codable, Equatable { + let language: MnemonicLanguageType + let seedPhrase: String + let version: Int + + var birthday: BlockHeight? + var hasUserPassedPhraseBackupTest: Bool +} diff --git a/secant/Features/TransactionHistory/TransactionState.swift b/secant/Models/TransactionState.swift similarity index 96% rename from secant/Features/TransactionHistory/TransactionState.swift rename to secant/Models/TransactionState.swift index 0ebb217..32ed372 100644 --- a/secant/Features/TransactionHistory/TransactionState.swift +++ b/secant/Models/TransactionState.swift @@ -8,6 +8,7 @@ import Foundation import ZcashLightClientKit +/// Representation of the transaction on the SDK side, used as a bridge to the TCA wallet side. struct TransactionState: Equatable, Identifiable { enum Status: Equatable { case paid(success: Bool) diff --git a/secant/Models/ValidationWord.swift b/secant/Models/ValidationWord.swift new file mode 100644 index 0000000..f271b39 --- /dev/null +++ b/secant/Models/ValidationWord.swift @@ -0,0 +1,15 @@ +// +// ValidationWord.swift +// secant-testnet +// +// Created by Lukáš Korba on 12.05.2022. +// + +import Foundation + +/// Represents the data of a word that has been placed into an empty position, that will be used +/// to validate the completed phrase when all ValidationWords have been placed. +struct ValidationWord: Equatable { + var groupIndex: Int + var word: String +} diff --git a/secant/Assets.xcassets/AppIcon.appiconset/100.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/100.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/100.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/100.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/1024.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/1024.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/1024.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/1024.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/114.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/114.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/114.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/114.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/120.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/120.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/120.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/120.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/128.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/128.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/128.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/128.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/144.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/144.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/144.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/144.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/152.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/152.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/152.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/152.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/16.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/16.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/16.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/16.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/167.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/167.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/167.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/167.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/172.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/172.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/172.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/172.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/180.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/180.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/180.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/180.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/196.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/196.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/196.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/196.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/20.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/20.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/20.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/20.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/216.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/216.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/216.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/216.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/256.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/256.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/256.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/256.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/29.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/29.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/29.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/29.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/32.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/32.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/32.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/32.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/40.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/40.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/40.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/40.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/48.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/48.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/48.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/48.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/50.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/50.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/50.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/50.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/512.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/512.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/512.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/512.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/55.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/55.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/55.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/55.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/57.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/57.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/57.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/57.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/58.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/58.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/58.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/58.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/60.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/60.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/60.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/60.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/64.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/64.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/64.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/64.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/72.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/72.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/72.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/72.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/76.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/76.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/76.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/76.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/80.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/80.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/80.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/80.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/87.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/87.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/87.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/87.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/88.png b/secant/Resources/Assets.xcassets/AppIcon.appiconset/88.png similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/88.png rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/88.png diff --git a/secant/Assets.xcassets/AppIcon.appiconset/Contents.json b/secant/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from secant/Assets.xcassets/AppIcon.appiconset/Contents.json rename to secant/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/secant/Assets.xcassets/Backgrounds/Contents.json b/secant/Resources/Assets.xcassets/Backgrounds/Contents.json similarity index 100% rename from secant/Assets.xcassets/Backgrounds/Contents.json rename to secant/Resources/Assets.xcassets/Backgrounds/Contents.json diff --git a/secant/Assets.xcassets/Backgrounds/callout0.imageset/Contents.json b/secant/Resources/Assets.xcassets/Backgrounds/callout0.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout0.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Backgrounds/callout0.imageset/Contents.json diff --git a/secant/Assets.xcassets/Backgrounds/callout0.imageset/callout0-1.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout0.imageset/callout0-1.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout0.imageset/callout0-1.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout0.imageset/callout0-1.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout0.imageset/callout0-2.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout0.imageset/callout0-2.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout0.imageset/callout0-2.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout0.imageset/callout0-2.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout0.imageset/callout0.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout0.imageset/callout0.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout0.imageset/callout0.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout0.imageset/callout0.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout1.imageset/Contents.json b/secant/Resources/Assets.xcassets/Backgrounds/callout1.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout1.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Backgrounds/callout1.imageset/Contents.json diff --git a/secant/Assets.xcassets/Backgrounds/callout1.imageset/callout1-1.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout1.imageset/callout1-1.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout1.imageset/callout1-1.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout1.imageset/callout1-1.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout1.imageset/callout1-2.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout1.imageset/callout1-2.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout1.imageset/callout1-2.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout1.imageset/callout1-2.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout1.imageset/callout1.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout1.imageset/callout1.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout1.imageset/callout1.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout1.imageset/callout1.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout2.imageset/Contents.json b/secant/Resources/Assets.xcassets/Backgrounds/callout2.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout2.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Backgrounds/callout2.imageset/Contents.json diff --git a/secant/Assets.xcassets/Backgrounds/callout2.imageset/callout2-1.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout2.imageset/callout2-1.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout2.imageset/callout2-1.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout2.imageset/callout2-1.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout2.imageset/callout2-2.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout2.imageset/callout2-2.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout2.imageset/callout2-2.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout2.imageset/callout2-2.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout2.imageset/callout2.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout2.imageset/callout2.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout2.imageset/callout2.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout2.imageset/callout2.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout3.imageset/Contents.json b/secant/Resources/Assets.xcassets/Backgrounds/callout3.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout3.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Backgrounds/callout3.imageset/Contents.json diff --git a/secant/Assets.xcassets/Backgrounds/callout3.imageset/callout3-1.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout3.imageset/callout3-1.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout3.imageset/callout3-1.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout3.imageset/callout3-1.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout3.imageset/callout3-2.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout3.imageset/callout3-2.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout3.imageset/callout3-2.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout3.imageset/callout3-2.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout3.imageset/callout3.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout3.imageset/callout3.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout3.imageset/callout3.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout3.imageset/callout3.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout4.imageset/Contents.json b/secant/Resources/Assets.xcassets/Backgrounds/callout4.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout4.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Backgrounds/callout4.imageset/Contents.json diff --git a/secant/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout-1.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout-1.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout-1.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout-1.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout-2.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout-2.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout-2.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout-2.jpg diff --git a/secant/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout.jpg b/secant/Resources/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout.jpg similarity index 100% rename from secant/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout.jpg rename to secant/Resources/Assets.xcassets/Backgrounds/callout4.imageset/finalcallout.jpg diff --git a/secant/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/Contents.json b/secant/Resources/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/Contents.json diff --git a/secant/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/calloutBackupFailed.png b/secant/Resources/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/calloutBackupFailed.png similarity index 100% rename from secant/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/calloutBackupFailed.png rename to secant/Resources/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/calloutBackupFailed.png diff --git a/secant/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/calloutBackupFailed@2x.png b/secant/Resources/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/calloutBackupFailed@2x.png similarity index 100% rename from secant/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/calloutBackupFailed@2x.png rename to secant/Resources/Assets.xcassets/Backgrounds/calloutBackupFailed.imageset/calloutBackupFailed@2x.png diff --git a/secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/Contents.json b/secant/Resources/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/Contents.json diff --git a/secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1.png b/secant/Resources/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1.png similarity index 100% rename from secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1.png rename to secant/Resources/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1.png diff --git a/secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1@2x.png b/secant/Resources/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1@2x.png similarity index 100% rename from secant/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1@2x.png rename to secant/Resources/Assets.xcassets/Backgrounds/calloutBackupFlow1.imageset/calloutBackupFlow1@2x.png diff --git a/secant/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/Contents.json b/secant/Resources/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/Contents.json diff --git a/secant/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/calloutBackupSucceeded.png b/secant/Resources/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/calloutBackupSucceeded.png similarity index 100% rename from secant/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/calloutBackupSucceeded.png rename to secant/Resources/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/calloutBackupSucceeded.png diff --git a/secant/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/calloutBackupSucceeded@2x.png b/secant/Resources/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/calloutBackupSucceeded@2x.png similarity index 100% rename from secant/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/calloutBackupSucceeded@2x.png rename to secant/Resources/Assets.xcassets/Backgrounds/calloutBackupSucceeded.imageset/calloutBackupSucceeded@2x.png diff --git a/secant/Assets.xcassets/Contents.json b/secant/Resources/Assets.xcassets/Contents.json similarity index 100% rename from secant/Assets.xcassets/Contents.json rename to secant/Resources/Assets.xcassets/Contents.json diff --git a/secant/Assets.xcassets/Icons/Contents.json b/secant/Resources/Assets.xcassets/Icons/Contents.json similarity index 100% rename from secant/Assets.xcassets/Icons/Contents.json rename to secant/Resources/Assets.xcassets/Icons/Contents.json diff --git a/secant/Assets.xcassets/Icons/bank.imageset/Contents.json b/secant/Resources/Assets.xcassets/Icons/bank.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Icons/bank.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Icons/bank.imageset/Contents.json diff --git a/secant/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme.png b/secant/Resources/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme.png similarity index 100% rename from secant/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme.png rename to secant/Resources/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme.png diff --git a/secant/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme@2x.png b/secant/Resources/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme@2x.png similarity index 100% rename from secant/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme@2x.png rename to secant/Resources/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme@2x.png diff --git a/secant/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme@3x.png b/secant/Resources/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme@3x.png similarity index 100% rename from secant/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme@3x.png rename to secant/Resources/Assets.xcassets/Icons/bank.imageset/iconbank_darktheme@3x.png diff --git a/secant/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme.png b/secant/Resources/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme.png similarity index 100% rename from secant/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme.png rename to secant/Resources/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme.png diff --git a/secant/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme@2x.png b/secant/Resources/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme@2x.png similarity index 100% rename from secant/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme@2x.png rename to secant/Resources/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme@2x.png diff --git a/secant/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme@3x.png b/secant/Resources/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme@3x.png similarity index 100% rename from secant/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme@3x.png rename to secant/Resources/Assets.xcassets/Icons/bank.imageset/iconbank_lighttheme@3x.png diff --git a/secant/Assets.xcassets/Icons/list.imageset/Contents.json b/secant/Resources/Assets.xcassets/Icons/list.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Icons/list.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Icons/list.imageset/Contents.json diff --git a/secant/Assets.xcassets/Icons/list.imageset/iconlist_darktheme.png b/secant/Resources/Assets.xcassets/Icons/list.imageset/iconlist_darktheme.png similarity index 100% rename from secant/Assets.xcassets/Icons/list.imageset/iconlist_darktheme.png rename to secant/Resources/Assets.xcassets/Icons/list.imageset/iconlist_darktheme.png diff --git a/secant/Assets.xcassets/Icons/list.imageset/iconlist_darktheme@2x.png b/secant/Resources/Assets.xcassets/Icons/list.imageset/iconlist_darktheme@2x.png similarity index 100% rename from secant/Assets.xcassets/Icons/list.imageset/iconlist_darktheme@2x.png rename to secant/Resources/Assets.xcassets/Icons/list.imageset/iconlist_darktheme@2x.png diff --git a/secant/Assets.xcassets/Icons/list.imageset/iconlist_darktheme@3x.png b/secant/Resources/Assets.xcassets/Icons/list.imageset/iconlist_darktheme@3x.png similarity index 100% rename from secant/Assets.xcassets/Icons/list.imageset/iconlist_darktheme@3x.png rename to secant/Resources/Assets.xcassets/Icons/list.imageset/iconlist_darktheme@3x.png diff --git a/secant/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme.png b/secant/Resources/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme.png similarity index 100% rename from secant/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme.png rename to secant/Resources/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme.png diff --git a/secant/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme@2x.png b/secant/Resources/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme@2x.png similarity index 100% rename from secant/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme@2x.png rename to secant/Resources/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme@2x.png diff --git a/secant/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme@3x.png b/secant/Resources/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme@3x.png similarity index 100% rename from secant/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme@3x.png rename to secant/Resources/Assets.xcassets/Icons/list.imageset/iconlist_lighttheme@3x.png diff --git a/secant/Assets.xcassets/Icons/profile.imageset/Contents.json b/secant/Resources/Assets.xcassets/Icons/profile.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Icons/profile.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Icons/profile.imageset/Contents.json diff --git a/secant/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme.png b/secant/Resources/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme.png similarity index 100% rename from secant/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme.png rename to secant/Resources/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme.png diff --git a/secant/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme@2x.png b/secant/Resources/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme@2x.png similarity index 100% rename from secant/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme@2x.png rename to secant/Resources/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme@2x.png diff --git a/secant/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme@3x.png b/secant/Resources/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme@3x.png similarity index 100% rename from secant/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme@3x.png rename to secant/Resources/Assets.xcassets/Icons/profile.imageset/iconperson_darktheme@3x.png diff --git a/secant/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme.png b/secant/Resources/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme.png similarity index 100% rename from secant/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme.png rename to secant/Resources/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme.png diff --git a/secant/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme@2x.png b/secant/Resources/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme@2x.png similarity index 100% rename from secant/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme@2x.png rename to secant/Resources/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme@2x.png diff --git a/secant/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme@3x.png b/secant/Resources/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme@3x.png similarity index 100% rename from secant/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme@3x.png rename to secant/Resources/Assets.xcassets/Icons/profile.imageset/iconperson_lighttheme@3x.png diff --git a/secant/Assets.xcassets/Icons/qr-code.imageset/Contents.json b/secant/Resources/Assets.xcassets/Icons/qr-code.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Icons/qr-code.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Icons/qr-code.imageset/Contents.json diff --git a/secant/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme.png b/secant/Resources/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme.png similarity index 100% rename from secant/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme.png rename to secant/Resources/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme.png diff --git a/secant/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme@2x.png b/secant/Resources/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme@2x.png similarity index 100% rename from secant/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme@2x.png rename to secant/Resources/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme@2x.png diff --git a/secant/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme@3x.png b/secant/Resources/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme@3x.png similarity index 100% rename from secant/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme@3x.png rename to secant/Resources/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme@3x.png diff --git a/secant/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme.png b/secant/Resources/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme.png similarity index 100% rename from secant/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme.png rename to secant/Resources/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme.png diff --git a/secant/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@2x.png b/secant/Resources/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@2x.png similarity index 100% rename from secant/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@2x.png rename to secant/Resources/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@2x.png diff --git a/secant/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@3x.png b/secant/Resources/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@3x.png similarity index 100% rename from secant/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@3x.png rename to secant/Resources/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@3x.png diff --git a/secant/Assets.xcassets/Icons/shield.imageset/Contents.json b/secant/Resources/Assets.xcassets/Icons/shield.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Icons/shield.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Icons/shield.imageset/Contents.json diff --git a/secant/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme.png b/secant/Resources/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme.png similarity index 100% rename from secant/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme.png rename to secant/Resources/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme.png diff --git a/secant/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme@2x.png b/secant/Resources/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme@2x.png similarity index 100% rename from secant/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme@2x.png rename to secant/Resources/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme@2x.png diff --git a/secant/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme@3x.png b/secant/Resources/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme@3x.png similarity index 100% rename from secant/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme@3x.png rename to secant/Resources/Assets.xcassets/Icons/shield.imageset/iconshield_darktheme@3x.png diff --git a/secant/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme.png b/secant/Resources/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme.png similarity index 100% rename from secant/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme.png rename to secant/Resources/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme.png diff --git a/secant/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme@2x.png b/secant/Resources/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme@2x.png similarity index 100% rename from secant/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme@2x.png rename to secant/Resources/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme@2x.png diff --git a/secant/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme@3x.png b/secant/Resources/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme@3x.png similarity index 100% rename from secant/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme@3x.png rename to secant/Resources/Assets.xcassets/Icons/shield.imageset/iconshield_lighttheme@3x.png diff --git a/secant/Assets.xcassets/Icons/swap.imageset/Contents.json b/secant/Resources/Assets.xcassets/Icons/swap.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/Icons/swap.imageset/Contents.json rename to secant/Resources/Assets.xcassets/Icons/swap.imageset/Contents.json diff --git a/secant/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme.png b/secant/Resources/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme.png similarity index 100% rename from secant/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme.png rename to secant/Resources/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme.png diff --git a/secant/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@2x.png b/secant/Resources/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@2x.png similarity index 100% rename from secant/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@2x.png rename to secant/Resources/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@2x.png diff --git a/secant/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@3x.png b/secant/Resources/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@3x.png similarity index 100% rename from secant/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@3x.png rename to secant/Resources/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@3x.png diff --git a/secant/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme.png b/secant/Resources/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme.png similarity index 100% rename from secant/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme.png rename to secant/Resources/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme.png diff --git a/secant/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme@2x.png b/secant/Resources/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme@2x.png similarity index 100% rename from secant/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme@2x.png rename to secant/Resources/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme@2x.png diff --git a/secant/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme@3x.png b/secant/Resources/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme@3x.png similarity index 100% rename from secant/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme@3x.png rename to secant/Resources/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme@3x.png diff --git a/secant/Assets.xcassets/WelcomeScreenLogo.imageset/Contents.json b/secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/Contents.json similarity index 100% rename from secant/Assets.xcassets/WelcomeScreenLogo.imageset/Contents.json rename to secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/Contents.json diff --git a/secant/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo.png b/secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo.png similarity index 100% rename from secant/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo.png rename to secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo.png diff --git a/secant/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo@2x.png b/secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo@2x.png similarity index 100% rename from secant/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo@2x.png rename to secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo@2x.png diff --git a/secant/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo@3x.png b/secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo@3x.png similarity index 100% rename from secant/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo@3x.png rename to secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/DarkLogo@3x.png diff --git a/secant/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo.png b/secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo.png similarity index 100% rename from secant/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo.png rename to secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo.png diff --git a/secant/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo@2x.png b/secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo@2x.png similarity index 100% rename from secant/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo@2x.png rename to secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo@2x.png diff --git a/secant/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo@3x.png b/secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo@3x.png similarity index 100% rename from secant/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo@3x.png rename to secant/Resources/Assets.xcassets/WelcomeScreenLogo.imageset/LightLogo@3x.png diff --git a/secant/Colors.xcassets/BackgroundColors/Contents.json b/secant/Resources/Colors.xcassets/BackgroundColors/Contents.json similarity index 100% rename from secant/Colors.xcassets/BackgroundColors/Contents.json rename to secant/Resources/Colors.xcassets/BackgroundColors/Contents.json diff --git a/secant/Colors.xcassets/BackgroundColors/numberedChip.colorset/Contents.json b/secant/Resources/Colors.xcassets/BackgroundColors/numberedChip.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/BackgroundColors/numberedChip.colorset/Contents.json rename to secant/Resources/Colors.xcassets/BackgroundColors/numberedChip.colorset/Contents.json diff --git a/secant/Colors.xcassets/BackgroundColors/phraseGridDarkGray.colorset/Contents.json b/secant/Resources/Colors.xcassets/BackgroundColors/phraseGridDarkGray.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/BackgroundColors/phraseGridDarkGray.colorset/Contents.json rename to secant/Resources/Colors.xcassets/BackgroundColors/phraseGridDarkGray.colorset/Contents.json diff --git a/secant/Colors.xcassets/BackgroundColors/red.colorset/Contents.json b/secant/Resources/Colors.xcassets/BackgroundColors/red.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/BackgroundColors/red.colorset/Contents.json rename to secant/Resources/Colors.xcassets/BackgroundColors/red.colorset/Contents.json diff --git a/secant/Colors.xcassets/BackgroundColors/staticWelcomeScreen.colorset/Contents.json b/secant/Resources/Colors.xcassets/BackgroundColors/staticWelcomeScreen.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/BackgroundColors/staticWelcomeScreen.colorset/Contents.json rename to secant/Resources/Colors.xcassets/BackgroundColors/staticWelcomeScreen.colorset/Contents.json diff --git a/secant/Colors.xcassets/Buttons/ActiveButton.colorset/Contents.json b/secant/Resources/Colors.xcassets/Buttons/ActiveButton.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/ActiveButton.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/ActiveButton.colorset/Contents.json diff --git a/secant/Colors.xcassets/Buttons/ActiveButtonDisabled.colorset/Contents.json b/secant/Resources/Colors.xcassets/Buttons/ActiveButtonDisabled.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/ActiveButtonDisabled.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/ActiveButtonDisabled.colorset/Contents.json diff --git a/secant/Colors.xcassets/Buttons/ActiveButtonPressed.colorset/Contents.json b/secant/Resources/Colors.xcassets/Buttons/ActiveButtonPressed.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/ActiveButtonPressed.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/ActiveButtonPressed.colorset/Contents.json diff --git a/secant/Colors.xcassets/Buttons/ButtonsTitleShadow.colorset/Contents.json b/secant/Resources/Colors.xcassets/Buttons/ButtonsTitleShadow.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/ButtonsTitleShadow.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/ButtonsTitleShadow.colorset/Contents.json diff --git a/secant/Colors.xcassets/Buttons/Contents.json b/secant/Resources/Colors.xcassets/Buttons/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/Contents.json diff --git a/secant/Colors.xcassets/Buttons/NeumorphicButtonDarkSide.colorset/Contents.json b/secant/Resources/Colors.xcassets/Buttons/NeumorphicButtonDarkSide.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/NeumorphicButtonDarkSide.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/NeumorphicButtonDarkSide.colorset/Contents.json diff --git a/secant/Colors.xcassets/Buttons/NeumorphicButtonLightSide.colorset/Contents.json b/secant/Resources/Colors.xcassets/Buttons/NeumorphicButtonLightSide.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/NeumorphicButtonLightSide.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/NeumorphicButtonLightSide.colorset/Contents.json diff --git a/secant/Colors.xcassets/Buttons/OnboardingNavigation.colorset/Contents.json b/secant/Resources/Colors.xcassets/Buttons/OnboardingNavigation.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/OnboardingNavigation.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/OnboardingNavigation.colorset/Contents.json diff --git a/secant/Colors.xcassets/Buttons/OnboardingNavigationPressed.colorset/Contents.json b/secant/Resources/Colors.xcassets/Buttons/OnboardingNavigationPressed.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/OnboardingNavigationPressed.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/OnboardingNavigationPressed.colorset/Contents.json diff --git a/secant/Colors.xcassets/Buttons/PrimaryButton.colorset/Contents.json b/secant/Resources/Colors.xcassets/Buttons/PrimaryButton.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/PrimaryButton.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/PrimaryButton.colorset/Contents.json diff --git a/secant/Colors.xcassets/Buttons/PrimaryButtonDisabled.colorset/Contents.json b/secant/Resources/Colors.xcassets/Buttons/PrimaryButtonDisabled.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/PrimaryButtonDisabled.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/PrimaryButtonDisabled.colorset/Contents.json diff --git a/secant/Colors.xcassets/Buttons/PrimaryButtonPressed.colorset/Contents.json b/secant/Resources/Colors.xcassets/Buttons/PrimaryButtonPressed.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/PrimaryButtonPressed.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/PrimaryButtonPressed.colorset/Contents.json diff --git a/secant/Colors.xcassets/Buttons/SecondaryButton.colorset/Contents.json b/secant/Resources/Colors.xcassets/Buttons/SecondaryButton.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/SecondaryButton.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/SecondaryButton.colorset/Contents.json diff --git a/secant/Colors.xcassets/Buttons/SecondaryButtonPressed.colorset/Contents.json b/secant/Resources/Colors.xcassets/Buttons/SecondaryButtonPressed.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Buttons/SecondaryButtonPressed.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Buttons/SecondaryButtonPressed.colorset/Contents.json diff --git a/secant/Colors.xcassets/Contents.json b/secant/Resources/Colors.xcassets/Contents.json similarity index 100% rename from secant/Colors.xcassets/Contents.json rename to secant/Resources/Colors.xcassets/Contents.json diff --git a/secant/Colors.xcassets/Cursor/Bar.colorset/Contents.json b/secant/Resources/Colors.xcassets/Cursor/Bar.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Cursor/Bar.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Cursor/Bar.colorset/Contents.json diff --git a/secant/Colors.xcassets/Cursor/Contents.json b/secant/Resources/Colors.xcassets/Cursor/Contents.json similarity index 100% rename from secant/Colors.xcassets/Cursor/Contents.json rename to secant/Resources/Colors.xcassets/Cursor/Contents.json diff --git a/secant/Colors.xcassets/Onboarding/BadgeShadow.colorset/Contents.json b/secant/Resources/Colors.xcassets/Onboarding/BadgeShadow.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Onboarding/BadgeShadow.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Onboarding/BadgeShadow.colorset/Contents.json diff --git a/secant/Colors.xcassets/Onboarding/CircularFrameDarkOutlineGradientEnd.colorset/Contents.json b/secant/Resources/Colors.xcassets/Onboarding/CircularFrameDarkOutlineGradientEnd.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Onboarding/CircularFrameDarkOutlineGradientEnd.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Onboarding/CircularFrameDarkOutlineGradientEnd.colorset/Contents.json diff --git a/secant/Colors.xcassets/Onboarding/CircularFrameDarkOutlineGradientStart.colorset/Contents.json b/secant/Resources/Colors.xcassets/Onboarding/CircularFrameDarkOutlineGradientStart.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Onboarding/CircularFrameDarkOutlineGradientStart.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Onboarding/CircularFrameDarkOutlineGradientStart.colorset/Contents.json diff --git a/secant/Colors.xcassets/Onboarding/CircularFrameGradientEnd.colorset/Contents.json b/secant/Resources/Colors.xcassets/Onboarding/CircularFrameGradientEnd.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Onboarding/CircularFrameGradientEnd.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Onboarding/CircularFrameGradientEnd.colorset/Contents.json diff --git a/secant/Colors.xcassets/Onboarding/CircularFrameGradientStart.colorset/Contents.json b/secant/Resources/Colors.xcassets/Onboarding/CircularFrameGradientStart.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Onboarding/CircularFrameGradientStart.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Onboarding/CircularFrameGradientStart.colorset/Contents.json diff --git a/secant/Colors.xcassets/Onboarding/Contents.json b/secant/Resources/Colors.xcassets/Onboarding/Contents.json similarity index 100% rename from secant/Colors.xcassets/Onboarding/Contents.json rename to secant/Resources/Colors.xcassets/Onboarding/Contents.json diff --git a/secant/Colors.xcassets/Onboarding/NavigationButtonDisabled.colorset/Contents.json b/secant/Resources/Colors.xcassets/Onboarding/NavigationButtonDisabled.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Onboarding/NavigationButtonDisabled.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Onboarding/NavigationButtonDisabled.colorset/Contents.json diff --git a/secant/Colors.xcassets/Onboarding/NavigationButtonEnabled.colorset/Contents.json b/secant/Resources/Colors.xcassets/Onboarding/NavigationButtonEnabled.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Onboarding/NavigationButtonEnabled.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Onboarding/NavigationButtonEnabled.colorset/Contents.json diff --git a/secant/Colors.xcassets/Onboarding/NeumorphicDarkSide.colorset/Contents.json b/secant/Resources/Colors.xcassets/Onboarding/NeumorphicDarkSide.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Onboarding/NeumorphicDarkSide.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Onboarding/NeumorphicDarkSide.colorset/Contents.json diff --git a/secant/Colors.xcassets/Onboarding/NeumorphicLightSide.colorset/Contents.json b/secant/Resources/Colors.xcassets/Onboarding/NeumorphicLightSide.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Onboarding/NeumorphicLightSide.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Onboarding/NeumorphicLightSide.colorset/Contents.json diff --git a/secant/Colors.xcassets/Onboarding/badgeBackground.colorset/Contents.json b/secant/Resources/Colors.xcassets/Onboarding/badgeBackground.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Onboarding/badgeBackground.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Onboarding/badgeBackground.colorset/Contents.json diff --git a/secant/Colors.xcassets/ProgressIndicator/Contents.json b/secant/Resources/Colors.xcassets/ProgressIndicator/Contents.json similarity index 100% rename from secant/Colors.xcassets/ProgressIndicator/Contents.json rename to secant/Resources/Colors.xcassets/ProgressIndicator/Contents.json diff --git a/secant/Colors.xcassets/ProgressIndicator/GradientLeft.colorset/Contents.json b/secant/Resources/Colors.xcassets/ProgressIndicator/GradientLeft.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ProgressIndicator/GradientLeft.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ProgressIndicator/GradientLeft.colorset/Contents.json diff --git a/secant/Colors.xcassets/ProgressIndicator/GradientRight.colorset/Contents.json b/secant/Resources/Colors.xcassets/ProgressIndicator/GradientRight.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ProgressIndicator/GradientRight.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ProgressIndicator/GradientRight.colorset/Contents.json diff --git a/secant/Colors.xcassets/ProgressIndicator/NegativeSpace.colorset/Contents.json b/secant/Resources/Colors.xcassets/ProgressIndicator/NegativeSpace.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ProgressIndicator/NegativeSpace.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ProgressIndicator/NegativeSpace.colorset/Contents.json diff --git a/secant/Colors.xcassets/ScreenBackground/Contents.json b/secant/Resources/Colors.xcassets/ScreenBackground/Contents.json similarity index 100% rename from secant/Colors.xcassets/ScreenBackground/Contents.json rename to secant/Resources/Colors.xcassets/ScreenBackground/Contents.json diff --git a/secant/Colors.xcassets/ScreenBackground/gradientEnd.colorset/Contents.json b/secant/Resources/Colors.xcassets/ScreenBackground/gradientEnd.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ScreenBackground/gradientEnd.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ScreenBackground/gradientEnd.colorset/Contents.json diff --git a/secant/Colors.xcassets/ScreenBackground/gradientStart.colorset/Contents.json b/secant/Resources/Colors.xcassets/ScreenBackground/gradientStart.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ScreenBackground/gradientStart.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ScreenBackground/gradientStart.colorset/Contents.json diff --git a/secant/Colors.xcassets/ScreenBackground/greenGradientEnd.colorset/Contents.json b/secant/Resources/Colors.xcassets/ScreenBackground/greenGradientEnd.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ScreenBackground/greenGradientEnd.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ScreenBackground/greenGradientEnd.colorset/Contents.json diff --git a/secant/Colors.xcassets/ScreenBackground/greenGradientStart.colorset/Contents.json b/secant/Resources/Colors.xcassets/ScreenBackground/greenGradientStart.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ScreenBackground/greenGradientStart.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ScreenBackground/greenGradientStart.colorset/Contents.json diff --git a/secant/Colors.xcassets/ScreenBackground/redGradientEnd.colorset/Contents.json b/secant/Resources/Colors.xcassets/ScreenBackground/redGradientEnd.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ScreenBackground/redGradientEnd.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ScreenBackground/redGradientEnd.colorset/Contents.json diff --git a/secant/Colors.xcassets/ScreenBackground/redGradientStart.colorset/Contents.json b/secant/Resources/Colors.xcassets/ScreenBackground/redGradientStart.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ScreenBackground/redGradientStart.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ScreenBackground/redGradientStart.colorset/Contents.json diff --git a/secant/Colors.xcassets/Shadow/Contents.json b/secant/Resources/Colors.xcassets/Shadow/Contents.json similarity index 100% rename from secant/Colors.xcassets/Shadow/Contents.json rename to secant/Resources/Colors.xcassets/Shadow/Contents.json diff --git a/secant/Colors.xcassets/Shadow/drawerShadow.colorset/Contents.json b/secant/Resources/Colors.xcassets/Shadow/drawerShadow.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Shadow/drawerShadow.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Shadow/drawerShadow.colorset/Contents.json diff --git a/secant/Colors.xcassets/Shadow/emptyChipInnerShadow.colorset/Contents.json b/secant/Resources/Colors.xcassets/Shadow/emptyChipInnerShadow.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Shadow/emptyChipInnerShadow.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Shadow/emptyChipInnerShadow.colorset/Contents.json diff --git a/secant/Colors.xcassets/Shadow/numberedTextShadow.colorset/Contents.json b/secant/Resources/Colors.xcassets/Shadow/numberedTextShadow.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Shadow/numberedTextShadow.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Shadow/numberedTextShadow.colorset/Contents.json diff --git a/secant/Colors.xcassets/Text/ActiveButtonText.colorset/Contents.json b/secant/Resources/Colors.xcassets/Text/ActiveButtonText.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Text/ActiveButtonText.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Text/ActiveButtonText.colorset/Contents.json diff --git a/secant/Colors.xcassets/Text/Body.colorset/Contents.json b/secant/Resources/Colors.xcassets/Text/Body.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Text/Body.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Text/Body.colorset/Contents.json diff --git a/secant/Colors.xcassets/Text/Button.colorset/Contents.json b/secant/Resources/Colors.xcassets/Text/Button.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Text/Button.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Text/Button.colorset/Contents.json diff --git a/secant/Colors.xcassets/Text/Contents.json b/secant/Resources/Colors.xcassets/Text/Contents.json similarity index 100% rename from secant/Colors.xcassets/Text/Contents.json rename to secant/Resources/Colors.xcassets/Text/Contents.json diff --git a/secant/Colors.xcassets/Text/Heading.colorset/Contents.json b/secant/Resources/Colors.xcassets/Text/Heading.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Text/Heading.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Text/Heading.colorset/Contents.json diff --git a/secant/Colors.xcassets/Text/ImportSeedEditor.colorset/Contents.json b/secant/Resources/Colors.xcassets/Text/ImportSeedEditor.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Text/ImportSeedEditor.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Text/ImportSeedEditor.colorset/Contents.json diff --git a/secant/Colors.xcassets/Text/Medium.colorset/Contents.json b/secant/Resources/Colors.xcassets/Text/Medium.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Text/Medium.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Text/Medium.colorset/Contents.json diff --git a/secant/Colors.xcassets/Text/Regular.colorset/Contents.json b/secant/Resources/Colors.xcassets/Text/Regular.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Text/Regular.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Text/Regular.colorset/Contents.json diff --git a/secant/Colors.xcassets/Text/SecondaryButtonText.colorset/Contents.json b/secant/Resources/Colors.xcassets/Text/SecondaryButtonText.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Text/SecondaryButtonText.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Text/SecondaryButtonText.colorset/Contents.json diff --git a/secant/Colors.xcassets/Text/TitleText.colorset/Contents.json b/secant/Resources/Colors.xcassets/Text/TitleText.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Text/TitleText.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Text/TitleText.colorset/Contents.json diff --git a/secant/Colors.xcassets/Text/captionText.colorset/Contents.json b/secant/Resources/Colors.xcassets/Text/captionText.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Text/captionText.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Text/captionText.colorset/Contents.json diff --git a/secant/Colors.xcassets/Text/captionTextShadow.colorset/Contents.json b/secant/Resources/Colors.xcassets/Text/captionTextShadow.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Text/captionTextShadow.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Text/captionTextShadow.colorset/Contents.json diff --git a/secant/Colors.xcassets/Text/highlightedSuperscriptText.colorset/Contents.json b/secant/Resources/Colors.xcassets/Text/highlightedSuperscriptText.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/Text/highlightedSuperscriptText.colorset/Contents.json rename to secant/Resources/Colors.xcassets/Text/highlightedSuperscriptText.colorset/Contents.json diff --git a/secant/Colors.xcassets/TextField/Contents.json b/secant/Resources/Colors.xcassets/TextField/Contents.json similarity index 100% rename from secant/Colors.xcassets/TextField/Contents.json rename to secant/Resources/Colors.xcassets/TextField/Contents.json diff --git a/secant/Colors.xcassets/TextField/TitleAccessoryButton.colorset/Contents.json b/secant/Resources/Colors.xcassets/TextField/TitleAccessoryButton.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/TextField/TitleAccessoryButton.colorset/Contents.json rename to secant/Resources/Colors.xcassets/TextField/TitleAccessoryButton.colorset/Contents.json diff --git a/secant/Colors.xcassets/TextField/TitleAccessoryButtonPressed.colorset/Contents.json b/secant/Resources/Colors.xcassets/TextField/TitleAccessoryButtonPressed.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/TextField/TitleAccessoryButtonPressed.colorset/Contents.json rename to secant/Resources/Colors.xcassets/TextField/TitleAccessoryButtonPressed.colorset/Contents.json diff --git a/secant/Colors.xcassets/TextField/Underline/Contents.json b/secant/Resources/Colors.xcassets/TextField/Underline/Contents.json similarity index 100% rename from secant/Colors.xcassets/TextField/Underline/Contents.json rename to secant/Resources/Colors.xcassets/TextField/Underline/Contents.json diff --git a/secant/Colors.xcassets/TextField/Underline/Gray.colorset/Contents.json b/secant/Resources/Colors.xcassets/TextField/Underline/Gray.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/TextField/Underline/Gray.colorset/Contents.json rename to secant/Resources/Colors.xcassets/TextField/Underline/Gray.colorset/Contents.json diff --git a/secant/Colors.xcassets/TextField/Underline/Purple.colorset/Contents.json b/secant/Resources/Colors.xcassets/TextField/Underline/Purple.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/TextField/Underline/Purple.colorset/Contents.json rename to secant/Resources/Colors.xcassets/TextField/Underline/Purple.colorset/Contents.json diff --git a/secant/Colors.xcassets/ZcashBadge/Contents.json b/secant/Resources/Colors.xcassets/ZcashBadge/Contents.json similarity index 100% rename from secant/Colors.xcassets/ZcashBadge/Contents.json rename to secant/Resources/Colors.xcassets/ZcashBadge/Contents.json diff --git a/secant/Colors.xcassets/ZcashBadge/ZcashLogoFill.colorset/Contents.json b/secant/Resources/Colors.xcassets/ZcashBadge/ZcashLogoFill.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ZcashBadge/ZcashLogoFill.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ZcashBadge/ZcashLogoFill.colorset/Contents.json diff --git a/secant/Colors.xcassets/ZcashBadge/innerCircle.colorset/Contents.json b/secant/Resources/Colors.xcassets/ZcashBadge/innerCircle.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ZcashBadge/innerCircle.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ZcashBadge/innerCircle.colorset/Contents.json diff --git a/secant/Colors.xcassets/ZcashBadge/outerRingGradientEnd.colorset/Contents.json b/secant/Resources/Colors.xcassets/ZcashBadge/outerRingGradientEnd.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ZcashBadge/outerRingGradientEnd.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ZcashBadge/outerRingGradientEnd.colorset/Contents.json diff --git a/secant/Colors.xcassets/ZcashBadge/outerRingGradientStart.colorset/Contents.json b/secant/Resources/Colors.xcassets/ZcashBadge/outerRingGradientStart.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ZcashBadge/outerRingGradientStart.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ZcashBadge/outerRingGradientStart.colorset/Contents.json diff --git a/secant/Colors.xcassets/ZcashBadge/shadowColor.colorset/Contents.json b/secant/Resources/Colors.xcassets/ZcashBadge/shadowColor.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ZcashBadge/shadowColor.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ZcashBadge/shadowColor.colorset/Contents.json diff --git a/secant/Colors.xcassets/ZcashBadge/thickRing.colorset/Contents.json b/secant/Resources/Colors.xcassets/ZcashBadge/thickRing.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ZcashBadge/thickRing.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ZcashBadge/thickRing.colorset/Contents.json diff --git a/secant/Colors.xcassets/ZcashBadge/thinRing.colorset/Contents.json b/secant/Resources/Colors.xcassets/ZcashBadge/thinRing.colorset/Contents.json similarity index 100% rename from secant/Colors.xcassets/ZcashBadge/thinRing.colorset/Contents.json rename to secant/Resources/Colors.xcassets/ZcashBadge/thinRing.colorset/Contents.json diff --git a/secant/Fonts/Roboto/LICENSE.txt b/secant/Resources/Fonts/Roboto/LICENSE.txt similarity index 100% rename from secant/Fonts/Roboto/LICENSE.txt rename to secant/Resources/Fonts/Roboto/LICENSE.txt diff --git a/secant/Fonts/Roboto/Roboto-Black.ttf b/secant/Resources/Fonts/Roboto/Roboto-Black.ttf similarity index 100% rename from secant/Fonts/Roboto/Roboto-Black.ttf rename to secant/Resources/Fonts/Roboto/Roboto-Black.ttf diff --git a/secant/Fonts/Roboto/Roboto-BlackItalic.ttf b/secant/Resources/Fonts/Roboto/Roboto-BlackItalic.ttf similarity index 100% rename from secant/Fonts/Roboto/Roboto-BlackItalic.ttf rename to secant/Resources/Fonts/Roboto/Roboto-BlackItalic.ttf diff --git a/secant/Fonts/Roboto/Roboto-Bold.ttf b/secant/Resources/Fonts/Roboto/Roboto-Bold.ttf similarity index 100% rename from secant/Fonts/Roboto/Roboto-Bold.ttf rename to secant/Resources/Fonts/Roboto/Roboto-Bold.ttf diff --git a/secant/Fonts/Roboto/Roboto-BoldItalic.ttf b/secant/Resources/Fonts/Roboto/Roboto-BoldItalic.ttf similarity index 100% rename from secant/Fonts/Roboto/Roboto-BoldItalic.ttf rename to secant/Resources/Fonts/Roboto/Roboto-BoldItalic.ttf diff --git a/secant/Fonts/Roboto/Roboto-Italic.ttf b/secant/Resources/Fonts/Roboto/Roboto-Italic.ttf similarity index 100% rename from secant/Fonts/Roboto/Roboto-Italic.ttf rename to secant/Resources/Fonts/Roboto/Roboto-Italic.ttf diff --git a/secant/Fonts/Roboto/Roboto-Light.ttf b/secant/Resources/Fonts/Roboto/Roboto-Light.ttf similarity index 100% rename from secant/Fonts/Roboto/Roboto-Light.ttf rename to secant/Resources/Fonts/Roboto/Roboto-Light.ttf diff --git a/secant/Fonts/Roboto/Roboto-LightItalic.ttf b/secant/Resources/Fonts/Roboto/Roboto-LightItalic.ttf similarity index 100% rename from secant/Fonts/Roboto/Roboto-LightItalic.ttf rename to secant/Resources/Fonts/Roboto/Roboto-LightItalic.ttf diff --git a/secant/Fonts/Roboto/Roboto-Medium.ttf b/secant/Resources/Fonts/Roboto/Roboto-Medium.ttf similarity index 100% rename from secant/Fonts/Roboto/Roboto-Medium.ttf rename to secant/Resources/Fonts/Roboto/Roboto-Medium.ttf diff --git a/secant/Fonts/Roboto/Roboto-MediumItalic.ttf b/secant/Resources/Fonts/Roboto/Roboto-MediumItalic.ttf similarity index 100% rename from secant/Fonts/Roboto/Roboto-MediumItalic.ttf rename to secant/Resources/Fonts/Roboto/Roboto-MediumItalic.ttf diff --git a/secant/Fonts/Roboto/Roboto-Regular.ttf b/secant/Resources/Fonts/Roboto/Roboto-Regular.ttf similarity index 100% rename from secant/Fonts/Roboto/Roboto-Regular.ttf rename to secant/Resources/Fonts/Roboto/Roboto-Regular.ttf diff --git a/secant/Fonts/Roboto/Roboto-Thin.ttf b/secant/Resources/Fonts/Roboto/Roboto-Thin.ttf similarity index 100% rename from secant/Fonts/Roboto/Roboto-Thin.ttf rename to secant/Resources/Fonts/Roboto/Roboto-Thin.ttf diff --git a/secant/Fonts/Roboto/Roboto-ThinItalic.ttf b/secant/Resources/Fonts/Roboto/Roboto-ThinItalic.ttf similarity index 100% rename from secant/Fonts/Roboto/Roboto-ThinItalic.ttf rename to secant/Resources/Fonts/Roboto/Roboto-ThinItalic.ttf diff --git a/secant/Fonts/Rubik/Rubik-Italic-VariableFont_wght.ttf b/secant/Resources/Fonts/Rubik/Rubik-Italic-VariableFont_wght.ttf similarity index 100% rename from secant/Fonts/Rubik/Rubik-Italic-VariableFont_wght.ttf rename to secant/Resources/Fonts/Rubik/Rubik-Italic-VariableFont_wght.ttf diff --git a/secant/Fonts/Rubik/Rubik-VariableFont_wght.ttf b/secant/Resources/Fonts/Rubik/Rubik-VariableFont_wght.ttf similarity index 100% rename from secant/Fonts/Rubik/Rubik-VariableFont_wght.ttf rename to secant/Resources/Fonts/Rubik/Rubik-VariableFont_wght.ttf diff --git a/secant/Fonts/Zboto.otf b/secant/Resources/Fonts/Zboto.otf similarity index 100% rename from secant/Fonts/Zboto.otf rename to secant/Resources/Fonts/Zboto.otf diff --git a/secant/Generated/Fonts+Generated.swift b/secant/Resources/Generated/Fonts+Generated.swift similarity index 100% rename from secant/Generated/Fonts+Generated.swift rename to secant/Resources/Generated/Fonts+Generated.swift diff --git a/secant/Generated/XCAssets+Generated.swift b/secant/Resources/Generated/XCAssets+Generated.swift similarity index 100% rename from secant/Generated/XCAssets+Generated.swift rename to secant/Resources/Generated/XCAssets+Generated.swift diff --git a/secant/Localizable.strings b/secant/Resources/Localizable.strings similarity index 100% rename from secant/Localizable.strings rename to secant/Resources/Localizable.strings diff --git a/secant/Stubs/MockServices.swift b/secant/Stubs/MockServices.swift deleted file mode 100644 index 3cf5922..0000000 --- a/secant/Stubs/MockServices.swift +++ /dev/null @@ -1,138 +0,0 @@ -// -// MockServices.swift -// secant -// -// Created by Francisco Gindre on 8/6/21. -// -// TODO: Move this to different Target when real functionality is developed. -import Foundation - -class MockServices: Services { - var networkProvider: ZcashNetworkProvider = MockNetworkProvider() - - var seedHandler: MnemonicSeedPhraseProvider = .mock - - var keyStorage: KeyStoring = MockKeyStoring() -} - -class MockNetworkProvider: ZcashNetworkProvider { - func currentNetwork() -> ZcashNetwork { - ZcashMainnet() - } -} - -class KeysPresentStub: KeyStoring { - init(returnBlock: @escaping () throws -> Bool) { - self.returnBlock = returnBlock - } - var returnBlock: () throws -> Bool - var called = false - func areKeysPresent() throws -> Bool { - called = true - return try returnBlock() - } - - var birthday: BlockHeight? - var phrase: String? - func importBirthday(_ height: BlockHeight) throws { - guard birthday == nil else { - throw KeyStoringError.alreadyImported - } - birthday = height - } - - func exportBirthday() throws -> BlockHeight { - guard let birthday = birthday else { - throw KeyStoringError.uninitializedWallet - } - return birthday - } - - func importPhrase(bip39 phrase: String) throws { - guard self.phrase == nil else { - throw KeyStoringError.alreadyImported - } - self.phrase = phrase - } - - func exportPhrase() throws -> String { - guard let phrase = self.phrase else { - throw KeyStoringError.uninitializedWallet - } - return phrase - } - - var keysPresent: Bool { - return self.phrase != nil && self.birthday != nil - } - - func nukePhrase() { - self.phrase = nil - } - - func nukeBirthday() { - self.birthday = nil - } - - func nukeWallet() { - nukePhrase() - nukeBirthday() - } -} -class MockKeyStoring: KeyStoring { - var birthday: BlockHeight? - var phrase: String? - - var keysPresent: Bool { - return self.phrase != nil && self.birthday != nil - } - - func areKeysPresent() throws -> Bool { - false - } - - func importBirthday(_ height: BlockHeight) throws { - guard birthday == nil else { - throw KeyStoringError.alreadyImported - } - - birthday = height - } - - func exportBirthday() throws -> BlockHeight { - guard let birthday = birthday else { - throw KeyStoringError.uninitializedWallet - } - - return birthday - } - - func importPhrase(bip39 phrase: String) throws { - guard self.phrase == nil else { - throw KeyStoringError.alreadyImported - } - - self.phrase = phrase - } - - func exportPhrase() throws -> String { - guard let phrase = self.phrase else { - throw KeyStoringError.uninitializedWallet - } - - return phrase - } - - func nukePhrase() { - self.phrase = nil - } - - func nukeBirthday() { - self.birthday = nil - } - - func nukeWallet() { - nukePhrase() - nukeBirthday() - } -} diff --git a/secant/UIComponents/Backgrounds/ScreenBackground.swift b/secant/UI Components/Backgrounds/ScreenBackground.swift similarity index 100% rename from secant/UIComponents/Backgrounds/ScreenBackground.swift rename to secant/UI Components/Backgrounds/ScreenBackground.swift diff --git a/secant/UIComponents/Buttons/ActiveButton.swift b/secant/UI Components/Buttons/ActiveButton.swift similarity index 100% rename from secant/UIComponents/Buttons/ActiveButton.swift rename to secant/UI Components/Buttons/ActiveButton.swift diff --git a/secant/UIComponents/Buttons/NavigationButtonStyle.swift b/secant/UI Components/Buttons/NavigationButtonStyle.swift similarity index 100% rename from secant/UIComponents/Buttons/NavigationButtonStyle.swift rename to secant/UI Components/Buttons/NavigationButtonStyle.swift diff --git a/secant/UIComponents/Buttons/NeumorphicDesignModifier.swift b/secant/UI Components/Buttons/NeumorphicDesignModifier.swift similarity index 100% rename from secant/UIComponents/Buttons/NeumorphicDesignModifier.swift rename to secant/UI Components/Buttons/NeumorphicDesignModifier.swift diff --git a/secant/UIComponents/Buttons/PrimaryButton.swift b/secant/UI Components/Buttons/PrimaryButton.swift similarity index 100% rename from secant/UIComponents/Buttons/PrimaryButton.swift rename to secant/UI Components/Buttons/PrimaryButton.swift diff --git a/secant/UIComponents/Buttons/SecondaryButton.swift b/secant/UI Components/Buttons/SecondaryButton.swift similarity index 100% rename from secant/UIComponents/Buttons/SecondaryButton.swift rename to secant/UI Components/Buttons/SecondaryButton.swift diff --git a/secant/UIComponents/Buttons/StandardButtonStyle.swift b/secant/UI Components/Buttons/StandardButtonStyle.swift similarity index 100% rename from secant/UIComponents/Buttons/StandardButtonStyle.swift rename to secant/UI Components/Buttons/StandardButtonStyle.swift diff --git a/secant/UIComponents/Chips/ColoredChip.swift b/secant/UI Components/Chips/ColoredChip.swift similarity index 100% rename from secant/UIComponents/Chips/ColoredChip.swift rename to secant/UI Components/Chips/ColoredChip.swift diff --git a/secant/UIComponents/Chips/EmptyChip.swift b/secant/UI Components/Chips/EmptyChip.swift similarity index 100% rename from secant/UIComponents/Chips/EmptyChip.swift rename to secant/UI Components/Chips/EmptyChip.swift diff --git a/secant/UIComponents/Chips/EnumeratedChip.swift b/secant/UI Components/Chips/EnumeratedChip.swift similarity index 100% rename from secant/UIComponents/Chips/EnumeratedChip.swift rename to secant/UI Components/Chips/EnumeratedChip.swift diff --git a/secant/UIComponents/Chips/PhraseChip.swift b/secant/UI Components/Chips/PhraseChip.swift similarity index 100% rename from secant/UIComponents/Chips/PhraseChip.swift rename to secant/UI Components/Chips/PhraseChip.swift diff --git a/secant/Features/BackupFlow/Views/WordChipGrid.swift b/secant/UI Components/Chips/WordChipGrid.swift similarity index 100% rename from secant/Features/BackupFlow/Views/WordChipGrid.swift rename to secant/UI Components/Chips/WordChipGrid.swift diff --git a/secant/UIComponents/CircularFrame/CircularFrame.swift b/secant/UI Components/CircularFrame/CircularFrame.swift similarity index 100% rename from secant/UIComponents/CircularFrame/CircularFrame.swift rename to secant/UI Components/CircularFrame/CircularFrame.swift diff --git a/secant/UIComponents/CircularFrame/CircularFrameBackground.swift b/secant/UI Components/CircularFrame/CircularFrameBackground.swift similarity index 100% rename from secant/UIComponents/CircularFrame/CircularFrameBackground.swift rename to secant/UI Components/CircularFrame/CircularFrameBackground.swift diff --git a/secant/UIComponents/CircularFrame/CircularFrameBadge.swift b/secant/UI Components/CircularFrame/CircularFrameBadge.swift similarity index 100% rename from secant/UIComponents/CircularFrame/CircularFrameBadge.swift rename to secant/UI Components/CircularFrame/CircularFrameBadge.swift diff --git a/secant/UIComponents/DesignGuide.swift b/secant/UI Components/DesignGuide.swift similarity index 100% rename from secant/UIComponents/DesignGuide.swift rename to secant/UI Components/DesignGuide.swift diff --git a/secant/UIComponents/Drawer/Drawer.swift b/secant/UI Components/Drawer/Drawer.swift similarity index 100% rename from secant/UIComponents/Drawer/Drawer.swift rename to secant/UI Components/Drawer/Drawer.swift diff --git a/secant/UIComponents/Extensions/ConditionalModifier.swift b/secant/UI Components/Extensions/ConditionalModifier.swift similarity index 100% rename from secant/UIComponents/Extensions/ConditionalModifier.swift rename to secant/UI Components/Extensions/ConditionalModifier.swift diff --git a/secant/UIComponents/Extensions/View+InnerShadow.swift b/secant/UI Components/Extensions/View+InnerShadow.swift similarity index 100% rename from secant/UIComponents/Extensions/View+InnerShadow.swift rename to secant/UI Components/Extensions/View+InnerShadow.swift diff --git a/secant/UIComponents/FontStyles/SecantButtonStyles.swift b/secant/UI Components/FontStyles/SecantButtonStyles.swift similarity index 100% rename from secant/UIComponents/FontStyles/SecantButtonStyles.swift rename to secant/UI Components/FontStyles/SecantButtonStyles.swift diff --git a/secant/UIComponents/FontStyles/SecantTextStyles.swift b/secant/UI Components/FontStyles/SecantTextStyles.swift similarity index 100% rename from secant/UIComponents/FontStyles/SecantTextStyles.swift rename to secant/UI Components/FontStyles/SecantTextStyles.swift diff --git a/secant/Features/ImportWallet/Views/ImportSeedEditor.swift b/secant/UI Components/ImportSeedEditor/ImportSeedEditor.swift similarity index 100% rename from secant/Features/ImportWallet/Views/ImportSeedEditor.swift rename to secant/UI Components/ImportSeedEditor/ImportSeedEditor.swift diff --git a/secant/UIComponents/ProgressIndicators/OnboardingProgressIndicator.swift b/secant/UI Components/ProgressIndicators/OnboardingProgressIndicator.swift similarity index 100% rename from secant/UIComponents/ProgressIndicators/OnboardingProgressIndicator.swift rename to secant/UI Components/ProgressIndicators/OnboardingProgressIndicator.swift diff --git a/secant/UIComponents/Shapes/ZcashSymbol.swift b/secant/UI Components/Shapes/ZcashSymbol.swift similarity index 100% rename from secant/UIComponents/Shapes/ZcashSymbol.swift rename to secant/UI Components/Shapes/ZcashSymbol.swift diff --git a/secant/UIComponents/TextFields/TransactionAmount/CurrencySelectionStore.swift b/secant/UI Components/TextFields/Components/CurrencySelection/CurrencySelectionStore.swift similarity index 100% rename from secant/UIComponents/TextFields/TransactionAmount/CurrencySelectionStore.swift rename to secant/UI Components/TextFields/Components/CurrencySelection/CurrencySelectionStore.swift diff --git a/secant/UIComponents/TextFields/TransactionAmount/TransactionCurrencySelector.swift b/secant/UI Components/TextFields/Components/CurrencySelection/CurrencySelectionView.swift similarity index 92% rename from secant/UIComponents/TextFields/TransactionAmount/TransactionCurrencySelector.swift rename to secant/UI Components/TextFields/Components/CurrencySelection/CurrencySelectionView.swift index 229ab4e..6d32407 100644 --- a/secant/UIComponents/TextFields/TransactionAmount/TransactionCurrencySelector.swift +++ b/secant/UI Components/TextFields/Components/CurrencySelection/CurrencySelectionView.swift @@ -1,5 +1,5 @@ // -// TransactionCurrencySelector.swift +// CurrencySelectionView.swift // secant-testnet // // Created by Adam Stener on 4/4/22. @@ -8,7 +8,7 @@ import SwiftUI import ComposableArchitecture -struct TransactionCurrencySelector: View { +struct CurrencySelectionView: View { let store: CurrencySelectionStore var body: some View { diff --git a/secant/UIComponents/TextFields/Components/TextFieldFooter.swift b/secant/UI Components/TextFields/Components/TextFieldFooter.swift similarity index 100% rename from secant/UIComponents/TextFields/Components/TextFieldFooter.swift rename to secant/UI Components/TextFields/Components/TextFieldFooter.swift diff --git a/secant/UIComponents/TextFields/Components/TextFieldTitleAccessoryButtonStyle.swift b/secant/UI Components/TextFields/Components/TextFieldTitleAccessoryButtonStyle.swift similarity index 100% rename from secant/UIComponents/TextFields/Components/TextFieldTitleAccessoryButtonStyle.swift rename to secant/UI Components/TextFields/Components/TextFieldTitleAccessoryButtonStyle.swift diff --git a/secant/UIComponents/TextFields/Components/SingleLineTextField.swift b/secant/UI Components/TextFields/SingleLineTextField.swift similarity index 94% rename from secant/UIComponents/TextFields/Components/SingleLineTextField.swift rename to secant/UI Components/TextFields/SingleLineTextField.swift index acd3802..6c3673b 100644 --- a/secant/UIComponents/TextFields/Components/SingleLineTextField.swift +++ b/secant/UI Components/TextFields/SingleLineTextField.swift @@ -12,7 +12,7 @@ struct SingleLineTextField -typealias TextFieldStore = Store +typealias TCATextFieldReducer = Reducer +typealias TCATextFieldStore = Store +typealias TCATextFieldViewStore = ViewStore -struct TextFieldState: Equatable { +// MARK: - State + +struct TCATextFieldState: Equatable { var validationType: String.ValidationType? var text: String var valid = false @@ -21,14 +24,20 @@ struct TextFieldState: Equatable { } } -enum TextFieldAction: Equatable { +// MARK: - Action + +enum TCATextFieldAction: Equatable { case set(String) } -struct TextFieldEnvironment: Equatable { } +// MARK: - Environment -extension TextFieldReducer { - static let `default` = TextFieldReducer { state, action, _ in +struct TCATextFieldEnvironment: Equatable { } + +// MARK: - Reducer + +extension TCATextFieldReducer { + static let `default` = TCATextFieldReducer { state, action, _ in switch action { case .set(let text): state.text = text @@ -38,7 +47,9 @@ extension TextFieldReducer { } } -extension TextFieldStore { +// MARK: - Store + +extension TCATextFieldStore { static var transaction: Self { .init( initialState: .init(validationType: .floatingPoint, text: ""), @@ -56,13 +67,15 @@ extension TextFieldStore { } } -extension TextFieldState { - static let placeholder = TextFieldState( +// MARK: - Placeholders + +extension TCATextFieldState { + static let placeholder = TCATextFieldState( validationType: nil, text: "" ) - static let amount = TextFieldState( + static let amount = TCATextFieldState( validationType: .floatingPoint, text: "" ) diff --git a/secant/UIComponents/TextFields/TransactionAddress/TransactionAddressTextField.swift b/secant/UI Components/TextFields/TransactionAddress/TransactionAddressTextField.swift similarity index 89% rename from secant/UIComponents/TextFields/TransactionAddress/TransactionAddressTextField.swift rename to secant/UI Components/TextFields/TransactionAddress/TransactionAddressTextField.swift index c3af618..d1e2cb2 100644 --- a/secant/UIComponents/TextFields/TransactionAddress/TransactionAddressTextField.swift +++ b/secant/UI Components/TextFields/TransactionAddress/TransactionAddressTextField.swift @@ -9,7 +9,7 @@ import SwiftUI import ComposableArchitecture struct TransactionAddressTextField: View { - let store: TransactionAddressInputStore + let store: TransactionAddressTextFieldStore var body: some View { WithViewStore(store) { viewStore in @@ -19,7 +19,7 @@ struct TransactionAddressTextField: View { title: "To", store: store.scope( state: \.textFieldState, - action: TransactionAddressInputAction.textField + action: TransactionAddressTextFieldAction.textField ), titleAccessoryView: { if !viewStore.textFieldState.text.isEmpty { @@ -49,7 +49,7 @@ struct TransactionAddressTextField: View { struct TransactionAddressTextField_Previews: PreviewProvider { static var previews: some View { TransactionAddressTextField( - store: TransactionAddressInputStore( + store: TransactionAddressTextFieldStore( initialState: .init( textFieldState: .init( validationType: .floatingPoint, @@ -57,7 +57,7 @@ struct TransactionAddressTextField_Previews: PreviewProvider { ) ), reducer: .default, - environment: .init(wrappedDerivationTool: .live()) + environment: .init(derivationTool: .live()) ) ) .preferredColorScheme(.dark) diff --git a/secant/UI Components/TextFields/TransactionAddress/TransactionAddressTextFieldStore.swift b/secant/UI Components/TextFields/TransactionAddress/TransactionAddressTextFieldStore.swift new file mode 100644 index 0000000..9f5cc2e --- /dev/null +++ b/secant/UI Components/TextFields/TransactionAddress/TransactionAddressTextFieldStore.swift @@ -0,0 +1,79 @@ +// +// TransactionAddressTextFieldStore.swift +// secant-testnet +// +// Created by Lukáš Korba on 05/05/22. +// + +import ComposableArchitecture +import SwiftUI + +typealias TransactionAddressTextFieldReducer = Reducer< + TransactionAddressTextFieldState, + TransactionAddressTextFieldAction, + TransactionAddressTextFieldEnvironment +> + +typealias TransactionAddressTextFieldStore = Store + +struct TransactionAddressTextFieldState: Equatable { + var textFieldState: TCATextFieldState + var isValidAddress = false +} + +enum TransactionAddressTextFieldAction: Equatable { + case clearAddress + case textField(TCATextFieldAction) +} + +struct TransactionAddressTextFieldEnvironment { + let derivationTool: WrappedDerivationTool +} + +extension TransactionAddressTextFieldReducer { + static let `default` = TransactionAddressTextFieldReducer.combine( + [ + addressReducer, + textFieldReducer + ] + ) + + private static let addressReducer = TransactionAddressTextFieldReducer { state, action, environment in + switch action { + case .clearAddress: + state.textFieldState.text = "" + return .none + + case .textField(.set(let address)): + do { + state.isValidAddress = try environment.derivationTool.isValidZcashAddress(address) + } catch { + state.isValidAddress = false + } + + return .none + } + } + + private static let textFieldReducer: TransactionAddressTextFieldReducer = TCATextFieldReducer.default.pullback( + state: \TransactionAddressTextFieldState.textFieldState, + action: /TransactionAddressTextFieldAction.textField, + environment: { _ in return .init() } + ) +} + +extension TransactionAddressTextFieldState { + static let placeholder = TransactionAddressTextFieldState( + textFieldState: .placeholder + ) +} + +extension TransactionAddressTextFieldStore { + static let placeholder = TransactionAddressTextFieldStore( + initialState: .placeholder, + reducer: .default, + environment: TransactionAddressTextFieldEnvironment( + derivationTool: .live() + ) + ) +} diff --git a/secant/UIComponents/TextFields/TransactionAmount/TransactionAmountTextField.swift b/secant/UI Components/TextFields/TransactionAmount/TransactionAmountTextField.swift similarity index 91% rename from secant/UIComponents/TextFields/TransactionAmount/TransactionAmountTextField.swift rename to secant/UI Components/TextFields/TransactionAmount/TransactionAmountTextField.swift index 4780046..92bfadb 100644 --- a/secant/UIComponents/TextFields/TransactionAmount/TransactionAmountTextField.swift +++ b/secant/UI Components/TextFields/TransactionAmount/TransactionAmountTextField.swift @@ -9,7 +9,7 @@ import SwiftUI import ComposableArchitecture struct TransactionAmountTextField: View { - let store: TransactionAmountInputStore + let store: TransactionAmountTextFieldStore var body: some View { WithViewStore(store) { viewStore in @@ -19,7 +19,7 @@ struct TransactionAmountTextField: View { title: "How much ZEC would you like to send?", store: store.scope( state: \.textFieldState, - action: TransactionAmountInputAction.textField + action: TransactionAmountTextFieldAction.textField ), titleAccessoryView: { Button( @@ -41,10 +41,10 @@ struct TransactionAmountTextField: View { } }, inputAccessoryView: { - TransactionCurrencySelector( + CurrencySelectionView( store: store.scope( state: \.currencySelectionState, - action: TransactionAmountInputAction.currencySelection + action: TransactionAmountTextFieldAction.currencySelection ) ) } @@ -57,7 +57,7 @@ struct TransactionAmountTextField: View { struct TransactionAmountTextField_Previews: PreviewProvider { static var previews: some View { TransactionAmountTextField( - store: TransactionAmountInputStore( + store: TransactionAmountTextFieldStore( initialState: .init( textFieldState: .init( validationType: .floatingPoint, diff --git a/secant/UIComponents/TextFields/TransactionAmount/TransactionAmountInputStore.swift b/secant/UI Components/TextFields/TransactionAmount/TransactionAmountTextFieldStore.swift similarity index 59% rename from secant/UIComponents/TextFields/TransactionAmount/TransactionAmountInputStore.swift rename to secant/UI Components/TextFields/TransactionAmount/TransactionAmountTextFieldStore.swift index 7c2d80c..b4bea23 100644 --- a/secant/UIComponents/TextFields/TransactionAmount/TransactionAmountInputStore.swift +++ b/secant/UI Components/TextFields/TransactionAmount/TransactionAmountTextFieldStore.swift @@ -1,5 +1,5 @@ // -// TransactionAmountInputStore.swift +// TransactionAmountTextFieldStore.swift // secant-testnet // // Created by Adam Stener on 4/5/22. @@ -7,16 +7,16 @@ import ComposableArchitecture -typealias TransactionAmountInputReducer = Reducer< - TransactionAmountInputState, - TransactionAmountInputAction, - TransactionAmountInputEnvironment +typealias TransactionAmountTextFieldReducer = Reducer< + TransactionAmountTextFieldState, + TransactionAmountTextFieldAction, + TransactionAmountTextFieldEnvironment > -typealias TransactionAmountInputStore = Store +typealias TransactionAmountTextFieldStore = Store -struct TransactionAmountInputState: Equatable { - var textFieldState: TextFieldState +struct TransactionAmountTextFieldState: Equatable { + var textFieldState: TCATextFieldState var currencySelectionState: CurrencySelectionState var maxValue: Int64 = 0 // TODO: - Get the ZEC price from the SDK, issue 311, https://github.com/zcash/secant-ios-wallet/issues/311 @@ -45,17 +45,17 @@ struct TransactionAmountInputState: Equatable { } } -enum TransactionAmountInputAction: Equatable { +enum TransactionAmountTextFieldAction: Equatable { case clearValue case setMax - case textField(TextFieldAction) + case textField(TCATextFieldAction) case currencySelection(CurrencySelectionAction) } -struct TransactionAmountInputEnvironment: Equatable {} +struct TransactionAmountTextFieldEnvironment: Equatable {} -extension TransactionAmountInputReducer { - static let `default` = TransactionAmountInputReducer.combine( +extension TransactionAmountTextFieldReducer { + static let `default` = TransactionAmountTextFieldReducer.combine( [ textFieldReducer, currencySelectionReducer, @@ -64,7 +64,7 @@ extension TransactionAmountInputReducer { ] ) - static let maxOverride = TransactionAmountInputReducer { state, action, _ in + static let maxOverride = TransactionAmountTextFieldReducer { state, action, _ in switch action { case .setMax: state.textFieldState.text = "\(state.maxCurrencyConvertedValue.asZecString())" @@ -78,7 +78,7 @@ extension TransactionAmountInputReducer { return .none } - static let currencyUpdate = TransactionAmountInputReducer { state, action, _ in + static let currencyUpdate = TransactionAmountTextFieldReducer { state, action, _ in switch action { case .currencySelection: guard let currentDoubleValue = state.textFieldState.text.doubleValue else { @@ -98,35 +98,35 @@ extension TransactionAmountInputReducer { return .none } - private static let textFieldReducer: TransactionAmountInputReducer = TextFieldReducer.default.pullback( - state: \TransactionAmountInputState.textFieldState, - action: /TransactionAmountInputAction.textField, + private static let textFieldReducer: TransactionAmountTextFieldReducer = TCATextFieldReducer.default.pullback( + state: \TransactionAmountTextFieldState.textFieldState, + action: /TransactionAmountTextFieldAction.textField, environment: { _ in return .init() } ) - private static let currencySelectionReducer: TransactionAmountInputReducer = CurrencySelectionReducer.default.pullback( - state: \TransactionAmountInputState.currencySelectionState, - action: /TransactionAmountInputAction.currencySelection, + private static let currencySelectionReducer: TransactionAmountTextFieldReducer = CurrencySelectionReducer.default.pullback( + state: \TransactionAmountTextFieldState.currencySelectionState, + action: /TransactionAmountTextFieldAction.currencySelection, environment: { _ in return .init() } ) } -extension TransactionAmountInputState { - static let placeholder = TransactionAmountInputState( +extension TransactionAmountTextFieldState { + static let placeholder = TransactionAmountTextFieldState( textFieldState: .placeholder, currencySelectionState: CurrencySelectionState() ) - static let amount = TransactionAmountInputState( + static let amount = TransactionAmountTextFieldState( textFieldState: .amount, currencySelectionState: CurrencySelectionState() ) } -extension TransactionAmountInputStore { - static let placeholder = TransactionAmountInputStore( +extension TransactionAmountTextFieldStore { + static let placeholder = TransactionAmountTextFieldStore( initialState: .placeholder, reducer: .default, - environment: TransactionAmountInputEnvironment() + environment: TransactionAmountTextFieldEnvironment() ) } diff --git a/secant/UI Components/ZcashBadge/ZcashBadge.swift b/secant/UI Components/ZcashBadge/ZcashBadge.swift new file mode 100644 index 0000000..106cf60 --- /dev/null +++ b/secant/UI Components/ZcashBadge/ZcashBadge.swift @@ -0,0 +1,60 @@ +// +// ZcashBadge.swift +// secant-testnet +// +// Created by Lukáš Korba on 12.05.2022. +// + +import SwiftUI + +struct ZcashBadge: View { + @Environment(\.colorScheme) var colorScheme + + var body: some View { + ZStack { + GeometryReader { proxy in + let outterPadding = proxy.size.height * 0.015 + let firstPadding = proxy.size.height * 0.075 + outterPadding + let secondRingPadding = firstPadding * 1.5 + let outerShadowDrop = proxy.size.height * 0.14 + let outerShadowOffset = proxy.size.height * 0.055 + + Circle() + .fill( + LinearGradient( + colors: [ + Asset.Colors.ZcashBadge.outerRingGradientStart.color, + Asset.Colors.ZcashBadge.outerRingGradientEnd.color + ], + startPoint: UnitPoint(x: 0.5, y: 0), + endPoint: UnitPoint(x: 0.5, y: 1) + ) + ) + .if(colorScheme == .light) { view in + view.shadow( + color: Asset.Colors.ZcashBadge.shadowColor.color, + radius: outerShadowDrop, + x: outerShadowOffset, + y: outerShadowOffset + ) + } + + Circle() + .foregroundColor(Asset.Colors.ZcashBadge.thickRing.color) + .padding(outterPadding) + + Circle() + .foregroundColor(Asset.Colors.ZcashBadge.thinRing.color) + .padding(firstPadding) + + Circle() + .foregroundColor(Asset.Colors.ZcashBadge.innerCircle.color) + .padding(secondRingPadding) + + ZcashSymbol() + .fill(Asset.Colors.ZcashBadge.zcashLogoFill.color) + .padding(firstPadding + secondRingPadding) + } + } + } +} diff --git a/secant/UIComponents/TextFields/TransactionAddress/TransactionAddressInputStore.swift b/secant/UIComponents/TextFields/TransactionAddress/TransactionAddressInputStore.swift deleted file mode 100644 index 5b05cd9..0000000 --- a/secant/UIComponents/TextFields/TransactionAddress/TransactionAddressInputStore.swift +++ /dev/null @@ -1,79 +0,0 @@ -// -// TransactionAddressInputStore.swift -// secant-testnet -// -// Created by Lukáš Korba on 05/05/22. -// - -import ComposableArchitecture -import SwiftUI - -typealias TransactionAddressInputReducer = Reducer< - TransactionAddressInputState, - TransactionAddressInputAction, - TransactionAddressInputEnvironment -> - -typealias TransactionAddressInputStore = Store - -struct TransactionAddressInputState: Equatable { - var textFieldState: TextFieldState - var isValidAddress = false -} - -enum TransactionAddressInputAction: Equatable { - case clearAddress - case textField(TextFieldAction) -} - -struct TransactionAddressInputEnvironment { - let wrappedDerivationTool: WrappedDerivationTool -} - -extension TransactionAddressInputReducer { - static let `default` = TransactionAddressInputReducer.combine( - [ - addressReducer, - textFieldReducer - ] - ) - - private static let addressReducer = TransactionAddressInputReducer { state, action, environment in - switch action { - case .clearAddress: - state.textFieldState.text = "" - return .none - - case .textField(.set(let address)): - do { - state.isValidAddress = try environment.wrappedDerivationTool.isValidZcashAddress(address) - } catch { - state.isValidAddress = false - } - - return .none - } - } - - private static let textFieldReducer: TransactionAddressInputReducer = TextFieldReducer.default.pullback( - state: \TransactionAddressInputState.textFieldState, - action: /TransactionAddressInputAction.textField, - environment: { _ in return .init() } - ) -} - -extension TransactionAddressInputState { - static let placeholder = TransactionAddressInputState( - textFieldState: .placeholder - ) -} - -extension TransactionAddressInputStore { - static let placeholder = TransactionAddressInputStore( - initialState: .placeholder, - reducer: .default, - environment: TransactionAddressInputEnvironment( - wrappedDerivationTool: .live() - ) - ) -} diff --git a/secant/Utils/Array+Chunked.swift b/secant/Utils/Array+Chunked.swift new file mode 100644 index 0000000..af7c5c4 --- /dev/null +++ b/secant/Utils/Array+Chunked.swift @@ -0,0 +1,16 @@ +// +// Array+Chuncked.swift +// secant-testnet +// +// Created by Lukáš Korba on 12.05.2022. +// + +import Foundation + +extension Array { + func chunked(into size: Int) -> [[Element]] { + return stride(from: 0, to: count, by: size).map { + Array(self[$0 ..< Swift.min($0 + size, count)]) + } + } +} diff --git a/secant/Util/Bindings.swift b/secant/Utils/Bindings.swift similarity index 100% rename from secant/Util/Bindings.swift rename to secant/Utils/Bindings.swift diff --git a/secant/Util/Clamped.swift b/secant/Utils/Clamped.swift similarity index 100% rename from secant/Util/Clamped.swift rename to secant/Utils/Clamped.swift diff --git a/secant/Utils/Date+Readable.swift b/secant/Utils/Date+Readable.swift new file mode 100644 index 0000000..a126a38 --- /dev/null +++ b/secant/Utils/Date+Readable.swift @@ -0,0 +1,19 @@ +// +// Date+Readable.swift +// secant-testnet +// +// Created by Lukáš Korba on 12.05.2022. +// + +import Foundation + +extension Date { + func asHumanReadable() -> String { + let dateFormatter = DateFormatter() + + dateFormatter.dateStyle = .short + dateFormatter.timeStyle = .short + + return dateFormatter.string(from: self) + } +} diff --git a/secant/Util/DebugFrame.swift b/secant/Utils/DebugFrame.swift similarity index 100% rename from secant/Util/DebugFrame.swift rename to secant/Utils/DebugFrame.swift diff --git a/secant/Util/DebugMenu.swift b/secant/Utils/DebugMenu.swift similarity index 100% rename from secant/Util/DebugMenu.swift rename to secant/Utils/DebugMenu.swift diff --git a/secant/Util/Double+Zcash.swift b/secant/Utils/Double+Zcash.swift similarity index 100% rename from secant/Util/Double+Zcash.swift rename to secant/Utils/Double+Zcash.swift diff --git a/secant/Util/Int64+Zcash.swift b/secant/Utils/Int64+Zcash.swift similarity index 100% rename from secant/Util/Int64+Zcash.swift rename to secant/Utils/Int64+Zcash.swift diff --git a/secant/Util/NavigationLinks.swift b/secant/Utils/NavigationLinks.swift similarity index 100% rename from secant/Util/NavigationLinks.swift rename to secant/Utils/NavigationLinks.swift diff --git a/secant/Util/Previews.swift b/secant/Utils/Previews.swift similarity index 100% rename from secant/Util/Previews.swift rename to secant/Utils/Previews.swift diff --git a/secant/Utils/SDKSynchronizer+SyncStatus.swift b/secant/Utils/SDKSynchronizer+SyncStatus.swift new file mode 100644 index 0000000..b6b7568 --- /dev/null +++ b/secant/Utils/SDKSynchronizer+SyncStatus.swift @@ -0,0 +1,45 @@ +// +// SDKSynchronizer+SyncStatus.swift +// secant-testnet +// +// Created by Lukáš Korba on 12.05.2022. +// + +import Foundation +import ZcashLightClientKit + +extension SDKSynchronizer { + static func textFor(state: SyncStatus) -> String { + switch state { + case .downloading(let progress): + return "Downloading \(progress.progressHeight)/\(progress.targetHeight)" + + case .enhancing(let enhanceProgress): + return "Enhancing tx \(enhanceProgress.enhancedTransactions) of \(enhanceProgress.totalTransactions)" + + case .fetching: + return "fetching UTXOs" + + case .scanning(let scanProgress): + return "Scanning: \(scanProgress.progressHeight)/\(scanProgress.targetHeight)" + + case .disconnected: + return "disconnected 💔" + + case .stopped: + return "Stopped 🚫" + + case .synced: + return "Synced 😎" + + case .unprepared: + return "Unprepared 😅" + + case .validating: + return "Validating" + + case .error(let err): + return "Error: \(err.localizedDescription)" + } + } +} diff --git a/secant/Util/ScrollableWhenScaled.swift b/secant/Utils/ScrollableWhenScaled.swift similarity index 100% rename from secant/Util/ScrollableWhenScaled.swift rename to secant/Utils/ScrollableWhenScaled.swift diff --git a/secant/Util/Strings.swift b/secant/Utils/Strings.swift similarity index 100% rename from secant/Util/Strings.swift rename to secant/Utils/Strings.swift diff --git a/secant/Util/UInt+SuperscriptText.swift b/secant/Utils/UInt+SuperscriptText.swift similarity index 100% rename from secant/Util/UInt+SuperscriptText.swift rename to secant/Utils/UInt+SuperscriptText.swift diff --git a/secant/Features/BackupFlow/View+WhenDraggable.swift b/secant/Utils/View+WhenDraggable.swift similarity index 100% rename from secant/Features/BackupFlow/View+WhenDraggable.swift rename to secant/Utils/View+WhenDraggable.swift diff --git a/secant/Util/WithStateBinding.swift b/secant/Utils/WithStateBinding.swift similarity index 100% rename from secant/Util/WithStateBinding.swift rename to secant/Utils/WithStateBinding.swift diff --git a/secant/Wrappers/WrappedDatabaseFiles.swift b/secant/Wrappers/WrappedDatabaseFiles.swift new file mode 100644 index 0000000..a9a214d --- /dev/null +++ b/secant/Wrappers/WrappedDatabaseFiles.swift @@ -0,0 +1,78 @@ +// +// WrappedDatabaseFiles.swift +// secant-testnet +// +// Created by Lukáš Korba on 13.05.2022. +// + +import Foundation +import ZcashLightClientKit + +struct WrappedDatabaseFiles { + let documentsDirectory: () throws -> URL + let cacheDbURLFor: (ZcashNetwork) throws -> URL + let dataDbURLFor: (ZcashNetwork) throws -> URL + let outputParamsURLFor: (ZcashNetwork) throws -> URL + let pendingDbURLFor: (ZcashNetwork) throws -> URL + let spendParamsURLFor: (ZcashNetwork) throws -> URL + let areDbFilesPresentFor: (ZcashNetwork) throws -> Bool + let nukeDbFilesFor: (ZcashNetwork) throws -> Void +} + +extension WrappedDatabaseFiles { + static func live(databaseFiles: DatabaseFiles = DatabaseFiles(fileManager: .live)) -> Self { + Self( + documentsDirectory: { + try databaseFiles.documentsDirectory() + }, + cacheDbURLFor: { network in + try databaseFiles.cacheDbURL(for: network) + }, + dataDbURLFor: { network in + try databaseFiles.dataDbURL(for: network) + }, + outputParamsURLFor: { network in + try databaseFiles.outputParamsURL(for: network) + }, + pendingDbURLFor: { network in + try databaseFiles.pendingDbURL(for: network) + }, + spendParamsURLFor: { network in + try databaseFiles.spendParamsURL(for: network) + }, + areDbFilesPresentFor: { network in + try databaseFiles.areDbFilesPresent(for: network) + }, + nukeDbFilesFor: { network in + try databaseFiles.nukeDbFiles(for: network) + } + ) + } + + static var throwing = WrappedDatabaseFiles( + documentsDirectory: { + throw DatabaseFiles.DatabaseFilesError.getDocumentsURL + }, + cacheDbURLFor: { _ in + throw DatabaseFiles.DatabaseFilesError.getCacheURL + }, + dataDbURLFor: { _ in + throw DatabaseFiles.DatabaseFilesError.getDataURL + }, + outputParamsURLFor: { _ in + throw DatabaseFiles.DatabaseFilesError.getOutputParamsURL + }, + pendingDbURLFor: { _ in + throw DatabaseFiles.DatabaseFilesError.getPendingURL + }, + spendParamsURLFor: { _ in + throw DatabaseFiles.DatabaseFilesError.getSpendParamsURL + }, + areDbFilesPresentFor: { _ in + throw DatabaseFiles.DatabaseFilesError.filesPresentCheck + }, + nukeDbFilesFor: { _ in + throw DatabaseFiles.DatabaseFilesError.nukeFiles + } + ) +} diff --git a/secant/Wrappers/WrappedFeedbackGenerator.swift b/secant/Wrappers/WrappedFeedbackGenerator.swift new file mode 100644 index 0000000..0f191e1 --- /dev/null +++ b/secant/Wrappers/WrappedFeedbackGenerator.swift @@ -0,0 +1,23 @@ +// +// WrappedFeedbackGenerator.swift +// secant-testnet +// +// Created by Lukáš Korba on 12.05.2022. +// + +import Foundation +import UIKit + +struct WrappedFeedbackGenerator { + let generateFeedback: () -> Void +} + +extension WrappedFeedbackGenerator { + static let haptic = WrappedFeedbackGenerator( + generateFeedback: { UINotificationFeedbackGenerator().notificationOccurred(.error) } + ) + + static let silent = WrappedFeedbackGenerator( + generateFeedback: { } + ) +} diff --git a/secant/Util/MnemonicSeedPhraseProvider.swift b/secant/Wrappers/WrappedMnemonic.swift similarity index 92% rename from secant/Util/MnemonicSeedPhraseProvider.swift rename to secant/Wrappers/WrappedMnemonic.swift index 09d72f3..297322a 100644 --- a/secant/Util/MnemonicSeedPhraseProvider.swift +++ b/secant/Wrappers/WrappedMnemonic.swift @@ -1,5 +1,5 @@ // -// MnemonicSeedPhraseProvider.swift +// WrappedMnemonic.swift // secant-testnet // // Created by Lukáš Korba on 03/09/2022. @@ -8,7 +8,7 @@ import Foundation import MnemonicSwift -struct MnemonicSeedPhraseProvider { +struct WrappedMnemonic { /// Random 24 words mnemonic phrase var randomMnemonic: () throws -> String /// Random 24 words mnemonic phrase as array of words @@ -21,8 +21,8 @@ struct MnemonicSeedPhraseProvider { var isValid: (String) throws -> Void } -extension MnemonicSeedPhraseProvider { - static let live = MnemonicSeedPhraseProvider( +extension WrappedMnemonic { + static let live = WrappedMnemonic( randomMnemonic: { try Mnemonic.generateMnemonic(strength: 256) }, @@ -42,7 +42,7 @@ extension MnemonicSeedPhraseProvider { } ) - static let mock = MnemonicSeedPhraseProvider( + static let mock = WrappedMnemonic( randomMnemonic: { """ still champion voice habit trend flight \ diff --git a/secant/Wrappers/WrappedPasteboard.swift b/secant/Wrappers/WrappedPasteboard.swift new file mode 100644 index 0000000..e260f1b --- /dev/null +++ b/secant/Wrappers/WrappedPasteboard.swift @@ -0,0 +1,31 @@ +// +// WrappedPasteboard.swift +// secant-testnet +// +// Created by Lukáš Korba on 12.05.2022. +// + +import Foundation +import UIKit + +struct WrappedPasteboard { + let setString: (String) -> Void + let getString: () -> String? +} + +extension WrappedPasteboard { + private struct TestPasteboard { + static var general = TestPasteboard() + var string: String? + } + + static let live = WrappedPasteboard( + setString: { UIPasteboard.general.string = $0 }, + getString: { UIPasteboard.general.string } + ) + + static let test = WrappedPasteboard( + setString: { TestPasteboard.general.string = $0 }, + getString: { TestPasteboard.general.string } + ) +} diff --git a/secant/Util/WalletStorageInteractor.swift b/secant/Wrappers/WrappedWalletStorage.swift similarity index 84% rename from secant/Util/WalletStorageInteractor.swift rename to secant/Wrappers/WrappedWalletStorage.swift index 614f4c7..691460d 100644 --- a/secant/Util/WalletStorageInteractor.swift +++ b/secant/Wrappers/WrappedWalletStorage.swift @@ -1,5 +1,5 @@ // -// WalletStorageInteractor.swift +// WrappedWalletStorage.swift // secant-testnet // // Created by Lukáš Korba on 03/25/2022. @@ -9,23 +9,13 @@ import Foundation import MnemonicSwift import ZcashLightClientKit -/// Representation of the wallet stored in the persistent storage (typically keychain, handled by `WalletStorage`). -struct StoredWallet: Codable, Equatable { - let language: MnemonicLanguageType - let seedPhrase: String - let version: Int - - var birthday: BlockHeight? - var hasUserPassedPhraseBackupTest: Bool -} - -/// The `WalletStorageInteractor` is a wrapper around the `WalletStorage` type that allows for easy testing. +/// The `WrappedWalletStorage` is a wrapper around the `WalletStorage` type that allows for easy testing. /// The `WalletStorage` Type is comprised of static functions that take and produce data. In order /// to easily produce test data, all of these static functions have been wrapped in function -/// properties that live on the `WalletStorageInteractor` type. Because of this, you can instantiate -/// the `WalletStorageInteractor` with your own implementation of these functions for testing purposes, -/// or you can use one of the built in static versions of the `WalletStorageInteractor`. -struct WalletStorageInteractor { +/// properties that live on the `WrappedWalletStorage` type. Because of this, you can instantiate +/// the `WrappedWalletStorage` with your own implementation of these functions for testing purposes, +/// or you can use one of the built in static versions of the `WrappedWalletStorage`. +struct WrappedWalletStorage { /// Store recovery phrase and optionally even birthday to the secured and persistent storage. /// This function creates an instance of `StoredWallet` and automatically handles versioning of the stored data. /// @@ -76,7 +66,7 @@ struct WalletStorageInteractor { let nukeWallet: () -> Void } -extension WalletStorageInteractor { +extension WrappedWalletStorage { public static func live(walletStorage: WalletStorage = WalletStorage(secItem: .live)) -> Self { Self( importWallet: { bip39, birthday, language, hasUserPassedPhraseBackupTest in @@ -105,7 +95,7 @@ extension WalletStorageInteractor { ) } - public static let throwing = WalletStorageInteractor( + public static let throwing = WrappedWalletStorage( importWallet: { _, _, _, _ in throw WalletStorage.WalletStorageError.alreadyImported }, diff --git a/secantTests/AppReducerTests/AppReducerTests.swift b/secantTests/AppReducerTests/AppReducerTests.swift index 86ca6d7..55fa9c4 100644 --- a/secantTests/AppReducerTests/AppReducerTests.swift +++ b/secantTests/AppReducerTests/AppReducerTests.swift @@ -13,23 +13,23 @@ class AppReducerTests: XCTestCase { static let testScheduler = DispatchQueue.test let testEnvironment = AppEnvironment( - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer(), + SDKSynchronizer: TestWrappedSDKSynchronizer(), databaseFiles: .throwing, mnemonicSeedPhraseProvider: .mock, scheduler: testScheduler.eraseToAnyScheduler(), walletStorage: .throwing, - wrappedDerivationTool: .live(), + derivationTool: .live(), zcashSDKEnvironment: .mainnet ) func testWalletInitializationState_Uninitialized() throws { let uninitializedEnvironment = AppEnvironment( - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer(), + SDKSynchronizer: TestWrappedSDKSynchronizer(), databaseFiles: .throwing, mnemonicSeedPhraseProvider: .mock, scheduler: DispatchQueue.test.eraseToAnyScheduler(), walletStorage: .throwing, - wrappedDerivationTool: .live(), + derivationTool: .live(), zcashSDKEnvironment: .mainnet ) @@ -46,12 +46,12 @@ class AppReducerTests: XCTestCase { ) let keysMissingEnvironment = AppEnvironment( - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer(), + SDKSynchronizer: TestWrappedSDKSynchronizer(), databaseFiles: .live(databaseFiles: DatabaseFiles(fileManager: wfmMock)), mnemonicSeedPhraseProvider: .mock, scheduler: Self.testScheduler.eraseToAnyScheduler(), walletStorage: .throwing, - wrappedDerivationTool: .live(), + derivationTool: .live(), zcashSDKEnvironment: .mainnet ) @@ -68,12 +68,12 @@ class AppReducerTests: XCTestCase { ) let keysMissingEnvironment = AppEnvironment( - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer(), + SDKSynchronizer: TestWrappedSDKSynchronizer(), databaseFiles: .live(databaseFiles: DatabaseFiles(fileManager: wfmMock)), mnemonicSeedPhraseProvider: .mock, scheduler: Self.testScheduler.eraseToAnyScheduler(), walletStorage: .throwing, - wrappedDerivationTool: .live(), + derivationTool: .live(), zcashSDKEnvironment: .testnet ) diff --git a/secantTests/OnboardingTests/OnboardingStoreTests.swift b/secantTests/OnboardingTests/OnboardingStoreTests.swift index 8110f87..1d96ada 100644 --- a/secantTests/OnboardingTests/OnboardingStoreTests.swift +++ b/secantTests/OnboardingTests/OnboardingStoreTests.swift @@ -12,10 +12,10 @@ import ComposableArchitecture class OnboardingStoreTests: XCTestCase { func testIncrementingOnboarding() { let store = TestStore( - initialState: OnboardingState( + initialState: OnboardingFlowState( importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: .demo ) @@ -52,11 +52,11 @@ class OnboardingStoreTests: XCTestCase { func testIncrementingPastTotalStepsDoesNothing() { let store = TestStore( - initialState: OnboardingState( + initialState: OnboardingFlowState( index: 3, importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: .demo ) @@ -79,11 +79,11 @@ class OnboardingStoreTests: XCTestCase { func testDecrementingOnboarding() { let store = TestStore( - initialState: OnboardingState( + initialState: OnboardingFlowState( index: 2, importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: .demo ) @@ -110,10 +110,10 @@ class OnboardingStoreTests: XCTestCase { func testDecrementingPastFirstStepDoesNothing() { let store = TestStore( - initialState: OnboardingState( + initialState: OnboardingFlowState( importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: .demo ) @@ -138,11 +138,11 @@ class OnboardingStoreTests: XCTestCase { let initialIndex = 1 let store = TestStore( - initialState: OnboardingState( + initialState: OnboardingFlowState( index: initialIndex, importWalletState: .placeholder ), - reducer: OnboardingReducer.default, + reducer: OnboardingFlowReducer.default, environment: .demo ) diff --git a/secantTests/RecoveryPhraseValidationTests/RecoveryPhraseValidationTests.swift b/secantTests/RecoveryPhraseValidationTests/RecoveryPhraseValidationTests.swift index d9e3da0..a98e089 100644 --- a/secantTests/RecoveryPhraseValidationTests/RecoveryPhraseValidationTests.swift +++ b/secantTests/RecoveryPhraseValidationTests/RecoveryPhraseValidationTests.swift @@ -12,7 +12,7 @@ import ComposableArchitecture class RecoveryPhraseValidationTests: XCTestCase { static let testScheduler = DispatchQueue.test - let testEnvironment = BackupPhraseEnvironment( + let testEnvironment = RecoveryPhraseValidationFlowEnvironment( mainQueue: testScheduler.eraseToAnyScheduler(), newPhrase: { Effect(value: .init(words: RecoveryPhrase.placeholder.words)) }, pasteboard: .test, @@ -66,14 +66,14 @@ class RecoveryPhraseValidationTests: XCTestCase { let missingWordChips: [PhraseChip.Kind] = ["salute", "boil", "cancel", "pizza"].map({ PhraseChip.Kind.unassigned(word: $0) }) - let initialStep = RecoveryPhraseValidationState( + let initialStep = RecoveryPhraseValidationFlowState( phrase: phrase, missingIndices: missingIndices, missingWordChips: missingWordChips, validationWords: [] ) - let store = TestStore(initialState: initialStep, reducer: RecoveryPhraseValidationReducer.default, environment: testEnvironment) + let store = TestStore(initialState: initialStep, reducer: RecoveryPhraseValidationFlowReducer.default, environment: testEnvironment) let expectedMissingChips = [ PhraseChip.Kind.empty, @@ -113,13 +113,13 @@ class RecoveryPhraseValidationTests: XCTestCase { let missingWordChips = ["salute", "boil", "cancel", "pizza"].map({ PhraseChip.Kind.unassigned(word: $0) }) - let initialStep = RecoveryPhraseValidationState.initial( + let initialStep = RecoveryPhraseValidationFlowState.initial( phrase: phrase, missingIndices: missingIndices, missingWordsChips: missingWordChips ) - let store = TestStore(initialState: initialStep, reducer: RecoveryPhraseValidationReducer.default, environment: testEnvironment) + let store = TestStore(initialState: initialStep, reducer: RecoveryPhraseValidationFlowReducer.default, environment: testEnvironment) let expectedMissingChips = [ PhraseChip.Kind.unassigned(word: "salute"), @@ -157,7 +157,7 @@ class RecoveryPhraseValidationTests: XCTestCase { let phrase = RecoveryPhrase(words: words) - let currentStep = RecoveryPhraseValidationState( + let currentStep = RecoveryPhraseValidationFlowState( phrase: phrase, missingIndices: missingIndices, missingWordChips: [ @@ -169,7 +169,7 @@ class RecoveryPhraseValidationTests: XCTestCase { validationWords: [ValidationWord(groupIndex: 0, word: "salute")] ) - let store = TestStore(initialState: currentStep, reducer: RecoveryPhraseValidationReducer.default, environment: testEnvironment) + let store = TestStore(initialState: currentStep, reducer: RecoveryPhraseValidationFlowReducer.default, environment: testEnvironment) let expectedMissingWordChips = [ PhraseChip.Kind.empty, @@ -210,7 +210,7 @@ class RecoveryPhraseValidationTests: XCTestCase { let phrase = RecoveryPhrase(words: words) - let currentStep = RecoveryPhraseValidationState( + let currentStep = RecoveryPhraseValidationFlowState( phrase: phrase, missingIndices: missingIndices, missingWordChips: [ @@ -225,7 +225,7 @@ class RecoveryPhraseValidationTests: XCTestCase { ] ) - let store = TestStore(initialState: currentStep, reducer: RecoveryPhraseValidationReducer.default, environment: testEnvironment) + let store = TestStore(initialState: currentStep, reducer: RecoveryPhraseValidationFlowReducer.default, environment: testEnvironment) let expectedMissingWordChips = [ PhraseChip.Kind.empty, @@ -267,7 +267,7 @@ class RecoveryPhraseValidationTests: XCTestCase { let phrase = RecoveryPhrase(words: words) - let currentStep = RecoveryPhraseValidationState( + let currentStep = RecoveryPhraseValidationFlowState( phrase: phrase, missingIndices: missingIndices, missingWordChips: [ @@ -283,7 +283,7 @@ class RecoveryPhraseValidationTests: XCTestCase { ] ) - let store = TestStore(initialState: currentStep, reducer: RecoveryPhraseValidationReducer.default, environment: testEnvironment) + let store = TestStore(initialState: currentStep, reducer: RecoveryPhraseValidationFlowReducer.default, environment: testEnvironment) let expectedMissingWordChips = [ PhraseChip.Kind.empty, @@ -334,7 +334,7 @@ class RecoveryPhraseValidationTests: XCTestCase { let phrase = RecoveryPhrase(words: words) - let currentStep = RecoveryPhraseValidationState( + let currentStep = RecoveryPhraseValidationFlowState( phrase: phrase, missingIndices: missingIndices, missingWordChips: [ @@ -350,7 +350,7 @@ class RecoveryPhraseValidationTests: XCTestCase { ] ) - let store = TestStore(initialState: currentStep, reducer: RecoveryPhraseValidationReducer.default, environment: testEnvironment) + let store = TestStore(initialState: currentStep, reducer: RecoveryPhraseValidationFlowReducer.default, environment: testEnvironment) let expectedMissingWordChips = [ PhraseChip.Kind.empty, @@ -404,7 +404,7 @@ class RecoveryPhraseValidationTests: XCTestCase { let phrase = RecoveryPhrase(words: words) - let currentStep = RecoveryPhraseValidationState( + let currentStep = RecoveryPhraseValidationFlowState( phrase: phrase, missingIndices: missingIndices, missingWordChips: [ @@ -456,7 +456,7 @@ class RecoveryPhraseValidationTests: XCTestCase { let phrase = RecoveryPhrase(words: words) - let currentStep = RecoveryPhraseValidationState( + let currentStep = RecoveryPhraseValidationFlowState( phrase: phrase, missingIndices: missingIndices, missingWordChips: [ @@ -509,7 +509,7 @@ class RecoveryPhraseValidationTests: XCTestCase { let phrase = RecoveryPhrase(words: words) - let currentStep = RecoveryPhraseValidationState( + let currentStep = RecoveryPhraseValidationFlowState( phrase: phrase, missingIndices: missingIndices, missingWordChips: [ @@ -547,7 +547,7 @@ class RecoveryPhraseValidationTests: XCTestCase { let phrase = RecoveryPhrase(words: words) - let currentStep = RecoveryPhraseValidationState( + let currentStep = RecoveryPhraseValidationFlowState( phrase: phrase, missingIndices: missingIndices, missingWordChips: [ @@ -593,7 +593,7 @@ class RecoveryPhraseValidationTests: XCTestCase { ValidationWord(groupIndex: 3, word: "pizza") ] - let result = RecoveryPhraseValidationState( + let result = RecoveryPhraseValidationFlowState( phrase: phrase, missingIndices: missingIndices, missingWordChips: phrase.words(fromMissingIndices: missingIndices), @@ -632,7 +632,7 @@ class RecoveryPhraseValidationTests: XCTestCase { ValidationWord(groupIndex: 2, word: "pizza") ] - let result = RecoveryPhraseValidationState( + let result = RecoveryPhraseValidationFlowState( phrase: phrase, missingIndices: missingIndices, missingWordChips: phrase.words(fromMissingIndices: missingIndices), @@ -646,13 +646,13 @@ class RecoveryPhraseValidationTests: XCTestCase { } } -extension RecoveryPhraseValidationState { +extension RecoveryPhraseValidationFlowState { static func initial( phrase: RecoveryPhrase, missingIndices: [Int], missingWordsChips: [PhraseChip.Kind] ) -> Self { - RecoveryPhraseValidationState( + RecoveryPhraseValidationFlowState( phrase: phrase, missingIndices: missingIndices, missingWordChips: missingWordsChips, diff --git a/secantTests/SendTests/SendTests.swift b/secantTests/SendTests/SendTests.swift index d683299..388364e 100644 --- a/secantTests/SendTests/SendTests.swift +++ b/secantTests/SendTests/SendTests.swift @@ -31,17 +31,17 @@ class SendTests: XCTestCase { // setup the store and environment to be fully mocked let testScheduler = DispatchQueue.test - let testEnvironment = SendEnvironment( + let testEnvironment = SendFlowEnvironment( mnemonicSeedPhraseProvider: .mock, scheduler: testScheduler.eraseToAnyScheduler(), walletStorage: .live(walletStorage: storage), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: MockWrappedSDKSynchronizer() + derivationTool: .live(), + SDKSynchronizer: MockWrappedSDKSynchronizer() ) let store = TestStore( initialState: .placeholder, - reducer: SendReducer.default, + reducer: SendFlowReducer.default, environment: testEnvironment ) @@ -87,17 +87,17 @@ class SendTests: XCTestCase { // setup the store and environment to be fully mocked let testScheduler = DispatchQueue.test - let testEnvironment = SendEnvironment( + let testEnvironment = SendFlowEnvironment( mnemonicSeedPhraseProvider: .mock, scheduler: testScheduler.eraseToAnyScheduler(), walletStorage: .live(walletStorage: storage), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer() + derivationTool: .live(), + SDKSynchronizer: TestWrappedSDKSynchronizer() ) let store = TestStore( initialState: .placeholder, - reducer: SendReducer.default, + reducer: SendFlowReducer.default, environment: testEnvironment ) @@ -126,17 +126,17 @@ class SendTests: XCTestCase { func testAddressValidation() throws { let testScheduler = DispatchQueue.test - let testEnvironment = SendEnvironment( + let testEnvironment = SendFlowEnvironment( mnemonicSeedPhraseProvider: .mock, scheduler: testScheduler.eraseToAnyScheduler(), walletStorage: .live(walletStorage: WalletStorage(secItem: .live)), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer() + derivationTool: .live(), + SDKSynchronizer: TestWrappedSDKSynchronizer() ) let store = TestStore( initialState: .placeholder, - reducer: SendReducer.default, + reducer: SendFlowReducer.default, environment: testEnvironment ) @@ -168,17 +168,17 @@ class SendTests: XCTestCase { func testInvalidAmountFormatEmptyInput() throws { let testScheduler = DispatchQueue.test - let testEnvironment = SendEnvironment( + let testEnvironment = SendFlowEnvironment( mnemonicSeedPhraseProvider: .mock, scheduler: testScheduler.eraseToAnyScheduler(), walletStorage: .live(walletStorage: WalletStorage(secItem: .live)), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer() + derivationTool: .live(), + SDKSynchronizer: TestWrappedSDKSynchronizer() ) let store = TestStore( initialState: .placeholder, - reducer: SendReducer.default, + reducer: SendFlowReducer.default, environment: testEnvironment ) @@ -197,17 +197,17 @@ class SendTests: XCTestCase { func testInvalidAddressFormatEmptyInput() throws { let testScheduler = DispatchQueue.test - let testEnvironment = SendEnvironment( + let testEnvironment = SendFlowEnvironment( mnemonicSeedPhraseProvider: .mock, scheduler: testScheduler.eraseToAnyScheduler(), walletStorage: .live(walletStorage: WalletStorage(secItem: .live)), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer() + derivationTool: .live(), + SDKSynchronizer: TestWrappedSDKSynchronizer() ) let store = TestStore( initialState: .placeholder, - reducer: SendReducer.default, + reducer: SendFlowReducer.default, environment: testEnvironment ) @@ -228,11 +228,11 @@ class SendTests: XCTestCase { func testFundsSufficiency() throws { try XCTSkipUnless(Locale.current.regionCode == "US", "testFundsSufficiency is designed to test US locale only") - let sendState = SendState( + let sendState = SendFlowState( transaction: .placeholder, transactionAddressInputState: .placeholder, transactionAmountInputState: - TransactionAmountInputState( + TransactionAmountTextFieldState( textFieldState: .amount, currencySelectionState: CurrencySelectionState(), maxValue: 501_300 @@ -241,17 +241,17 @@ class SendTests: XCTestCase { let testScheduler = DispatchQueue.test - let testEnvironment = SendEnvironment( + let testEnvironment = SendFlowEnvironment( mnemonicSeedPhraseProvider: .mock, scheduler: testScheduler.eraseToAnyScheduler(), walletStorage: .live(walletStorage: WalletStorage(secItem: .live)), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer() + derivationTool: .live(), + SDKSynchronizer: TestWrappedSDKSynchronizer() ) let store = TestStore( initialState: sendState, - reducer: SendReducer.default, + reducer: SendFlowReducer.default, environment: testEnvironment ) @@ -279,17 +279,17 @@ class SendTests: XCTestCase { let testScheduler = DispatchQueue.test - let testEnvironment = SendEnvironment( + let testEnvironment = SendFlowEnvironment( mnemonicSeedPhraseProvider: .mock, scheduler: testScheduler.eraseToAnyScheduler(), walletStorage: .live(walletStorage: WalletStorage(secItem: .live)), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer() + derivationTool: .live(), + SDKSynchronizer: TestWrappedSDKSynchronizer() ) let store = TestStore( initialState: .placeholder, - reducer: SendReducer.default, + reducer: SendFlowReducer.default, environment: testEnvironment ) @@ -310,13 +310,13 @@ class SendTests: XCTestCase { func testValidForm() throws { try XCTSkipUnless(Locale.current.regionCode == "US", "testValidForm is designed to test US locale only") - let sendState = SendState( + let sendState = SendFlowState( transaction: .placeholder, transactionAddressInputState: .placeholder, transactionAmountInputState: - TransactionAmountInputState( + TransactionAmountTextFieldState( textFieldState: - TextFieldState( + TCATextFieldState( validationType: .floatingPoint, text: "0.00501301" ), @@ -327,17 +327,17 @@ class SendTests: XCTestCase { let testScheduler = DispatchQueue.test - let testEnvironment = SendEnvironment( + let testEnvironment = SendFlowEnvironment( mnemonicSeedPhraseProvider: .mock, scheduler: testScheduler.eraseToAnyScheduler(), walletStorage: .live(walletStorage: WalletStorage(secItem: .live)), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer() + derivationTool: .live(), + SDKSynchronizer: TestWrappedSDKSynchronizer() ) let store = TestStore( initialState: sendState, - reducer: SendReducer.default, + reducer: SendFlowReducer.default, environment: testEnvironment ) @@ -355,13 +355,13 @@ class SendTests: XCTestCase { } func testInvalidForm_InsufficientFunds() throws { - let sendState = SendState( + let sendState = SendFlowState( transaction: .placeholder, transactionAddressInputState: .placeholder, transactionAmountInputState: - TransactionAmountInputState( + TransactionAmountTextFieldState( textFieldState: - TextFieldState( + TCATextFieldState( validationType: .floatingPoint, text: "0.00501301" ), @@ -372,17 +372,17 @@ class SendTests: XCTestCase { let testScheduler = DispatchQueue.test - let testEnvironment = SendEnvironment( + let testEnvironment = SendFlowEnvironment( mnemonicSeedPhraseProvider: .mock, scheduler: testScheduler.eraseToAnyScheduler(), walletStorage: .live(walletStorage: WalletStorage(secItem: .live)), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer() + derivationTool: .live(), + SDKSynchronizer: TestWrappedSDKSynchronizer() ) let store = TestStore( initialState: sendState, - reducer: SendReducer.default, + reducer: SendFlowReducer.default, environment: testEnvironment ) @@ -400,13 +400,13 @@ class SendTests: XCTestCase { } func testInvalidForm_AddressFormat() throws { - let sendState = SendState( + let sendState = SendFlowState( transaction: .placeholder, transactionAddressInputState: .placeholder, transactionAmountInputState: - TransactionAmountInputState( + TransactionAmountTextFieldState( textFieldState: - TextFieldState( + TCATextFieldState( validationType: .floatingPoint, text: "0.00501301" ), @@ -417,17 +417,17 @@ class SendTests: XCTestCase { let testScheduler = DispatchQueue.test - let testEnvironment = SendEnvironment( + let testEnvironment = SendFlowEnvironment( mnemonicSeedPhraseProvider: .mock, scheduler: testScheduler.eraseToAnyScheduler(), walletStorage: .live(walletStorage: WalletStorage(secItem: .live)), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer() + derivationTool: .live(), + SDKSynchronizer: TestWrappedSDKSynchronizer() ) let store = TestStore( initialState: sendState, - reducer: SendReducer.default, + reducer: SendFlowReducer.default, environment: testEnvironment ) @@ -445,13 +445,13 @@ class SendTests: XCTestCase { } func testInvalidForm_AmountFormat() throws { - let sendState = SendState( + let sendState = SendFlowState( transaction: .placeholder, transactionAddressInputState: .placeholder, transactionAmountInputState: - TransactionAmountInputState( + TransactionAmountTextFieldState( textFieldState: - TextFieldState( + TCATextFieldState( validationType: .floatingPoint, text: "0.0.0501301" ), @@ -462,17 +462,17 @@ class SendTests: XCTestCase { let testScheduler = DispatchQueue.test - let testEnvironment = SendEnvironment( + let testEnvironment = SendFlowEnvironment( mnemonicSeedPhraseProvider: .mock, scheduler: testScheduler.eraseToAnyScheduler(), walletStorage: .live(walletStorage: WalletStorage(secItem: .live)), - wrappedDerivationTool: .live(), - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer() + derivationTool: .live(), + SDKSynchronizer: TestWrappedSDKSynchronizer() ) let store = TestStore( initialState: sendState, - reducer: SendReducer.default, + reducer: SendFlowReducer.default, environment: testEnvironment ) @@ -495,7 +495,7 @@ private extension SendTests { _ amount: String, _ expectedValidationResult: Bool, _ expectedAmount: Int64, - _ store: TestStore + _ store: TestStore ) throws { store.send(.transactionAmountInput(.textField(.set(amount)))) { state in state.transactionAmountInputState.textFieldState.text = amount diff --git a/secantTests/SendTests/TransactionAddressInputTests.swift b/secantTests/SendTests/TransactionAddressInputTests.swift index 5b17d2f..fc08aad 100644 --- a/secantTests/SendTests/TransactionAddressInputTests.swift +++ b/secantTests/SendTests/TransactionAddressInputTests.swift @@ -1,5 +1,5 @@ // -// TransactionAddressInputTests.swift +// TransactionAddressTextFieldTests.swift // secantTests // // Created by Lukáš Korba on 06.05.2022. @@ -9,21 +9,21 @@ import XCTest @testable import secant_testnet import ComposableArchitecture -class TransactionAddressInputTests: XCTestCase { +class TransactionAddressTextFieldTests: XCTestCase { func testClearValue() throws { let store = TestStore( initialState: - TransactionAddressInputState( + TransactionAddressTextFieldState( textFieldState: - TextFieldState( + TCATextFieldState( validationType: nil, text: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po" ) ), - reducer: TransactionAddressInputReducer.default, + reducer: TransactionAddressTextFieldReducer.default, environment: - TransactionAddressInputEnvironment( - wrappedDerivationTool: .live() + TransactionAddressTextFieldEnvironment( + derivationTool: .live() ) ) diff --git a/secantTests/SendTests/TransactionAmountInputTests.swift b/secantTests/SendTests/TransactionAmountInputTests.swift index e3e068b..9ed7c38 100644 --- a/secantTests/SendTests/TransactionAmountInputTests.swift +++ b/secantTests/SendTests/TransactionAmountInputTests.swift @@ -1,5 +1,5 @@ // -// TransactionAmountInputTests.swift +// TransactionAmountTextFieldTests.swift // secantTests // // Created by Lukáš Korba on 06.05.2022. @@ -13,23 +13,23 @@ import ComposableArchitecture // TODO: these test will be updated with the NumberFormater dependency to handle locale, issue #312 (https://github.com/zcash/secant-ios-wallet/issues/312) -class TransactionAmountInputTests: XCTestCase { +class TransactionAmountTextFieldTests: XCTestCase { func testMaxValue() throws { try XCTSkipUnless(Locale.current.regionCode == "US", "testMaxValue is designed to test US locale only") let store = TestStore( initialState: - TransactionAmountInputState( + TransactionAmountTextFieldState( textFieldState: - TextFieldState( + TCATextFieldState( validationType: .floatingPoint, text: "0.002" ), currencySelectionState: CurrencySelectionState(), maxValue: 501_301 ), - reducer: TransactionAmountInputReducer.default, - environment: TransactionAmountInputEnvironment() + reducer: TransactionAmountTextFieldReducer.default, + environment: TransactionAmountTextFieldEnvironment() ) store.send(.setMax) { state in @@ -41,17 +41,17 @@ class TransactionAmountInputTests: XCTestCase { func testClearValue() throws { let store = TestStore( initialState: - TransactionAmountInputState( + TransactionAmountTextFieldState( textFieldState: - TextFieldState( + TCATextFieldState( validationType: .floatingPoint, text: "0.002" ), currencySelectionState: CurrencySelectionState(), maxValue: 501_301 ), - reducer: TransactionAmountInputReducer.default, - environment: TransactionAmountInputEnvironment() + reducer: TransactionAmountTextFieldReducer.default, + environment: TransactionAmountTextFieldEnvironment() ) store.send(.clearValue) { state in @@ -65,9 +65,9 @@ class TransactionAmountInputTests: XCTestCase { let store = TestStore( initialState: - TransactionAmountInputState( + TransactionAmountTextFieldState( textFieldState: - TextFieldState( + TCATextFieldState( validationType: .floatingPoint, text: "1.0" ), @@ -77,8 +77,8 @@ class TransactionAmountInputTests: XCTestCase { ), zecPrice: 1000.0 ), - reducer: TransactionAmountInputReducer.default, - environment: TransactionAmountInputEnvironment() + reducer: TransactionAmountTextFieldReducer.default, + environment: TransactionAmountTextFieldEnvironment() ) store.send(.currencySelection(.swapCurrencyType)) { state in @@ -97,9 +97,9 @@ class TransactionAmountInputTests: XCTestCase { let store = TestStore( initialState: - TransactionAmountInputState( + TransactionAmountTextFieldState( textFieldState: - TextFieldState( + TCATextFieldState( validationType: .floatingPoint, text: "1 000" ), @@ -109,8 +109,8 @@ class TransactionAmountInputTests: XCTestCase { ), zecPrice: 1000.0 ), - reducer: TransactionAmountInputReducer.default, - environment: TransactionAmountInputEnvironment() + reducer: TransactionAmountTextFieldReducer.default, + environment: TransactionAmountTextFieldEnvironment() ) store.send(.currencySelection(.swapCurrencyType)) { state in @@ -129,9 +129,9 @@ class TransactionAmountInputTests: XCTestCase { let store = TestStore( initialState: - TransactionAmountInputState( + TransactionAmountTextFieldState( textFieldState: - TextFieldState( + TCATextFieldState( validationType: .floatingPoint, text: "5" ), @@ -142,8 +142,8 @@ class TransactionAmountInputTests: XCTestCase { maxValue: 100_000_000, zecPrice: 1000.0 ), - reducer: TransactionAmountInputReducer.default, - environment: TransactionAmountInputEnvironment() + reducer: TransactionAmountTextFieldReducer.default, + environment: TransactionAmountTextFieldEnvironment() ) store.send(.textField(.set("1 000"))) { state in @@ -162,9 +162,9 @@ class TransactionAmountInputTests: XCTestCase { let store = TestStore( initialState: - TransactionAmountInputState( + TransactionAmountTextFieldState( textFieldState: - TextFieldState( + TCATextFieldState( validationType: .floatingPoint, text: "5" ), @@ -175,8 +175,8 @@ class TransactionAmountInputTests: XCTestCase { maxValue: 200_000_000, zecPrice: 1000.0 ), - reducer: TransactionAmountInputReducer.default, - environment: TransactionAmountInputEnvironment() + reducer: TransactionAmountTextFieldReducer.default, + environment: TransactionAmountTextFieldEnvironment() ) store.send(.setMax) { state in @@ -194,9 +194,9 @@ class TransactionAmountInputTests: XCTestCase { let store = TestStore( initialState: - TransactionAmountInputState( + TransactionAmountTextFieldState( textFieldState: - TextFieldState( + TCATextFieldState( validationType: .floatingPoint, text: "5" ), @@ -207,8 +207,8 @@ class TransactionAmountInputTests: XCTestCase { maxValue: 200_000_000, zecPrice: 1000.0 ), - reducer: TransactionAmountInputReducer.default, - environment: TransactionAmountInputEnvironment() + reducer: TransactionAmountTextFieldReducer.default, + environment: TransactionAmountTextFieldEnvironment() ) store.send(.setMax) { state in diff --git a/secantTests/TransactionHistoryTests/TransactionHistoryTests.swift b/secantTests/TransactionHistoryTests/TransactionHistoryTests.swift index 599c027..144df99 100644 --- a/secantTests/TransactionHistoryTests/TransactionHistoryTests.swift +++ b/secantTests/TransactionHistoryTests/TransactionHistoryTests.swift @@ -12,19 +12,19 @@ import ComposableArchitecture class TransactionHistoryTests: XCTestCase { static let testScheduler = DispatchQueue.test - let testEnvironment = TransactionHistoryEnvironment( + let testEnvironment = TransactionHistoryFlowEnvironment( scheduler: testScheduler.eraseToAnyScheduler(), - wrappedSDKSynchronizer: TestWrappedSDKSynchronizer() + SDKSynchronizer: TestWrappedSDKSynchronizer() ) func testSynchronizerSubscription() throws { let store = TestStore( - initialState: TransactionHistoryState( + initialState: TransactionHistoryFlowState( route: .latest, isScrollable: true, transactions: [] ), - reducer: TransactionHistoryReducer.default, + reducer: TransactionHistoryFlowReducer.default, environment: testEnvironment ) @@ -63,12 +63,12 @@ class TransactionHistoryTests: XCTestCase { let identifiedTransactions = IdentifiedArrayOf(uniqueElements: transactions) let store = TestStore( - initialState: TransactionHistoryState( + initialState: TransactionHistoryFlowState( route: .latest, isScrollable: true, transactions: identifiedTransactions ), - reducer: TransactionHistoryReducer.default, + reducer: TransactionHistoryFlowReducer.default, environment: testEnvironment ) diff --git a/secantTests/UtilTests/DatabaseFilesTests.swift b/secantTests/UtilTests/DatabaseFilesTests.swift index 2baae8a..7d20dd4 100644 --- a/secantTests/UtilTests/DatabaseFilesTests.swift +++ b/secantTests/UtilTests/DatabaseFilesTests.swift @@ -36,7 +36,7 @@ class DatabaseFilesTests: XCTestCase { removeItem: { _ in } ) - let dfInteractor = DatabaseFilesInteractor.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) + let dfInteractor = WrappedDatabaseFiles.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) do { _ = try dfInteractor.documentsDirectory() @@ -64,7 +64,7 @@ class DatabaseFilesTests: XCTestCase { removeItem: { _ in } ) - let dfInteractor = DatabaseFilesInteractor.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) + let dfInteractor = WrappedDatabaseFiles.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) do { _ = try dfInteractor.dataDbURLFor(network) @@ -92,7 +92,7 @@ class DatabaseFilesTests: XCTestCase { removeItem: { _ in } ) - let dfInteractor = DatabaseFilesInteractor.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) + let dfInteractor = WrappedDatabaseFiles.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) do { let areFilesPresent = try dfInteractor.areDbFilesPresentFor(network) @@ -110,7 +110,7 @@ class DatabaseFilesTests: XCTestCase { removeItem: { _ in } ) - let dfInteractor = DatabaseFilesInteractor.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) + let dfInteractor = WrappedDatabaseFiles.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) do { let areFilesPresent = try dfInteractor.areDbFilesPresentFor(network) @@ -128,7 +128,7 @@ class DatabaseFilesTests: XCTestCase { removeItem: { _ in } ) - let dfInteractor = DatabaseFilesInteractor.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) + let dfInteractor = WrappedDatabaseFiles.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) do { _ = try dfInteractor.areDbFilesPresentFor(network) @@ -156,7 +156,7 @@ class DatabaseFilesTests: XCTestCase { removeItem: { _ in throw "some error" } ) - let dfInteractor = DatabaseFilesInteractor.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) + let dfInteractor = WrappedDatabaseFiles.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) do { _ = try dfInteractor.nukeDbFilesFor(network) @@ -184,7 +184,7 @@ class DatabaseFilesTests: XCTestCase { removeItem: { _ in } ) - let dfInteractor = DatabaseFilesInteractor.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) + let dfInteractor = WrappedDatabaseFiles.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager)) do { _ = try dfInteractor.nukeDbFilesFor(network) diff --git a/swiftgen.yml b/swiftgen.yml index d371e28..0ed42b0 100644 --- a/swiftgen.yml +++ b/swiftgen.yml @@ -1,5 +1,5 @@ -input_dir: secant/ -output_dir: secant/Generated/ +input_dir: secant/Resources/ +output_dir: secant/Resources/Generated/ xcassets: inputs: