First Stab at The Composable Architecture
Initial Light Mode Recovery Phrase Backup Screen Dark mode color adjustments Quotes "Navigation Bar Title" Fix Recovery Phrase chips throw a UI Thread warning Integrate Copy to buffer button create test build 4 for UX validation FIX: Archiving compilation errors fix warnings. add TODOs for demo code that is needed to build on release mode PR Fixes: remove dump calles add TODOs remove unneeded padding remove prints PR fixes: remove min height from standard button style. change extension from View to Text change comment format PR Fixes. code style Add ticket number to TODOs Rename "Backup Flow" to BackupFlow PR lint fixes Add tests Fix lint issue. cleanup rename "UI Components" to "UIComponents" Renamed folder with spaces to CamelCase names: "App Errors" to "AppErrors" "Mocked Dependencies" to "MockedDependencies" Renamed "Font Styles" to "FontStyles" hook up to home screen Adding: [Suggestion(adding pasteboard to environment)] Implement [Suggestion(use specific RecoveryPhraseError) | non-blocking] Remove double carriage return and replace by VStack of Text() add App Uses Non-Exempt Encryption -> NO value to Info.plist bump build # 0.0.1-7 make view modifiers private move modifiers into extension Testable Pasteboard Fix: Word groups don't have noticeable spacing that allows the user to tell them apert FIX: don't truncate enumerated chips
This commit is contained in:
parent
0500f5db68
commit
5646afbdd5
|
@ -14,6 +14,7 @@
|
|||
0D1922ED26BDE0C600052649 /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1922EC26BDE0C600052649 /* AppRouter.swift */; };
|
||||
0D1922F226BDE29300052649 /* ZcashSDKStubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1922F126BDE29300052649 /* ZcashSDKStubs.swift */; };
|
||||
0D1922F826BDEB3500052649 /* MockServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1922F726BDEB3500052649 /* MockServices.swift */; };
|
||||
0D1C1AA327611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1C1AA227611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift */; };
|
||||
0D2ACE8026C2C67100D62E3C /* Zboto.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0D2ACE7F26C2C67100D62E3C /* Zboto.otf */; };
|
||||
0D32281926C5864B00262533 /* ProfileScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32281726C5864B00262533 /* ProfileScreen.swift */; };
|
||||
0D32281A26C5864B00262533 /* ProfileScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32281826C5864B00262533 /* ProfileScreenViewModel.swift */; };
|
||||
|
@ -30,6 +31,8 @@
|
|||
0D354A0926D5A9D000315F45 /* Services.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D354A0626D5A9D000315F45 /* Services.swift */; };
|
||||
0D354A0A26D5A9D000315F45 /* KeyStoring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D354A0726D5A9D000315F45 /* KeyStoring.swift */; };
|
||||
0D354A0B26D5A9D000315F45 /* MnemonicSeedPhraseHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D354A0826D5A9D000315F45 /* MnemonicSeedPhraseHandling.swift */; };
|
||||
0D3D04082728B3440032ABC1 /* RecoveryPhraseDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D3D04072728B3440032ABC1 /* RecoveryPhraseDisplayView.swift */; };
|
||||
0D3D040A2728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D3D04092728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift */; };
|
||||
0D4E7A0926B364170058B01E /* SecantApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4E7A0826B364170058B01E /* SecantApp.swift */; };
|
||||
0D4E7A0B26B364170058B01E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4E7A0A26B364170058B01E /* ContentView.swift */; };
|
||||
0D4E7A0D26B364180058B01E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0D4E7A0C26B364180058B01E /* Assets.xcassets */; };
|
||||
|
@ -46,6 +49,8 @@
|
|||
0D864A0A26E154FD00A61879 /* InitFailedScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D864A0826E154FD00A61879 /* InitFailedScreenViewModel.swift */; };
|
||||
0D864A0E26E1583000A61879 /* LoadingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D864A0C26E1583000A61879 /* LoadingScreen.swift */; };
|
||||
0D864A0F26E1583000A61879 /* LoadingScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D864A0D26E1583000A61879 /* LoadingScreenViewModel.swift */; };
|
||||
0D8A43C4272AEEDE005A6414 /* SecantTextStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D8A43C3272AEEDE005A6414 /* SecantTextStyles.swift */; };
|
||||
0D8A43C6272B129C005A6414 /* WordChipGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D8A43C5272B129C005A6414 /* WordChipGrid.swift */; };
|
||||
0DA13C8F26C15D1D00E3B610 /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA13C8D26C15D1D00E3B610 /* WelcomeScreen.swift */; };
|
||||
0DA13C9026C15D1D00E3B610 /* WelcomeScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA13C8E26C15D1D00E3B610 /* WelcomeScreenViewModel.swift */; };
|
||||
0DA13C9726C186FF00E3B610 /* RestoreWalletScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA13C9526C186FF00E3B610 /* RestoreWalletScreen.swift */; };
|
||||
|
@ -72,6 +77,7 @@
|
|||
0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB8AA80271DC7520035BC9D /* DesignGuide.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 */; };
|
||||
0DFE93DF272C6D4B000FCCA5 /* RecoveryFlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DFE93DE272C6D4B000FCCA5 /* RecoveryFlowTests.swift */; };
|
||||
2E58E73B274679F000B2B84B /* OnboardingHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E58E73A274679F000B2B84B /* OnboardingHeaderView.swift */; };
|
||||
2EA11F5B27467EF800709571 /* OnboardingFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EA11F5A27467EF800709571 /* OnboardingFooterView.swift */; };
|
||||
2EA11F5D27467F7700709571 /* OnboardingContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EA11F5C27467F7700709571 /* OnboardingContentView.swift */; };
|
||||
|
@ -135,6 +141,7 @@
|
|||
0D1922EC26BDE0C600052649 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = "<group>"; };
|
||||
0D1922F126BDE29300052649 /* ZcashSDKStubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZcashSDKStubs.swift; sourceTree = "<group>"; };
|
||||
0D1922F726BDEB3500052649 /* MockServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockServices.swift; sourceTree = "<group>"; };
|
||||
0D1C1AA227611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseDisplayReducerTests.swift; sourceTree = "<group>"; };
|
||||
0D2ACE7F26C2C67100D62E3C /* Zboto.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Zboto.otf; sourceTree = "<group>"; };
|
||||
0D32281726C5864B00262533 /* ProfileScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileScreen.swift; sourceTree = "<group>"; };
|
||||
0D32281826C5864B00262533 /* ProfileScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
|
@ -151,6 +158,8 @@
|
|||
0D354A0626D5A9D000315F45 /* Services.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Services.swift; sourceTree = "<group>"; };
|
||||
0D354A0726D5A9D000315F45 /* KeyStoring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyStoring.swift; sourceTree = "<group>"; };
|
||||
0D354A0826D5A9D000315F45 /* MnemonicSeedPhraseHandling.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MnemonicSeedPhraseHandling.swift; sourceTree = "<group>"; };
|
||||
0D3D04072728B3440032ABC1 /* RecoveryPhraseDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseDisplayView.swift; sourceTree = "<group>"; };
|
||||
0D3D04092728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseDisplayStore.swift; sourceTree = "<group>"; };
|
||||
0D4E7A0526B364170058B01E /* secant-testnet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "secant-testnet.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0D4E7A0826B364170058B01E /* SecantApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecantApp.swift; sourceTree = "<group>"; };
|
||||
0D4E7A0A26B364170058B01E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
|
@ -173,6 +182,8 @@
|
|||
0D864A0826E154FD00A61879 /* InitFailedScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitFailedScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
0D864A0C26E1583000A61879 /* LoadingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingScreen.swift; sourceTree = "<group>"; };
|
||||
0D864A0D26E1583000A61879 /* LoadingScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
0D8A43C3272AEEDE005A6414 /* SecantTextStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecantTextStyles.swift; sourceTree = "<group>"; };
|
||||
0D8A43C5272B129C005A6414 /* WordChipGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordChipGrid.swift; sourceTree = "<group>"; };
|
||||
0DA13C8D26C15D1D00E3B610 /* WelcomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreen.swift; sourceTree = "<group>"; };
|
||||
0DA13C8E26C15D1D00E3B610 /* WelcomeScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
0DA13C9526C186FF00E3B610 /* RestoreWalletScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreWalletScreen.swift; sourceTree = "<group>"; };
|
||||
|
@ -199,6 +210,7 @@
|
|||
0DB8AA80271DC7520035BC9D /* DesignGuide.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DesignGuide.swift; sourceTree = "<group>"; };
|
||||
0DF2DC50272344E400FA31E2 /* EmptyChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyChip.swift; sourceTree = "<group>"; };
|
||||
0DF2DC5327235E3E00FA31E2 /* View+InnerShadow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+InnerShadow.swift"; sourceTree = "<group>"; };
|
||||
0DFE93DE272C6D4B000FCCA5 /* RecoveryFlowTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryFlowTests.swift; sourceTree = "<group>"; };
|
||||
2E58E73A274679F000B2B84B /* OnboardingHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingHeaderView.swift; sourceTree = "<group>"; };
|
||||
2E5C03802738C570008BFFD3 /* OnboardingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingScreen.swift; sourceTree = "<group>"; };
|
||||
2EA11F5A27467EF800709571 /* OnboardingFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingFooterView.swift; sourceTree = "<group>"; };
|
||||
|
@ -271,14 +283,14 @@
|
|||
path = Routers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D170A7426BC9B7500EB6A46 /* Mocked Dependencies */ = {
|
||||
0D170A7426BC9B7500EB6A46 /* MockedDependencies */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D354A0726D5A9D000315F45 /* KeyStoring.swift */,
|
||||
0D354A0826D5A9D000315F45 /* MnemonicSeedPhraseHandling.swift */,
|
||||
0D354A0626D5A9D000315F45 /* Services.swift */,
|
||||
);
|
||||
path = "Mocked Dependencies";
|
||||
path = MockedDependencies;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D1922E826BDD95000052649 /* Base */ = {
|
||||
|
@ -383,6 +395,24 @@
|
|||
path = Balance;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D3D04052728B2D70032ABC1 /* BackupFlow */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D3D04062728B2EC0032ABC1 /* Views */,
|
||||
0D3D04092728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift */,
|
||||
);
|
||||
path = BackupFlow;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D3D04062728B2EC0032ABC1 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D3D04072728B3440032ABC1 /* RecoveryPhraseDisplayView.swift */,
|
||||
0D8A43C5272B129C005A6414 /* WordChipGrid.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D4E79FC26B364170058B01E = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -410,14 +440,14 @@
|
|||
0DACFA7D27208CC80039EEA5 /* Util */,
|
||||
6654C73B2715A3F000901167 /* Features */,
|
||||
660558F4270C85F7009D6954 /* Generated */,
|
||||
0D5D16F326E24CB900AD33D1 /* App Errors */,
|
||||
0D5D16F326E24CB900AD33D1 /* AppErrors */,
|
||||
0D2ACE7E26C2C65E00D62E3C /* Fonts */,
|
||||
0DA13CA326C1960A00E3B610 /* Models */,
|
||||
0DA13C9126C15E1900E3B610 /* UI Components */,
|
||||
0DA13C9126C15E1900E3B610 /* UIComponents */,
|
||||
0D1922F026BDE27D00052649 /* Stubs */,
|
||||
0D1922EB26BDD9A500052649 /* Screens */,
|
||||
0D1922E826BDD95000052649 /* Base */,
|
||||
0D170A7426BC9B7500EB6A46 /* Mocked Dependencies */,
|
||||
0D170A7426BC9B7500EB6A46 /* MockedDependencies */,
|
||||
0D4E7A0826B364170058B01E /* SecantApp.swift */,
|
||||
0D4E7A0A26B364170058B01E /* ContentView.swift */,
|
||||
0D4E7A0C26B364180058B01E /* Assets.xcassets */,
|
||||
|
@ -440,6 +470,7 @@
|
|||
0D4E7A1926B364180058B01E /* secantTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0DFE93DD272C6D4B000FCCA5 /* BackupFlowTests */,
|
||||
6654C7422715A48E00901167 /* OnboardingTests */,
|
||||
0D4E7A1A26B364180058B01E /* secantTests.swift */,
|
||||
0D4E7A1C26B364180058B01E /* Info.plist */,
|
||||
|
@ -477,12 +508,12 @@
|
|||
path = Chips;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D5D16F326E24CB900AD33D1 /* App Errors */ = {
|
||||
0D5D16F326E24CB900AD33D1 /* AppErrors */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D5D16F426E24CCF00AD33D1 /* AppError.swift */,
|
||||
);
|
||||
path = "App Errors";
|
||||
path = AppErrors;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D864A0626E154D100A61879 /* Error */ = {
|
||||
|
@ -503,6 +534,14 @@
|
|||
path = Loading;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D8A43C2272AEEA7005A6414 /* FontStyles */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D8A43C3272AEEDE005A6414 /* SecantTextStyles.swift */,
|
||||
);
|
||||
path = FontStyles;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0DA13C8C26C15CBE00E3B610 /* Welcome Screen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -512,9 +551,10 @@
|
|||
path = "Welcome Screen";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0DA13C9126C15E1900E3B610 /* UI Components */ = {
|
||||
0DA13C9126C15E1900E3B610 /* UIComponents */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D8A43C2272AEEA7005A6414 /* FontStyles */,
|
||||
669FDAE7272C239D007B9422 /* CircularFrame */,
|
||||
0DB8AA80271DC7520035BC9D /* DesignGuide.swift */,
|
||||
0DF2DC5227235E1F00FA31E2 /* Extensions */,
|
||||
|
@ -523,7 +563,7 @@
|
|||
669FDAE5272C2371007B9422 /* ProgressIndicators */,
|
||||
669FDAE6272C2380007B9422 /* Backgrounds */,
|
||||
);
|
||||
path = "UI Components";
|
||||
path = UIComponents;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0DA13C9426C186B100E3B610 /* Restore Wallet */ = {
|
||||
|
@ -602,6 +642,15 @@
|
|||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0DFE93DD272C6D4B000FCCA5 /* BackupFlowTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0DFE93DE272C6D4B000FCCA5 /* RecoveryFlowTests.swift */,
|
||||
0D1C1AA227611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift */,
|
||||
);
|
||||
path = BackupFlowTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2E5C037F2738C55F008BFFD3 /* Onboarding */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -649,6 +698,7 @@
|
|||
F93874EC273C4DE200F0E875 /* Home */,
|
||||
F9C165B62740403600592F76 /* Send */,
|
||||
F96B41E2273B501F0021B49A /* TransactionHistory */,
|
||||
0D3D04052728B2D70032ABC1 /* BackupFlow */,
|
||||
6654C73C2715A3FA00901167 /* Onboarding */,
|
||||
);
|
||||
path = Features;
|
||||
|
@ -967,6 +1017,7 @@
|
|||
0D32282D26C5870B00262533 /* SendScreen.swift in Sources */,
|
||||
663FABA2271D876C00E495F8 /* SecondaryButton.swift in Sources */,
|
||||
0D1922ED26BDE0C600052649 /* AppRouter.swift in Sources */,
|
||||
0D8A43C4272AEEDE005A6414 /* SecantTextStyles.swift in Sources */,
|
||||
0D1922F226BDE29300052649 /* ZcashSDKStubs.swift in Sources */,
|
||||
0DA13C9D26C1942100E3B610 /* BackupWalletScreenViewModel.swift in Sources */,
|
||||
0DACFA7F27208CE00039EEA5 /* Clamped.swift in Sources */,
|
||||
|
@ -989,11 +1040,13 @@
|
|||
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */,
|
||||
F93874F1273C4DE200F0E875 /* HomeView.swift in Sources */,
|
||||
0D32282326C586A800262533 /* HistoryScreen.swift in Sources */,
|
||||
0D3D04082728B3440032ABC1 /* RecoveryPhraseDisplayView.swift in Sources */,
|
||||
0D864A0A26E154FD00A61879 /* InitFailedScreenViewModel.swift in Sources */,
|
||||
0DA13CA526C1963000E3B610 /* Balance.swift in Sources */,
|
||||
2EA11F5B27467EF800709571 /* OnboardingFooterView.swift in Sources */,
|
||||
66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */,
|
||||
0D1922F826BDEB3500052649 /* MockServices.swift in Sources */,
|
||||
0D3D040A2728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift in Sources */,
|
||||
0D4E7A0B26B364170058B01E /* ContentView.swift in Sources */,
|
||||
0D170A7226BC802800EB6A46 /* Router.swift in Sources */,
|
||||
0D354A0926D5A9D000315F45 /* Services.swift in Sources */,
|
||||
|
@ -1018,6 +1071,7 @@
|
|||
0DA13CA126C1955600E3B610 /* HomeScreen.swift in Sources */,
|
||||
0DA13C9026C15D1D00E3B610 /* WelcomeScreenViewModel.swift in Sources */,
|
||||
2E58E73B274679F000B2B84B /* OnboardingHeaderView.swift in Sources */,
|
||||
0D8A43C6272B129C005A6414 /* WordChipGrid.swift in Sources */,
|
||||
66A0807B271993C500118B79 /* OnboardingProgressIndicator.swift in Sources */,
|
||||
663FAB9E271D875700E495F8 /* CreateButton.swift in Sources */,
|
||||
0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */,
|
||||
|
@ -1042,8 +1096,10 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0DFE93DF272C6D4B000FCCA5 /* RecoveryFlowTests.swift in Sources */,
|
||||
0D864A0526E1546000A61879 /* LoadingScreenTests.swift in Sources */,
|
||||
6654C7442715A4AC00901167 /* OnboardingStoreTests.swift in Sources */,
|
||||
0D1C1AA327611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift in Sources */,
|
||||
0D4E7A1B26B364180058B01E /* secantTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1194,9 +1250,9 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 5;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DEVELOPMENT_TEAM = RLPRR8CPQG;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = secant/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
|
@ -1218,9 +1274,9 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 5;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DEVELOPMENT_TEAM = RLPRR8CPQG;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = secant/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
enum AppError: Error {
|
||||
case failedToInitialize(Error)
|
||||
enum AppError: Error, Equatable {
|
||||
case failedToInitialize
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFB",
|
||||
"green" : "0xF5",
|
||||
"red" : "0xEE"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x62",
|
||||
"green" : "0x3D",
|
||||
"red" : "0x30"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -23,9 +23,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x57",
|
||||
"green" : "0x33",
|
||||
"red" : "0x26"
|
||||
"blue" : "0.000",
|
||||
"green" : "0.725",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
"blue" : "0.341",
|
||||
"green" : "0.200",
|
||||
"red" : "0.149"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
"blue" : "0.333",
|
||||
"green" : "0.192",
|
||||
"red" : "0.141"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.000",
|
||||
"green" : "0.000",
|
||||
"red" : "0.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -23,9 +23,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x41",
|
||||
"green" : "0x23",
|
||||
"red" : "0x0F"
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
|
|
@ -19,6 +19,15 @@
|
|||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.000",
|
||||
"green" : "0.725",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
//
|
||||
// RecoveryPhraseDisplayStore.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Francisco Gindre on 10/26/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ComposableArchitecture
|
||||
import UIKit
|
||||
|
||||
enum RecoveryPhraseError: Error {
|
||||
/// This error is thrown then the Recovery Phrase can't be generated
|
||||
case unableToGeneratePhrase
|
||||
}
|
||||
|
||||
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 {
|
||||
let mainQueue: AnySchedulerOf<DispatchQueue>
|
||||
let newPhrase: () -> Effect<RecoveryPhrase, RecoveryPhraseError>
|
||||
let pasteboard: Pasteboard
|
||||
}
|
||||
|
||||
extension BackupPhraseEnvironment {
|
||||
private struct DemoPasteboard {
|
||||
static var general = Self()
|
||||
var string: String?
|
||||
}
|
||||
|
||||
static let demo = Self(
|
||||
mainQueue: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
newPhrase: { Effect(value: .init(words: RecoveryPhrase.demo.words)) },
|
||||
pasteboard: .test
|
||||
)
|
||||
|
||||
static let live = Self(
|
||||
mainQueue: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
newPhrase: { Effect(value: .init(words: RecoveryPhrase.demo.words)) },
|
||||
pasteboard: .live
|
||||
)
|
||||
}
|
||||
|
||||
typealias RecoveryPhraseDisplayStore = Store<RecoveryPhraseDisplayState, RecoveryPhraseDisplayAction>
|
||||
|
||||
struct RecoveryPhrase: Equatable {
|
||||
struct Chunk: Hashable {
|
||||
var startIndex: Int
|
||||
var words: [String]
|
||||
}
|
||||
|
||||
let words: [String]
|
||||
|
||||
private let chunkSize = 6
|
||||
|
||||
func toChunks() -> [Chunk] {
|
||||
let chunks = words.count / chunkSize
|
||||
return zip(0 ..< chunks, words.chunked(into: chunkSize)).map {
|
||||
Chunk(startIndex: $0 * chunkSize + 1, words: $1)
|
||||
}
|
||||
}
|
||||
|
||||
func toString() -> String {
|
||||
words.joined(separator: " ")
|
||||
}
|
||||
}
|
||||
|
||||
struct RecoveryPhraseDisplayState: Equatable {
|
||||
var phrase: RecoveryPhrase?
|
||||
var showCopyToBufferAlert = false
|
||||
}
|
||||
|
||||
enum RecoveryPhraseDisplayAction: Equatable {
|
||||
case createPhrase
|
||||
case copyToBufferPressed
|
||||
case finishedPressed
|
||||
case phraseResponse(Result<RecoveryPhrase, RecoveryPhraseError>)
|
||||
}
|
||||
|
||||
typealias RecoveryPhraseDisplayReducer = Reducer<RecoveryPhraseDisplayState, RecoveryPhraseDisplayAction, BackupPhraseEnvironment>
|
||||
|
||||
extension RecoveryPhraseDisplayReducer {
|
||||
static let `default` = RecoveryPhraseDisplayReducer { state, action, environment in
|
||||
switch action {
|
||||
case .createPhrase:
|
||||
return environment.newPhrase()
|
||||
.receive(on: environment.mainQueue)
|
||||
.catchToEffect(RecoveryPhraseDisplayAction.phraseResponse)
|
||||
case .copyToBufferPressed:
|
||||
guard let phrase = state.phrase?.toString() else { return .none }
|
||||
environment.pasteboard.setString(phrase)
|
||||
state.showCopyToBufferAlert = true
|
||||
return .none
|
||||
case .finishedPressed:
|
||||
// TODO: remove this when feature is implemented in https://github.com/zcash/secant-ios-wallet/issues/47
|
||||
return .none
|
||||
case let .phraseResponse(.success(phrase)):
|
||||
state.phrase = phrase
|
||||
return .none
|
||||
case .phraseResponse(.failure):
|
||||
// TODO: remove this when feature is implemented in https://github.com/zcash/secant-ios-wallet/issues/129
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)])
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// RecoveryPhraseDisplayView.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Francisco Gindre on 10/26/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
struct RecoveryPhraseDisplayView: View {
|
||||
let store: RecoveryPhraseDisplayStore
|
||||
|
||||
var body: some View {
|
||||
WithViewStore(self.store) { viewStore in
|
||||
ScrollView {
|
||||
VStack {
|
||||
if let chunks = viewStore.phrase?.toChunks() {
|
||||
VStack(spacing: 20) {
|
||||
Text("Your Secret Recovery Phrase")
|
||||
.titleText()
|
||||
.multilineTextAlignment(.center)
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("The following 24 words represent your funds and the security used to protect them.")
|
||||
.bodyText()
|
||||
|
||||
Text("Back them up now! There will be a test.")
|
||||
.bodyText()
|
||||
}
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
ForEach(chunks, id: \.startIndex) { chunk in
|
||||
WordChipGrid(words: chunk.words, startingAt: chunk.startIndex)
|
||||
}
|
||||
}
|
||||
|
||||
VStack {
|
||||
Button(
|
||||
action: { viewStore.send(.finishedPressed) },
|
||||
label: { Text("Finished!") }
|
||||
)
|
||||
.activeButtonStyle
|
||||
.frame(height: 60)
|
||||
|
||||
Button(
|
||||
action: {
|
||||
viewStore.send(.copyToBufferPressed)
|
||||
},
|
||||
label: {
|
||||
Text("Copy To Buffer")
|
||||
.bodyText()
|
||||
}
|
||||
)
|
||||
.frame(height: 60)
|
||||
}
|
||||
.padding()
|
||||
} else {
|
||||
Text("Oops no words")
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
|
||||
// TODO: NavigationBar Style
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.applyScreenBackground()
|
||||
}
|
||||
}
|
||||
// TODO: This should have a #DEBUG tag, but if so, it's not possible to compile this on release mode and submit it to testflight
|
||||
extension RecoveryPhraseDisplayStore {
|
||||
static var demo: RecoveryPhraseDisplayStore {
|
||||
RecoveryPhraseDisplayStore(
|
||||
initialState: .init(phrase: .demo),
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This should have a #DEBUG tag, but if so, it's not possible to compile this on release mode and submit it to testflight
|
||||
extension RecoveryPhrase {
|
||||
static let testPhrase = [
|
||||
// 1
|
||||
"bring", "salute", "thank",
|
||||
"require", "spirit", "toe",
|
||||
// 7
|
||||
"boil", "hill", "casino",
|
||||
"trophy", "drink", "frown",
|
||||
// 13
|
||||
"bird", "grit", "close",
|
||||
"morning", "bind", "cancel",
|
||||
// 19
|
||||
"daughter", "salon", "quit",
|
||||
"pizza", "just", "garlic"
|
||||
]
|
||||
|
||||
static let demo = RecoveryPhrase(words: testPhrase)
|
||||
static let empty = RecoveryPhrase(words: [])
|
||||
}
|
||||
|
||||
struct RecoveryPhraseDisplayView_Previews: PreviewProvider {
|
||||
static let scheduler = DispatchQueue.main
|
||||
|
||||
static let store = RecoveryPhraseDisplayStore.demo
|
||||
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
RecoveryPhraseDisplayView(store: store)
|
||||
}
|
||||
|
||||
NavigationView {
|
||||
RecoveryPhraseDisplayView(store: store)
|
||||
}
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// WordChipGrid.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Francisco Gindre on 10/28/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
/**
|
||||
A 3x2 grid of numbered or empty chips.
|
||||
*/
|
||||
struct WordChipGrid: View {
|
||||
static let spacing: CGFloat = 10
|
||||
var chips: [PhraseChip.Kind]
|
||||
|
||||
var threeColumnGrid = Array(
|
||||
repeating: GridItem(
|
||||
.flexible(minimum: 60, maximum: 120),
|
||||
spacing: Self.spacing,
|
||||
alignment: .topLeading
|
||||
),
|
||||
count: 3
|
||||
)
|
||||
|
||||
var body: some View {
|
||||
LazyVGrid(
|
||||
columns: threeColumnGrid,
|
||||
alignment: .leading,
|
||||
spacing: Self.spacing
|
||||
) {
|
||||
ForEach(chips, id: \.self) { wordChip in
|
||||
chipView(for: wordChip)
|
||||
.frame(
|
||||
minWidth: 0,
|
||||
maxWidth: .infinity,
|
||||
minHeight: 30
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(words: [String], startingAt index: Int) {
|
||||
self.chips = zip(words, index..<index + words.count).map({ word, index in
|
||||
word.isEmpty ? .empty : .ordered(position: index, word: word)
|
||||
})
|
||||
}
|
||||
|
||||
@ViewBuilder func chipView(for chipKind: PhraseChip.Kind) -> some View {
|
||||
switch chipKind {
|
||||
case .empty:
|
||||
EmptyChip()
|
||||
|
||||
case let .ordered(position, word):
|
||||
EnumeratedChip(index: position, text: word)
|
||||
|
||||
case .unassigned(let word):
|
||||
BlueChip(word: word)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct WordChipGrid_Previews: PreviewProvider {
|
||||
private static var words = [
|
||||
"pyramid", "negative", "page",
|
||||
"crown", "", "zebra"
|
||||
]
|
||||
|
||||
static var previews: some View {
|
||||
VStack {
|
||||
WordChipGrid(words: words, startingAt: 1)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ struct HomeState: Equatable {
|
|||
case history
|
||||
case send
|
||||
case onboarding
|
||||
case recoveryPhraseDisplay
|
||||
}
|
||||
var transactionHistoryState: TransactionHistoryState
|
||||
var route: Route?
|
||||
|
@ -91,6 +92,15 @@ extension HomeViewStore {
|
|||
)
|
||||
}
|
||||
|
||||
var showPhraseDisplayBinding: Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.route == .recoveryPhraseDisplay },
|
||||
send: { isActive in
|
||||
return .updateRoute(isActive ? .send : nil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
var showSendBinding: Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.route == .send },
|
||||
|
@ -102,7 +112,7 @@ extension HomeViewStore {
|
|||
|
||||
var showOnboardingBinding: Binding<Bool> {
|
||||
self.binding(
|
||||
get: {$0.route == .onboarding },
|
||||
get: { $0.route == .onboarding },
|
||||
send: { isActive in
|
||||
return .updateRoute(isActive ? .onboarding : nil)
|
||||
}
|
||||
|
|
|
@ -21,6 +21,13 @@ struct HomeView: View {
|
|||
.primaryButtonStyle
|
||||
.frame(height: 50)
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.updateRoute(.recoveryPhraseDisplay)) },
|
||||
label: { Text("Show Recovery Phrase Demo") }
|
||||
)
|
||||
.primaryButtonStyle
|
||||
.frame(height: 50)
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.updateRoute(.send)) },
|
||||
label: { Text("Go to Send") }
|
||||
|
@ -53,6 +60,12 @@ struct HomeView: View {
|
|||
}
|
||||
.padding(.horizontal, 30)
|
||||
.navigationBarTitle("Home", displayMode: .inline)
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.showPhraseDisplayBinding,
|
||||
destination: {
|
||||
RecoveryPhraseDisplayView(store: .demo)
|
||||
}
|
||||
)
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.showSendBinding,
|
||||
destination: {
|
||||
|
@ -100,9 +113,6 @@ struct HomeView: View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
#if DEBUG
|
||||
extension HomeStore {
|
||||
static var demo: HomeStore {
|
||||
HomeStore(
|
||||
|
@ -118,7 +128,8 @@ extension HomeStore {
|
|||
)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct HomeView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
|
|
|
@ -11,7 +11,7 @@ enum SendAction: Equatable {
|
|||
case updateRoute(SendView.Route?)
|
||||
}
|
||||
|
||||
// Mark: - SendReducer
|
||||
// MARK: - SendReducer
|
||||
|
||||
typealias SendReducer = Reducer<SendState, SendAction, Void>
|
||||
|
||||
|
@ -41,16 +41,15 @@ extension SendReducer {
|
|||
}
|
||||
}
|
||||
|
||||
// Mark: - SendStore
|
||||
// MARK: - SendStore
|
||||
|
||||
typealias SendStore = Store<SendState, SendAction>
|
||||
|
||||
// Mark: - SendViewStore
|
||||
// MARK: - SendViewStore
|
||||
|
||||
typealias SendViewStore = ViewStore<SendState, SendAction>
|
||||
|
||||
extension SendViewStore {
|
||||
|
||||
var bindingForTransaction: Binding<Transaction> {
|
||||
self.binding(
|
||||
get: \.transaction,
|
||||
|
@ -86,4 +85,3 @@ extension SendViewStore {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ struct Sent: View {
|
|||
.frame(height: 50)
|
||||
.padding()
|
||||
|
||||
|
||||
Text("\(String(dumping: transaction))")
|
||||
Text("\(String(dumping: isComplete))")
|
||||
|
||||
|
|
|
@ -9,15 +9,6 @@ struct TransactionDetailView: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct TransactionDetail_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
TransactionDetailView(transaction: .demo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
extension Transaction {
|
||||
static var demo: Self {
|
||||
.init(
|
||||
|
@ -29,4 +20,13 @@ extension Transaction {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct TransactionDetail_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
TransactionDetailView(transaction: .demo)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -22,21 +22,6 @@ struct TransactionHistoryView: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct TransactionView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
TransactionHistoryView(store: .demo)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
|
||||
NavigationView {
|
||||
TransactionHistoryView(store: .demoWithSelectedTransaction)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
extension TransactionHistoryStore {
|
||||
static var demo: Store<TransactionHistoryState, TransactionHistoryAction> {
|
||||
return Store(
|
||||
|
@ -77,4 +62,17 @@ extension IdentifiedArrayOf where Element == Transaction {
|
|||
)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct TransactionView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
TransactionHistoryView(store: .demo)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
|
||||
NavigationView {
|
||||
TransactionHistoryView(store: .demoWithSelectedTransaction)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ internal enum Asset {
|
|||
}
|
||||
internal enum Colors {
|
||||
internal enum BackgroundColors {
|
||||
internal static let numberedChip = ColorAsset(name: "numberedChip")
|
||||
internal static let phraseGridDarkGray = ColorAsset(name: "phraseGridDarkGray")
|
||||
}
|
||||
internal enum Buttons {
|
||||
|
@ -73,6 +74,7 @@ internal enum Asset {
|
|||
}
|
||||
internal enum Text {
|
||||
internal static let activeButtonText = ColorAsset(name: "ActiveButtonText")
|
||||
internal static let body = ColorAsset(name: "Body")
|
||||
internal static let button = ColorAsset(name: "Button")
|
||||
internal static let heading = ColorAsset(name: "Heading")
|
||||
internal static let medium = ColorAsset(name: "Medium")
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIAppFonts</key>
|
||||
|
|
|
@ -45,7 +45,7 @@ struct ButtonModifier_Previews: PreviewProvider {
|
|||
Button("Example Button") { dump("Example button") }
|
||||
.exampleButtonStyle
|
||||
.padding(.horizontal, 25)
|
||||
.frame(height: 75)
|
||||
.frame(height: 60)
|
||||
.previewLayout(.fixed(width: 300, height: 100))
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
|
@ -19,14 +19,14 @@ struct EnumeratedChip: View {
|
|||
minWidth: 0,
|
||||
maxWidth: .infinity,
|
||||
minHeight: 30,
|
||||
idealHeight: 40,
|
||||
maxHeight: .infinity,
|
||||
alignment: .leading
|
||||
)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.leading, 14)
|
||||
.padding(.vertical, 4)
|
||||
.background(Asset.Colors.Buttons.primaryButtonPressed.color)
|
||||
.background(Asset.Colors.BackgroundColors.numberedChip.color)
|
||||
.cornerRadius(6)
|
||||
.shadow(color: Asset.Colors.Shadow.numberedTextShadow.color, radius: 3, x: 0, y: 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,24 +35,30 @@ struct NumberedText: View {
|
|||
var text: String
|
||||
|
||||
@ViewBuilder var numberedText: some View {
|
||||
Text(number.superscriptRepresentation)
|
||||
GeometryReader { geometry in
|
||||
(Text("\(number)")
|
||||
.baselineOffset(geometry.size.height / 4)
|
||||
.foregroundColor(Asset.Colors.Text.highlightedSuperscriptText.color)
|
||||
.font(.custom(FontFamily.Roboto.bold.name, size: 20)) +
|
||||
.font(.custom(FontFamily.Roboto.bold.name, size: 12)) +
|
||||
|
||||
Text(" \(text)")
|
||||
.foregroundColor(Asset.Colors.Text.button.color)
|
||||
.font(.custom(FontFamily.Rubik.medium.name, size: 16))
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
numberedText
|
||||
.font(.custom(FontFamily.Rubik.regular.name, size: 14))
|
||||
)
|
||||
.shadow(
|
||||
color: Asset.Colors.Shadow.numberedTextShadow.color,
|
||||
radius: 1,
|
||||
x: 0,
|
||||
y: 1
|
||||
)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.frame(height: geometry.size.height, alignment: .center)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
numberedText
|
||||
.layoutPriority(1)
|
||||
.fixedSize(horizontal: false, vertical: false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,8 +89,8 @@ struct EnumeratedChip_Previews: PreviewProvider {
|
|||
.frame(
|
||||
minWidth: 0,
|
||||
maxWidth: .infinity,
|
||||
minHeight: 30,
|
||||
idealHeight: 40
|
||||
minHeight: 40,
|
||||
maxHeight: .infinity
|
||||
)
|
||||
} else {
|
||||
EnumeratedChip(index: (i + 1), text: word)
|
||||
|
@ -92,7 +98,7 @@ struct EnumeratedChip_Previews: PreviewProvider {
|
|||
minWidth: 0,
|
||||
maxWidth: .infinity,
|
||||
minHeight: 30,
|
||||
idealHeight: 40
|
||||
maxHeight: .infinity
|
||||
)
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
import SwiftUI
|
||||
|
||||
struct PhraseChip: View {
|
||||
enum Kind {
|
||||
enum Kind: Hashable {
|
||||
case empty
|
||||
case unassigned(word: String)
|
||||
case ordered(position: Int, word: String)
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// SecantTextStyles.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Francisco Gindre on 10/28/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
extension Text {
|
||||
func bodyText() -> some View {
|
||||
self.modifier(BodyTextStyle())
|
||||
}
|
||||
|
||||
func titleText() -> some View {
|
||||
self.modifier(TitleTextStyle())
|
||||
}
|
||||
/// Body text style. Used for content. Roboto-Regular 18pt
|
||||
private struct BodyTextStyle: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.foregroundColor(Asset.Colors.Text.body.color)
|
||||
.font(.custom(FontFamily.Rubik.regular.name, size: 18))
|
||||
}
|
||||
}
|
||||
|
||||
private struct TitleTextStyle: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.foregroundColor(Asset.Colors.Text.body.color)
|
||||
.font(.custom(FontFamily.Roboto.medium.name, size: 24))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import SwiftUI
|
||||
|
||||
#if DEBUG
|
||||
// TODO: This should have a #DEBUG tag, but if so, it's not possible to compile this on release mode and submit it to testflight
|
||||
struct StateContainer<T, Content: View>: View {
|
||||
@State private var state: T
|
||||
private var content: (Binding<T>) -> Content
|
||||
|
@ -14,4 +14,3 @@ struct StateContainer<T, Content: View>: View {
|
|||
content($state)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Foundation
|
||||
|
||||
#if DEBUG
|
||||
// TODO: This should have a #DEBUG tag, but if so, it's not possible to compile this on release mode and submit it to testflight
|
||||
extension String {
|
||||
init<T>(dumping value: T) {
|
||||
var output = String()
|
||||
|
@ -8,4 +8,3 @@ extension String {
|
|||
self.init(stringLiteral: output)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
struct WithStateBinding<T: Equatable, Content: View>: View {
|
||||
@State var localState: T
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// RecoveryFlowTests.swift
|
||||
// secantTests
|
||||
//
|
||||
// Created by Francisco Gindre on 10/29/21.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import secant_testnet
|
||||
|
||||
class RecoveryFlowTests: XCTestCase {
|
||||
func testGiven24WordsBIP39ChunkItIntoQuarters() throws {
|
||||
let words = [
|
||||
"bring", "salute", "thank",
|
||||
"require", "spirit", "toe",
|
||||
// second chunk
|
||||
"boil", "hill", "casino",
|
||||
"trophy", "drink", "frown",
|
||||
// third chunk
|
||||
"bird", "grit", "close",
|
||||
"morning", "bind", "cancel",
|
||||
// Fourth chunk
|
||||
"daughter", "salon", "quit",
|
||||
"pizza", "just", "garlic"
|
||||
]
|
||||
let phrase = RecoveryPhrase(words: words)
|
||||
|
||||
let chunks = phrase.toChunks()
|
||||
|
||||
XCTAssertEqual(chunks.count, 4)
|
||||
XCTAssertEqual(chunks[0].startIndex, 1)
|
||||
XCTAssertEqual(chunks[0].words, ["bring", "salute", "thank", "require", "spirit", "toe"])
|
||||
XCTAssertEqual(chunks[1].startIndex, 7)
|
||||
XCTAssertEqual(chunks[2].startIndex, 13)
|
||||
XCTAssertEqual(chunks[3].startIndex, 19)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// RecoveryPhraseDisplayStoreTests.swift
|
||||
// secantTests
|
||||
//
|
||||
// Created by Francisco Gindre on 12/8/21.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import ComposableArchitecture
|
||||
@testable import secant_testnet
|
||||
|
||||
class RecoveryPhraseDisplayReducerTests: XCTestCase {
|
||||
func testCopyToBuffer() {
|
||||
let store = TestStore(
|
||||
initialState: .test,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
|
||||
store.send(.copyToBufferPressed) {
|
||||
$0.phrase = .demo
|
||||
$0.showCopyToBufferAlert = true
|
||||
}
|
||||
|
||||
XCTAssertEqual(
|
||||
store.environment.pasteboard.getString(),
|
||||
RecoveryPhrase.demo.toString()
|
||||
)
|
||||
}
|
||||
|
||||
func testNewPhrase() {
|
||||
let store = TestStore(
|
||||
initialState: .empty,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
|
||||
store.send(.phraseResponse(.success(.demo))) {
|
||||
$0.phrase = .demo
|
||||
$0.showCopyToBufferAlert = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension RecoveryPhraseDisplayState {
|
||||
static let test = RecoveryPhraseDisplayState(
|
||||
phrase: .demo,
|
||||
showCopyToBufferAlert: false
|
||||
)
|
||||
|
||||
static let empty = RecoveryPhraseDisplayState(
|
||||
phrase: .empty,
|
||||
showCopyToBufferAlert: false
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue