From b665a18fe22c564eaa85b8171ad6c1adb8593a30 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 7 Apr 2022 07:06:47 -0500 Subject: [PATCH] Issue #145 - Single Line Text Field * Single Line Text Field * add TODO comment to secant/Util/Strings.swift Issue #245 comments the changes needed Co-authored-by: Francisco Gindre --- secant.xcodeproj/project.pbxproj | 62 ++++++++- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../Icons/qr-code.imageset/Contents.json | 56 ++++++++ .../Icons/qr-code.imageset/qr_darktheme.png | Bin 0 -> 494 bytes .../qr-code.imageset/qr_darktheme@2x.png | Bin 0 -> 733 bytes .../qr-code.imageset/qr_darktheme@3x.png | Bin 0 -> 1069 bytes .../Icons/qr-code.imageset/qr_lighttheme.png | Bin 0 -> 546 bytes .../qr-code.imageset/qr_lighttheme@2x.png | Bin 0 -> 715 bytes .../qr-code.imageset/qr_lighttheme@3x.png | Bin 0 -> 770 bytes .../Icons/swap.imageset/Contents.json | 56 ++++++++ .../swap.imageset/swapicon_darktheme.png | Bin 0 -> 271 bytes .../swap.imageset/swapicon_darktheme@2x.png | Bin 0 -> 424 bytes .../swap.imageset/swapicon_darktheme@3x.png | Bin 0 -> 552 bytes .../swap.imageset/swapicon_lighttheme.png | Bin 0 -> 298 bytes .../swap.imageset/swapicon_lighttheme@2x.png | Bin 0 -> 463 bytes .../swap.imageset/swapicon_lighttheme@3x.png | Bin 0 -> 599 bytes .../Cursor/Bar.colorset/Contents.json | 38 ++++++ secant/Colors.xcassets/Cursor/Contents.json | 6 + .../Colors.xcassets/TextField/Contents.json | 6 + .../Contents.json | 38 ++++++ .../Contents.json | 38 ++++++ .../TextField/Underline/Contents.json | 6 + .../Underline/Gray.colorset/Contents.json | 38 ++++++ .../Underline/Purple.colorset/Contents.json | 38 ++++++ secant/Features/Home/Views/HomeView.swift | 2 +- secant/Generated/XCAssets+Generated.swift | 13 ++ .../Components/SingleLineTextField.swift | 124 ++++++++++++++++++ .../Components/TextFieldFooter.swift | 23 ++++ .../Components/TextFieldInput.swift | 29 ++++ .../Components/TextFieldStore.swift | 62 +++++++++ .../TextFieldTitleAccessoryButtonStyle.swift | 29 ++++ .../TextFields/CurrencySelectionStore.swift | 53 ++++++++ .../TransactionCurrencySelector.swift | 36 +++++ .../TextFields/TransactionInputStore.swift | 101 ++++++++++++++ .../TextFields/TransactionTextField.swift | 72 ++++++++++ secant/Util/DebugFrame.swift | 31 +++++ secant/Util/Strings.swift | 54 ++++++++ 37 files changed, 1010 insertions(+), 5 deletions(-) create mode 100644 secant/Assets.xcassets/Icons/qr-code.imageset/Contents.json create mode 100644 secant/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme.png create mode 100644 secant/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme@2x.png create mode 100644 secant/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme@3x.png create mode 100644 secant/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme.png create mode 100644 secant/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@2x.png create mode 100644 secant/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@3x.png create mode 100644 secant/Assets.xcassets/Icons/swap.imageset/Contents.json create mode 100644 secant/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme.png create mode 100644 secant/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@2x.png create mode 100644 secant/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@3x.png create mode 100644 secant/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme.png create mode 100644 secant/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme@2x.png create mode 100644 secant/Assets.xcassets/Icons/swap.imageset/swapicon_lighttheme@3x.png create mode 100644 secant/Colors.xcassets/Cursor/Bar.colorset/Contents.json create mode 100644 secant/Colors.xcassets/Cursor/Contents.json create mode 100644 secant/Colors.xcassets/TextField/Contents.json create mode 100644 secant/Colors.xcassets/TextField/TitleAccessoryButton.colorset/Contents.json create mode 100644 secant/Colors.xcassets/TextField/TitleAccessoryButtonPressed.colorset/Contents.json create mode 100644 secant/Colors.xcassets/TextField/Underline/Contents.json create mode 100644 secant/Colors.xcassets/TextField/Underline/Gray.colorset/Contents.json create mode 100644 secant/Colors.xcassets/TextField/Underline/Purple.colorset/Contents.json create mode 100644 secant/UIComponents/TextFields/Components/SingleLineTextField.swift create mode 100644 secant/UIComponents/TextFields/Components/TextFieldFooter.swift create mode 100644 secant/UIComponents/TextFields/Components/TextFieldInput.swift create mode 100644 secant/UIComponents/TextFields/Components/TextFieldStore.swift create mode 100644 secant/UIComponents/TextFields/Components/TextFieldTitleAccessoryButtonStyle.swift create mode 100644 secant/UIComponents/TextFields/CurrencySelectionStore.swift create mode 100644 secant/UIComponents/TextFields/TransactionCurrencySelector.swift create mode 100644 secant/UIComponents/TextFields/TransactionInputStore.swift create mode 100644 secant/UIComponents/TextFields/TransactionTextField.swift create mode 100644 secant/Util/DebugFrame.swift diff --git a/secant.xcodeproj/project.pbxproj b/secant.xcodeproj/project.pbxproj index c04273e..4f44f49 100644 --- a/secant.xcodeproj/project.pbxproj +++ b/secant.xcodeproj/project.pbxproj @@ -57,10 +57,20 @@ 0DFE93E1272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DFE93E0272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift */; }; 0DFE93E3272CA1AA000FCCA5 /* RecoveryPhraseValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DFE93E2272CA1AA000FCCA5 /* RecoveryPhraseValidation.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 /* TransactionTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8719CA27FB09990082C926 /* TransactionTextField.swift */; }; + 2E8719CD27FB0D3B0082C926 /* TransactionCurrencySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8719CC27FB0D3B0082C926 /* TransactionCurrencySelector.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 /* TransactionInputStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB7758627FC67FD00269373 /* TransactionInputStore.swift */; }; + 2EDA07A027EDE18C00D6F09B /* TextFieldInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EDA079F27EDE18C00D6F09B /* TextFieldInput.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 */; }; 660558F7270C862F009D6954 /* Fonts+Generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660558F5270C862F009D6954 /* Fonts+Generated.swift */; }; 660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660558F6270C862F009D6954 /* XCAssets+Generated.swift */; }; @@ -92,9 +102,9 @@ 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 */; }; - 9EF8139C27F47AED0075AF48 /* InitializationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8139B27F47AED0075AF48 /* InitializationState.swift */; }; 9EF8139127F191BF0075AF48 /* WalletStorageInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8139027F191BF0075AF48 /* WalletStorageInteractor.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 */; }; F93874F0273C4DE200F0E875 /* HomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874ED273C4DE200F0E875 /* HomeStore.swift */; }; @@ -198,10 +208,20 @@ 0DFE93E0272C9ECB000FCCA5 /* RecoveryPhraseBackupValidationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseBackupValidationView.swift; sourceTree = ""; }; 0DFE93E2272CA1AA000FCCA5 /* RecoveryPhraseValidation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseValidation.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 = ""; }; + 2E6CF8DC27D78319004DCD7A /* CurrencySelectionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencySelectionStore.swift; sourceTree = ""; }; + 2E8719CA27FB09990082C926 /* TransactionTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionTextField.swift; sourceTree = ""; }; + 2E8719CC27FB0D3B0082C926 /* TransactionCurrencySelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionCurrencySelector.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 /* TransactionInputStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionInputStore.swift; sourceTree = ""; }; + 2EDA079F27EDE18C00D6F09B /* TextFieldInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldInput.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 = ""; }; 660558F5270C862F009D6954 /* Fonts+Generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Fonts+Generated.swift"; sourceTree = ""; }; 660558F6270C862F009D6954 /* XCAssets+Generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCAssets+Generated.swift"; sourceTree = ""; }; @@ -232,8 +252,8 @@ 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 = ""; }; - 9EF8139B27F47AED0075AF48 /* InitializationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitializationState.swift; sourceTree = ""; }; 9EF8139027F191BF0075AF48 /* WalletStorageInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletStorageInteractor.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 = ""; }; @@ -391,6 +411,8 @@ 0D4E7A0726B364170058B01E /* secant */ = { isa = PBXGroup; children = ( + 0D4E7A0826B364170058B01E /* SecantApp.swift */, + 0D4E7A0A26B364170058B01E /* ContentView.swift */, 0DACFA7D27208CC80039EEA5 /* Util */, 6654C73B2715A3F000901167 /* Features */, 660558F4270C85F7009D6954 /* Generated */, @@ -481,6 +503,7 @@ 0DA13C9126C15E1900E3B610 /* UIComponents */ = { isa = PBXGroup; children = ( + 2E35F99027B28E6800EB79CD /* TextFields */, 0D0781C5278776B90083ACD7 /* Shapes */, 0D8A43C2272AEEA7005A6414 /* FontStyles */, 669FDAE7272C239D007B9422 /* CircularFrame */, @@ -513,6 +536,7 @@ F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */, F93673D52742CB840099C6AF /* Previews.swift */, 0D35CC45277A36E00074316A /* ScrollableWhenScaled.swift */, + 2EDA07A327EDE2A900D6F09B /* DebugFrame.swift */, 9E2AC10027D8EF0B0042AA47 /* MnemonicSeedPhraseProvider.swift */, 9E2AC10227DA28200042AA47 /* WalletStorage.swift */, 9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */, @@ -567,6 +591,18 @@ path = RecoveryPhraseValidationTests; sourceTree = ""; }; + 2E35F99027B28E6800EB79CD /* TextFields */ = { + isa = PBXGroup; + children = ( + 2E8719CA27FB09990082C926 /* TransactionTextField.swift */, + 2E8719CC27FB0D3B0082C926 /* TransactionCurrencySelector.swift */, + 2EB7758627FC67FD00269373 /* TransactionInputStore.swift */, + 2E6CF8DC27D78319004DCD7A /* CurrencySelectionStore.swift */, + 2EDA07A527EDE31100D6F09B /* Components */, + ); + path = TextFields; + sourceTree = ""; + }; 2E5C037F2738C55F008BFFD3 /* Onboarding */ = { isa = PBXGroup; children = ( @@ -578,6 +614,18 @@ path = Onboarding; sourceTree = ""; }; + 2EDA07A527EDE31100D6F09B /* Components */ = { + isa = PBXGroup; + children = ( + 2E35F99127B28E7600EB79CD /* SingleLineTextField.swift */, + 2EDA079F27EDE18C00D6F09B /* TextFieldInput.swift */, + 2EDA07A127EDE1AE00D6F09B /* TextFieldFooter.swift */, + 2E35F99927B3E99C00EB79CD /* TextFieldTitleAccessoryButtonStyle.swift */, + 2EB1C5E727D77F6100BC43D7 /* TextFieldStore.swift */, + ); + path = Components; + sourceTree = ""; + }; 660558F4270C85F7009D6954 /* Generated */ = { isa = PBXGroup; children = ( @@ -1078,12 +1126,15 @@ 660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */, 0D35CC46277A36E00074316A /* ScrollableWhenScaled.swift in Sources */, F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */, + 2EDA07A027EDE18C00D6F09B /* TextFieldInput.swift in Sources */, + 2EB7758727FC67FD00269373 /* TransactionInputStore.swift in Sources */, 669FDAE9272C23B3007B9422 /* CircularFrame.swift in Sources */, 9EF8136027F043CC0075AF48 /* AppDelegate.swift in Sources */, 9E80B47227E4B34B008FF493 /* UserPreferencesStorage.swift in Sources */, F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */, 663FABA2271D876C00E495F8 /* SecondaryButton.swift in Sources */, 0DC487C32772574C00BE6A63 /* ValidationSucceededView.swift in Sources */, + 2EB1C5E827D77F6100BC43D7 /* TextFieldStore.swift in Sources */, 0D8A43C4272AEEDE005A6414 /* SecantTextStyles.swift in Sources */, 0D1922F226BDE29300052649 /* ZcashSDKStubs.swift in Sources */, 9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */, @@ -1092,6 +1143,7 @@ 9E2DF99E27CF704D00649636 /* ImportWalletView.swift in Sources */, 0D535FE2271F9476009A9E3E /* EnumeratedChip.swift in Sources */, 6654C73E2715A41300901167 /* OnboardingStore.swift in Sources */, + 2E6CF8DD27D78319004DCD7A /* CurrencySelectionStore.swift in Sources */, 9EBEF87A27CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift in Sources */, 9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */, 0DDB6A5127737D4A0012A410 /* ValidationFailedView.swift in Sources */, @@ -1100,6 +1152,7 @@ F9971A5327680DD000A2DB75 /* Profile.swift in Sources */, F93874F0273C4DE200F0E875 /* HomeStore.swift in Sources */, 669FDAEB272C23C2007B9422 /* CircularFrameBadge.swift in Sources */, + 2E8719CD27FB0D3B0082C926 /* TransactionCurrencySelector.swift in Sources */, F9971A6C27680E1000A2DB75 /* WalletInfoView.swift in Sources */, F9EEB8162742C2210032EEB8 /* WithStateBinding.swift in Sources */, F93673D62742CB840099C6AF /* Previews.swift in Sources */, @@ -1118,6 +1171,7 @@ 0DA13CA526C1963000E3B610 /* Balance.swift in Sources */, 2EA11F5B27467EF800709571 /* OnboardingFooterView.swift in Sources */, 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 */, @@ -1138,14 +1192,17 @@ 0D185819272723FF0046B928 /* ColoredChip.swift in Sources */, 2EA11F5D27467F7700709571 /* OnboardingContentView.swift in Sources */, 2E58E73B274679F000B2B84B /* OnboardingHeaderView.swift in Sources */, + 2E35F99227B28E7600EB79CD /* SingleLineTextField.swift in Sources */, 0D8A43C6272B129C005A6414 /* WordChipGrid.swift in Sources */, 66A0807B271993C500118B79 /* OnboardingProgressIndicator.swift in Sources */, 0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */, F9C165C22740403600592F76 /* CreateView.swift in Sources */, F9C165B4274031F600592F76 /* Bindings.swift in Sources */, + 2E35F99A27B3E99C00EB79CD /* TextFieldTitleAccessoryButtonStyle.swift in Sources */, 9E2DF99C27CF704D00649636 /* ImportWalletStore.swift in Sources */, F9971A6627680DFE00A2DB75 /* SettingsView.swift in Sources */, F96B41EB273B50520021B49A /* Strings.swift in Sources */, + 2EDA07A227EDE1AE00D6F09B /* TextFieldFooter.swift in Sources */, F9971A5427680DD000A2DB75 /* ProfileView.swift in Sources */, F9971A6027680DF600A2DB75 /* Scan.swift in Sources */, 9EF8139127F191BF0075AF48 /* WalletStorageInteractor.swift in Sources */, @@ -1156,6 +1213,7 @@ F9971A6527680DFE00A2DB75 /* Settings.swift in Sources */, 9EF8139C27F47AED0075AF48 /* InitializationState.swift in Sources */, 0D0781C9278776D20083ACD7 /* ZcashSymbol.swift in Sources */, + 2E8719CB27FB09990082C926 /* TransactionTextField.swift in Sources */, 6654C7412715A47300901167 /* Onboarding.swift in Sources */, F9C165C42740403600592F76 /* SentView.swift in Sources */, F9971A5927680DDE00A2DB75 /* Request.swift in Sources */, diff --git a/secant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/secant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7289df1..4e8bcfd 100644 --- a/secant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/secant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -69,8 +69,8 @@ "repositoryURL": "https://github.com/apple/swift-crypto.git", "state": { "branch": null, - "revision": "a8911e0fadc25aef1071d582355bd1037a176060", - "version": "2.0.4" + "revision": "067254c79435de759aeef4a6a03e43d087d61312", + "version": "2.0.5" } }, { diff --git a/secant/Assets.xcassets/Icons/qr-code.imageset/Contents.json b/secant/Assets.xcassets/Icons/qr-code.imageset/Contents.json new file mode 100644 index 0000000..d3a392a --- /dev/null +++ b/secant/Assets.xcassets/Icons/qr-code.imageset/Contents.json @@ -0,0 +1,56 @@ +{ + "images" : [ + { + "filename" : "qr_lighttheme.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "qr_darktheme.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "qr_lighttheme@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "qr_darktheme@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "qr_lighttheme@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "qr_darktheme@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme.png b/secant/Assets.xcassets/Icons/qr-code.imageset/qr_darktheme.png new file mode 100644 index 0000000000000000000000000000000000000000..341a34e3fa70a500ce25ecfc27b0ced3025e758f GIT binary patch literal 494 zcmVNJHUKtgH%K?=2I-w^ z<*AO01^toK@A2()4rkFulNtd5Ol>5~<*{loB3e4*7n5 zpc_QB!tT;$gukQ~W;s(`BuY{;Jz&!>3yJE*WqzmX;GaM>kWSMqK`AvQk`+^66w)AF zN)hdL_cGP04A0uz^e>f>!|`gtME&&QWoi2>NKWF3G~wsw;U$E@ME&uKE%59mPVbw2acWZWdu1F-kk7 zavKzoui(-kuq7xr7{|Q@OBM#u0=KgL!q4y z_sXP;osl=M7{llI%zI~KK#v}{97dTIvI4-}iRm%^KUP?;&QXr8|4ojDT1Y4D1rND~ zx7@GNxA5X#V?fH~nDjZPs3U|q6-akw@{Q$n^43ZE)xRU*K=gY+gK;RJqNs;#IhF2JYRhVTOz9HPeBp_VE4I~g~nwd+ufdm3g z7c=*8IDBc)_TSZN_1M5p6BCH(GYq}+8beI`nvELVL(PEV^F18dDCEu}0=PRg4JSk@xRjqehwMH{egDUbWC$) zXBD9Xn#Q=s`#H;?BeGLbP5kDmn8x^=!D)OWTn#0o%q-x-i7ov9YK$SPhP&f7_aWN+ z*}6U0`_^Bo&js#19Q)on*iz{!1@7?U7H#g1`h@EsDX0=|g2K#oW);iAYZm2du6CiZ zhM5oJzA(Ujf{kZopGpga<&*{OTsYS`2Dk$_CCHhwa#px0BgG4WzkOzb(>H3-*T882 zR~3Jjk@6NQido<+0j`%#b*n6z#oW$KAJDu_GIOsjbGHaMy_#O7qo>`;nWpW**j;~v(rdg)S@1e>srrN)*t3L7 zxPb%$O*3-|H;_P}=_Z+rY0(-!o@|YK{l>H?yETtCrLFNN=nS4MK<+*K_%!yVWZUca zQNI=3dl;OMS<4^1_aMkh1MuKvxwm43)?Dp~>^)R+gwQ=H8&A>p($h6< zS@8fS+#$W?JS=DCdh)*0_1@NAeU^nnCtBb_M;efFWls7gq$K-*yX|*_eEVl&u{Bpa zAYJS{H?rmuZeeS@f9n~`f^@4dXAx04c+t;F3!Eju)yAKz(M`U!o`Z`E00000NkvXXu0mjfJi!Hl literal 0 HcmV?d00001 diff --git a/secant/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme.png b/secant/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme.png new file mode 100644 index 0000000000000000000000000000000000000000..263e06513966e7b8e370616208cde24d75e9edd3 GIT binary patch literal 546 zcmV+-0^R+IP)5bd_&T5e#fWN$nH@dhr)T8;oG5KkaCFs|c*8we+G z*kc)QfNN8>Zzdn|nMhQMg{h(-$;@>3d;MO+{fj`za;={uwmr8!>w80=n4cQ&gqD1; z0G}e0BO;Y&u~v_Zt`)f6RnH;EE7mj5@cW!96^LAB3kJ4>h4}47MWKENNT&F*LYq+X z;LveMOaog0jpdCsb7Zqtk#7RD@MiXzM0xscfI3`;z-E9I4jVUNsRbx!Yf9G@Ad4~w zCgXa5(tQeBut>A6?ya=qKMG)Pqs}Ck;jKV)<^>}>7}?J!g*G*$8oXPsglHVAsL)eq zJLoF9`ntmgD7vT%2U(l8bzXjiy-kTABHC<4l*+zPGB|)RVrIV2l`JewP=xBbj@_XLQBoAd}-q|$-?YHR1g;sWy3;rX|UY!Pp!hnf$IgH!*r4e zy>ACjYS*coik=Hn;(+}s2SZ@aE|Xki&k`SSLRv$o3Gu`sn*GVHcX7~a*T63ZOs=g< zk=drK(0q#NJsNWZ3rP?63Ik_ush|oAIwPNOJ4KuwewQx9~&kcw~_m~p^H(){6zzw(oa06~YS(gPjpuK_F zgOuI?)>I7Rgtm6-)Q+9DRM=9bDs~*d`MmiUyGK0&-$5{XlYTX7(Imk4laeH&qqpC1}?Rv%cgqlQ5P zVDma*2Y>lr02(orE*hfKH~BQwAI0t!q=DFvCIAJil-i8ez%q7%DnmF-h=9gOn0v8} z6M*O$wyIO5VM0sTX+b(JA7J`psovTE)a{jtp6CGF8?FY>XWFeFe+fX{DH8xCnRS>@ z(f}t(wY9*uXh1SuS`)c%0%ruQ}rL45w#egI7)6{gOpt%>Vn-Ia^+}5fd`a(QK)I7SiSjWTG=ax7S%R&jjc4}ZnW#F$v7B;VjPL({j$IdFMb3rE5 zNa`zI+yp=^+e`>Pb2+fyV777oLNmBq1hnc{Xm}Nrm2T?Wko$a(bW`t?XTy5cxnE^L xmfcp84;SXBUnWjSR+3ElQ002ovPDHLkV1n*rL23X1 literal 0 HcmV?d00001 diff --git a/secant/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@3x.png b/secant/Assets.xcassets/Icons/qr-code.imageset/qr_lighttheme@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..bff5f4676bc03eb5cf154ea55637cbbf6a2e0daa GIT binary patch literal 770 zcmV+d1O5DoP)ybu%w1+}c8Ok!bO^uKml6lYBgfS5c8fGPkQN7rW~eR|%s(a{_l;M*d&@wmDbNK0P^~|MzVbSgN?c0CaVPi`DPM;7>xrE z0EM~1t`#Ir1$<7vtiIDpTLUbkC8^Ixv21h6^W-I^X7Y@Pa&0R{tN#xGt<)EVF* z0OITwfEYpm1V9#n3Qj(&2?aqxP>@#xp=kn4jEnJ2_#kRyD(FXj8Wo*r^66M7li;qMwD3c{#i{}d?N0$vwbeWaJUGmA9dPfVAchbC9Sb1NREGLq?1P31 zfEyP8HMJJwNDuWr6a)o@X&VY^NzC`OiBUZvB1`-LI5GJ9ls{aSQ1#W zGQD=vQYYK%rH0gp)ENW-F;2P{fM{mDr{^i5B76US>L?&@=XL6L0f-xa>80Mh`P3QU zK>#Yb^{r8<6y`Y?T61EX7WqAsj$Z!;#VfYqib-=@%SUj@mp4cNQqWU@L9TOR(a2z|nV!^FTR=@1M?>OQ%l|sQCWz z6W^+q)U|IrR~)li;FhPZxVY%t-HXA=wp<+!?`G&Uyj>8RUdtqSB9P(7B&JR3?`wsf zGFTRf^-5nmE}?&HMxCcx^R->I++sIB929(V**-jSd+R#wT~Au{6FwfRzHszig_W>E zr1Gq|854#7Pi|CHmI^nPs`I+kVLw;l<7$=_Q>uK^3)P-p<*`zZ_&0G;6_bUc+x^1t S>i>a0V(@hJb6Mw<&;$T{q-hxd literal 0 HcmV?d00001 diff --git a/secant/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@2x.png b/secant/Assets.xcassets/Icons/swap.imageset/swapicon_darktheme@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ba0972413206261d569cd92318727064e45d3a7b GIT binary patch literal 424 zcmV;Z0ayNsP){TrbjkIM04NwZ$*=2n4uL#(Lap;Njpz` zgCh@O8-iI%ATA(EAcUB85VN7Fut0l(5G)k>nMWv&<103dOhHuWGRT-AQbz#7EIhR# zWG~aSU4ihm<(!y-W@QQj+I<-6sr0e748w5m>UJ>GAqWDIu&y9709Oz>04)U9dn``z zUQlAsb@By?&vjxaHA(26KtgcCspXBT17r{? z0ILu>0BaDR0IJ;B!1_Z}O!SUB#QF^gekJh}y+c6A-XUf{7mo-B8!i--91BR-p68W@ z(}sX=T!XSJssoXOshv8Z^N4i_6$l^(aM`0F_bX+Tt^7UcARkT`I<_VOSo;OjhQB1? S)kv}c0000>xG(zQ-I)Lg4QB0ka!^CBt&}KJ4umG+}rpNwOfKHzlnoikC19W0Oqc#*k&%6vBUw zb>aWg1i}$w4$)cj<(fdmKJd>8T_EBh+CW4?G(ia2V1}Uiz>O)lOl8wk1v{l_ z`VHfm?E$X$QBNKfM8Q>{qps~GKw4 zeWd|=9c+8GLUaL?t|P*_{>HZOnD@^hOaLMvYyj*bTmV=@xB^re%JaNY0^1?3J`tZ= z)y1BqQHKXvnUt+K3Rw(QPpp*n6LS%h-9&jp<@~#T@O-Rsoc_vBHW)-U5+^(TD%WMP z>j|>MBi2@63}3+u8)V8=U9nw3*tZ=nAnYMrj+O`r8;BSP6No5?HV|ZkUrbrkF)~k| qJ*=N>^voNnv{r8<6y`Y?T61EX7WqAsj$Z!;#Vf0yLE8_V`SVM#*0t)dGjCA(GPZ-aC+m@4^JZ1uPL1A zQ|*0Qw|a9zXZxNnIt=+b9St=ubGqlnFPJoURo%Ikj#nyNf0id$O3zP@JHGH%P4UuF zhHi_X%=MT3tPXgd<1s8#zb(ivwSfDfVWNThqGek*K6@+jC$Yfl#s|j&)yR*lg`4(r uZkT(Z*1A6ZlE0{cV(w&v0-re@-`FyleD{}TM3nEJcds+<%|%;=4oeywV%l00mE(n+RH+6EUz|qGtw3 z`!WH9bM`|3BaEPFg9L?efdq%h1HnYf+Ul5ge5HjHwzW1tQ_D;ZrdRS^?>2lv%+qC% z6@p!c(C-y4f}V!B9Rc7*RTzlS!pm!UK#GohLFh2o5pgS5pWv_)i-$^XM|MMq9=q{z z_QO^JVFFYFVFNfJvU*RjuN1tU#p1Qh|DwxwTgUs}PJyX8lMV(T1uuG|#vhK?U^5Xy z-=N~_qA&N@+I65Q2pfP66+yFXG#26iLgWDig>V4`gO~@fD2z+kAM&8Xz2ifR)t4Yx zOyWQE4qXa*-@St-Ghnf^l(Nbi7%P|s#a1K)q3g!Xa9Yaq#Q1X^23&*wuBcJjOb0>h zgexM!A@V>Xg6%jK!)1iurIFjpe+C_j@VR>`?tB8U_7}eX#?ymCm^1(Y002ovPDHLk FV1fREGvW{RcJ^cnxW_yg^8{) z#IY<*5I+9IMNlmQaSq}pmIndC%hJm&0PzPxQRRU61(5^dA4CR-pAZWm*uIx#n7|YN zUUKcE(#A8(5o~wl8ylCk3mi2-gcAipj>cyi*3f$h0(!?*+#Mm}t|_*2cAOV@dnb>H zh_wlBI~SlXpfs^lJ5}y=Yi!J80A&_^iTEoTED*)zx#%eyFY5&FoX5+sR!JefgIEOc z?n`6=cmK(H5vVx$XX9rG%NNQkk{S0+RrfO0k=IJ^qQ zK+@E#mmFaPKgrkEs|n46&CY z2M&I=pf|7BrXbGC4igaPAxyTG7Z5rSZy<_5yn@IA;p6O+uS!aGP9F28Xs>KkveSej l{;O%c0T|ip>dX-UegS(CBxqoD0;2!`002ovPDHLkV1h2L{Eh$s literal 0 HcmV?d00001 diff --git a/secant/Colors.xcassets/Cursor/Bar.colorset/Contents.json b/secant/Colors.xcassets/Cursor/Bar.colorset/Contents.json new file mode 100644 index 0000000..970dc26 --- /dev/null +++ b/secant/Colors.xcassets/Cursor/Bar.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.171", + "green" : "0.138", + "red" : "0.126" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.810", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Colors.xcassets/Cursor/Contents.json b/secant/Colors.xcassets/Cursor/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/secant/Colors.xcassets/Cursor/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Colors.xcassets/TextField/Contents.json b/secant/Colors.xcassets/TextField/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/secant/Colors.xcassets/TextField/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Colors.xcassets/TextField/TitleAccessoryButton.colorset/Contents.json b/secant/Colors.xcassets/TextField/TitleAccessoryButton.colorset/Contents.json new file mode 100644 index 0000000..8219f80 --- /dev/null +++ b/secant/Colors.xcassets/TextField/TitleAccessoryButton.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.110", + "green" : "0.110", + "red" : "0.110" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.110", + "green" : "0.110", + "red" : "0.110" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Colors.xcassets/TextField/TitleAccessoryButtonPressed.colorset/Contents.json b/secant/Colors.xcassets/TextField/TitleAccessoryButtonPressed.colorset/Contents.json new file mode 100644 index 0000000..a568890 --- /dev/null +++ b/secant/Colors.xcassets/TextField/TitleAccessoryButtonPressed.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.226", + "green" : "0.226", + "red" : "0.226" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.226", + "green" : "0.226", + "red" : "0.226" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Colors.xcassets/TextField/Underline/Contents.json b/secant/Colors.xcassets/TextField/Underline/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/secant/Colors.xcassets/TextField/Underline/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Colors.xcassets/TextField/Underline/Gray.colorset/Contents.json b/secant/Colors.xcassets/TextField/Underline/Gray.colorset/Contents.json new file mode 100644 index 0000000..9751b83 --- /dev/null +++ b/secant/Colors.xcassets/TextField/Underline/Gray.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.827", + "blue" : "0.623", + "green" : "0.589", + "red" : "0.560" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.290", + "green" : "0.275", + "red" : "0.259" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Colors.xcassets/TextField/Underline/Purple.colorset/Contents.json b/secant/Colors.xcassets/TextField/Underline/Purple.colorset/Contents.json new file mode 100644 index 0000000..323fc68 --- /dev/null +++ b/secant/Colors.xcassets/TextField/Underline/Purple.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.678", + "green" : "0.459", + "red" : "0.557" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.678", + "green" : "0.459", + "red" : "0.557" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/secant/Features/Home/Views/HomeView.swift b/secant/Features/Home/Views/HomeView.swift index 520a7a2..1f12cdd 100644 --- a/secant/Features/Home/Views/HomeView.swift +++ b/secant/Features/Home/Views/HomeView.swift @@ -25,7 +25,7 @@ struct HomeView: View { reducer: SendReducer.default( whenDone: { HomeViewStore(store).send(.updateRoute(nil)) } ) - .debug(), + .debug(), environment: () ) ) diff --git a/secant/Generated/XCAssets+Generated.swift b/secant/Generated/XCAssets+Generated.swift index 21942a0..de418f8 100644 --- a/secant/Generated/XCAssets+Generated.swift +++ b/secant/Generated/XCAssets+Generated.swift @@ -37,7 +37,9 @@ internal enum Asset { internal static let bank = ImageAsset(name: "bank") internal static let list = ImageAsset(name: "list") internal static let profile = ImageAsset(name: "profile") + internal static let qrCode = ImageAsset(name: "qr-code") internal static let shield = ImageAsset(name: "shield") + internal static let swap = ImageAsset(name: "swap") } } internal enum Colors { @@ -61,6 +63,9 @@ internal enum Asset { internal static let secondaryButton = ColorAsset(name: "SecondaryButton") internal static let secondaryButtonPressed = ColorAsset(name: "SecondaryButtonPressed") } + internal enum Cursor { + internal static let bar = ColorAsset(name: "Bar") + } internal enum Onboarding { internal static let badgeShadow = ColorAsset(name: "BadgeShadow") internal static let circularFrameDarkOutlineGradientEnd = ColorAsset(name: "CircularFrameDarkOutlineGradientEnd") @@ -104,6 +109,14 @@ internal enum Asset { internal static let captionTextShadow = ColorAsset(name: "captionTextShadow") internal static let highlightedSuperscriptText = ColorAsset(name: "highlightedSuperscriptText") } + internal enum TextField { + internal static let titleAccessoryButton = ColorAsset(name: "TitleAccessoryButton") + internal static let titleAccessoryButtonPressed = ColorAsset(name: "TitleAccessoryButtonPressed") + internal enum Underline { + internal static let gray = ColorAsset(name: "Gray") + internal static let purple = ColorAsset(name: "Purple") + } + } internal enum ZcashBadge { internal static let zcashLogoFill = ColorAsset(name: "ZcashLogoFill") internal static let innerCircle = ColorAsset(name: "innerCircle") diff --git a/secant/UIComponents/TextFields/Components/SingleLineTextField.swift b/secant/UIComponents/TextFields/Components/SingleLineTextField.swift new file mode 100644 index 0000000..af25276 --- /dev/null +++ b/secant/UIComponents/TextFields/Components/SingleLineTextField.swift @@ -0,0 +1,124 @@ +// +// SingleLineTextField.swift +// secant-testnet +// +// Created by Adam Stener on 2/8/22. +// + +import SwiftUI +import ComposableArchitecture + +struct SingleLineTextField: View + where TitleAccessoryContent: View, InputAccessoryContent: View { + let placeholderText: String + let title: String + let store: TextFieldStore + + @ViewBuilder let titleAccessoryView: TitleAccessoryContent + @ViewBuilder let inputAccessoryView: InputAccessoryContent + + var body: some View { + VStack(spacing: 0) { + HStack { + Text(title) + .lineLimit(1) + .truncationMode(.middle) + + Spacer() + + titleAccessoryView + } + + HStack { + TextFieldInput( + placeholder: placeholderText, + store: store + ) + + Spacer() + + inputAccessoryView + } + .frame(maxHeight: 50) + + TextFieldFooter() + } + } +} + +struct SingleLineTextField_Previews: PreviewProvider { + struct TransactionPreview: View { + let store: TextFieldStore + let maxTransactionValue = 500.0 + + var body: some View { + WithViewStore(store) { viewStore in + VStack { + SingleLineTextField( + placeholderText: "$0", + title: "How much?", + store: store, + titleAccessoryView: { + Button( + action: { + viewStore.send(.set("\(500)")) + }, + label: { Text("Max") } + ) + .textFieldTitleAccessoryButtonStyle + }, + inputAccessoryView: { EmptyView() } + ) + } + } + } + } + + struct UserPreview: View { + let store: TextFieldStore + + var body: some View { + VStack { + SingleLineTextField( + placeholderText: "", + title: "Who would you like to deal with really long text today?", + store: store, + titleAccessoryView: { EmptyView() }, + inputAccessoryView: { EmptyView() } + ) + } + } + } + + static var previews: some View { + TransactionPreview( + store: TextFieldStore( + initialState: .init( + validationType: .floatingPoint, + text: "" + ), + reducer: .default, + environment: .init() + ) + ) + .preferredColorScheme(.dark) + .padding(.horizontal, 50) + .applyScreenBackground() + .previewLayout(.fixed(width: 500, height: 200)) + + UserPreview( + store: TextFieldStore( + initialState: .init( + validationType: .email, + text: "" + ), + reducer: .default, + environment: .init() + ) + ) + .preferredColorScheme(.light) + .padding(.horizontal, 50) + .applyScreenBackground() + .previewLayout(.fixed(width: 500, height: 200)) + } +} diff --git a/secant/UIComponents/TextFields/Components/TextFieldFooter.swift b/secant/UIComponents/TextFields/Components/TextFieldFooter.swift new file mode 100644 index 0000000..afd8e7b --- /dev/null +++ b/secant/UIComponents/TextFields/Components/TextFieldFooter.swift @@ -0,0 +1,23 @@ +// +// TextFieldFooter.swift +// secant-testnet +// +// Created by Adam Stener on 3/25/22. +// + +import SwiftUI +import ComposableArchitecture + +struct TextFieldFooter: View { + var body: some View { + HStack(spacing: 0) { + Rectangle() + .frame(height: 1.5) + .foregroundColor(Asset.Colors.TextField.Underline.purple.color) + + Rectangle() + .frame(height: 1.5) + .foregroundColor(Asset.Colors.TextField.Underline.gray.color) + } + } +} diff --git a/secant/UIComponents/TextFields/Components/TextFieldInput.swift b/secant/UIComponents/TextFields/Components/TextFieldInput.swift new file mode 100644 index 0000000..e6934f2 --- /dev/null +++ b/secant/UIComponents/TextFields/Components/TextFieldInput.swift @@ -0,0 +1,29 @@ +// +// TextFieldInput.swift +// secant-testnet +// +// Created by Adam Stener on 3/25/22. +// + +import SwiftUI +import ComposableArchitecture + +struct TextFieldInput: View { + let placeholder: String + let store: TextFieldStore + + var body: some View { + WithViewStore(store) { viewStore in + TextField( + placeholder, + text: Binding( + get: { viewStore.state.text }, + set: { viewStore.send(.set($0)) } + ) + ) + .lineLimit(1) + .truncationMode(.middle) + .accentColor(Asset.Colors.Cursor.bar.color) + } + } +} diff --git a/secant/UIComponents/TextFields/Components/TextFieldStore.swift b/secant/UIComponents/TextFields/Components/TextFieldStore.swift new file mode 100644 index 0000000..00e61f6 --- /dev/null +++ b/secant/UIComponents/TextFields/Components/TextFieldStore.swift @@ -0,0 +1,62 @@ +// +// TextFieldStore.swift +// secant-testnet +// +// Created by Adam Stener on 3/8/22. +// + +import ComposableArchitecture + +typealias TextFieldReducer = Reducer +typealias TextFieldStore = Store + +struct TextFieldState: Equatable { + var validationType: String.ValidationType? + var text: String + var valid = false + + init(validationType: String.ValidationType?, text: String) { + self.validationType = validationType + self.text = text + } +} + +enum TextFieldAction { + case apply((String) -> String) + case set(String) +} + +struct TextFieldEnvironment: Equatable { } + +extension TextFieldReducer { + static let `default` = TextFieldReducer { state, action, _ in + switch action { + case .apply(let action): + state.text = action(state.text) + state.valid = state.text.isValid(for: state.validationType) + + case .set(let text): + state.text = text + state.valid = state.text.isValid(for: state.validationType) + } + return .none + } +} + +extension TextFieldStore { + static var transaction: Self { + .init( + initialState: .init(validationType: .floatingPoint, text: ""), + reducer: .default, + environment: .init() + ) + } + + static var address: Self { + .init( + initialState: .init(validationType: .email, text: ""), + reducer: .default, + environment: .init() + ) + } +} diff --git a/secant/UIComponents/TextFields/Components/TextFieldTitleAccessoryButtonStyle.swift b/secant/UIComponents/TextFields/Components/TextFieldTitleAccessoryButtonStyle.swift new file mode 100644 index 0000000..50dd045 --- /dev/null +++ b/secant/UIComponents/TextFields/Components/TextFieldTitleAccessoryButtonStyle.swift @@ -0,0 +1,29 @@ +// +// TextFieldTitleAccessoryButtonStyle.swift +// secant-testnet +// +// Created by Adam Stener on 2/9/22. +// + +import SwiftUI + +struct TextFieldTitleAccessoryButtonStyle: ButtonStyle { + func makeBody(configuration: Configuration) -> some View { + configuration.label + .padding(.horizontal, 10) + .padding(.vertical, 3) + .foregroundColor(.white) + .background( + configuration.isPressed ? + Asset.Colors.TextField.titleAccessoryButtonPressed.color : + Asset.Colors.TextField.titleAccessoryButton.color + ) + .cornerRadius(6) + } +} + +extension Button { + var textFieldTitleAccessoryButtonStyle: some View { + buttonStyle(TextFieldTitleAccessoryButtonStyle()) + } +} diff --git a/secant/UIComponents/TextFields/CurrencySelectionStore.swift b/secant/UIComponents/TextFields/CurrencySelectionStore.swift new file mode 100644 index 0000000..f1b3c76 --- /dev/null +++ b/secant/UIComponents/TextFields/CurrencySelectionStore.swift @@ -0,0 +1,53 @@ +// +// ExchangeStore.swift +// secant-testnet +// +// Created by Adam Stener on 3/8/22. +// + +import ComposableArchitecture + +typealias CurrencySelectionReducer = Reducer< + CurrencySelectionState, + CurrencySelectionAction, + CurrencySelectionEnvironment +> + +typealias CurrencySelectionStore = Store + +struct CurrencySelectionState: Equatable { + enum Currency: Equatable { + case usd + case zec + + var acronym: String { + switch self { + case .usd: return "USD" + case .zec: return "ZEC" + } + } + } + + var currencyType: Currency = .zec +} + +enum CurrencySelectionAction: Equatable { + case swapCurrencyType +} + +struct CurrencySelectionEnvironment: Equatable { } + +extension CurrencySelectionReducer { + static var `default`: Self { + .init { state, action, _ in + switch action { + case .swapCurrencyType: + switch state.currencyType { + case .usd: state.currencyType = .zec + case .zec: state.currencyType = .usd + } + } + return .none + } + } +} diff --git a/secant/UIComponents/TextFields/TransactionCurrencySelector.swift b/secant/UIComponents/TextFields/TransactionCurrencySelector.swift new file mode 100644 index 0000000..229ab4e --- /dev/null +++ b/secant/UIComponents/TextFields/TransactionCurrencySelector.swift @@ -0,0 +1,36 @@ +// +// TransactionCurrencySelector.swift +// secant-testnet +// +// Created by Adam Stener on 4/4/22. +// + +import SwiftUI +import ComposableArchitecture + +struct TransactionCurrencySelector: View { + let store: CurrencySelectionStore + + var body: some View { + WithViewStore(store) { viewStore in + Button( + action: { viewStore.send(.swapCurrencyType) }, + label: { + HStack { + Text(CurrencySelectionState.Currency.usd.acronym) + .foregroundColor( + viewStore.currencyType == .usd ? .yellow : .white + ) + + Asset.Assets.Icons.swap.image + + Text(CurrencySelectionState.Currency.zec.acronym) + .foregroundColor( + viewStore.currencyType == .zec ? .yellow : .white + ) + } + } + ) + } + } +} diff --git a/secant/UIComponents/TextFields/TransactionInputStore.swift b/secant/UIComponents/TextFields/TransactionInputStore.swift new file mode 100644 index 0000000..30f64d9 --- /dev/null +++ b/secant/UIComponents/TextFields/TransactionInputStore.swift @@ -0,0 +1,101 @@ +// +// TransactionInputStore.swift +// secant-testnet +// +// Created by Adam Stener on 4/5/22. +// + +import ComposableArchitecture + +typealias TransactionInputReducer = Reducer< + TransactionInputState, + TransactionInputAction, + TransactionInputEnvironment +> +typealias TransactionReducerData = (inout TransactionInputState, TransactionInputAction) -> Void +typealias TransactionInputStore = Store + +struct TransactionInputState: Equatable { + var textFieldState: TextFieldState + var currencySelectionState: CurrencySelectionState +} + +enum TransactionInputAction { + case setMax(Double) + case textField(TextFieldAction) + case currencySelection(CurrencySelectionAction) +} + +struct TransactionInputEnvironment: Equatable {} + +func maxOverride(_ reducer: @escaping (TransactionReducerData)) -> TransactionReducerData { + return { state, action in + switch action { + case .setMax(let value): + state.textFieldState.text = "\(value)" + state.currencySelectionState.currencyType = .usd + + default: break + } + + reducer(&state, action) + } +} + +extension TransactionInputReducer { + static let `default` = TransactionInputReducer.combine( + [ + textFieldReducer, + currencySelectionReducer, + maxOverride, + currencyUpdate + ] + ) + + static let maxOverride = TransactionInputReducer { state, action, _ in + switch action { + case .setMax(let value): + state.currencySelectionState.currencyType = .zec + state.textFieldState.text = "\(value)" + + default: break + } + + return .none + } + + static let currencyUpdate = TransactionInputReducer { state, action, _ in + switch action { + case .currencySelection: + guard let currentDoubleValue = Double(state.textFieldState.text) else { + return .none + } + + let currencyType = state.currencySelectionState.currencyType + + // The 2100 is another hard coded value (🚀🌒) but the store could + // have a dependency injected that would be responsible for + // providing the current exchange rate. + let newValue = currencyType == .zec ? + currentDoubleValue / 2100 : + currentDoubleValue * 2100 + state.textFieldState.text = "\(newValue)" + + default: break + } + + return .none + } + + private static let textFieldReducer: TransactionInputReducer = TextFieldReducer.default.pullback( + state: \TransactionInputState.textFieldState, + action: /TransactionInputAction.textField, + environment: { _ in return .init() } + ) + + private static let currencySelectionReducer: TransactionInputReducer = CurrencySelectionReducer.default.pullback( + state: \TransactionInputState.currencySelectionState, + action: /TransactionInputAction.currencySelection, + environment: { _ in return .init() } + ) +} diff --git a/secant/UIComponents/TextFields/TransactionTextField.swift b/secant/UIComponents/TextFields/TransactionTextField.swift new file mode 100644 index 0000000..0497357 --- /dev/null +++ b/secant/UIComponents/TextFields/TransactionTextField.swift @@ -0,0 +1,72 @@ +// +// TransactionTextField.swift +// secant-testnet +// +// Created by Adam Stener on 4/4/22. +// + +import SwiftUI +import ComposableArchitecture + +struct TransactionTextField: View { + let store: TransactionInputStore + + // Constant example used here, this could be injected by a dependency + // Access to this value could also be injected into the store as a dependency + // with a function to prouce this value. + let maxTransactionValue = 500.0 + + var body: some View { + WithViewStore(store) { viewStore in + VStack { + SingleLineTextField( + placeholderText: "$0", + title: "How much?", + store: store.scope( + state: \.textFieldState, + action: TransactionInputAction.textField + ), + titleAccessoryView: { + Button( + action: { + viewStore.send(.setMax(maxTransactionValue)) + }, + label: { Text("Max") } + ) + .textFieldTitleAccessoryButtonStyle + }, + inputAccessoryView: { + TransactionCurrencySelector( + store: store.scope( + state: \.currencySelectionState, + action: TransactionInputAction.currencySelection + ) + ) + } + ) + } + } + } +} + +struct TransactionTextField_Previews: PreviewProvider { + static var previews: some View { + TransactionTextField( + store: TransactionInputStore( + initialState: .init( + textFieldState: .init( + validationType: .floatingPoint, + text: "" + ), + currencySelectionState: .init(currencyType: .usd) + ), + reducer: .default, + environment: .init() + ) + ) + .preferredColorScheme(.dark) + .padding(.horizontal, 50) + .applyScreenBackground() + .previewLayout(.fixed(width: 500, height: 200)) + } +} diff --git a/secant/Util/DebugFrame.swift b/secant/Util/DebugFrame.swift new file mode 100644 index 0000000..fc6d95f --- /dev/null +++ b/secant/Util/DebugFrame.swift @@ -0,0 +1,31 @@ +// +// DebugFrame.swift +// secant-testnet +// +// Created by Adam Stener on 3/25/22. +// + +import SwiftUI + +struct DebugFrame: ViewModifier { + let color: Color + func body(content: Content) -> some View { + content + .overlay( + Rectangle() + .strokeBorder( + style: StrokeStyle( + lineWidth: 0.5, + dash: [3] + ) + ) + ) + .foregroundColor(color) + } +} + +extension View { + func debug(_ color: Color) -> some View { + modifier(DebugFrame(color: color)) + } +} diff --git a/secant/Util/Strings.swift b/secant/Util/Strings.swift index 18231dc..1ff9e03 100644 --- a/secant/Util/Strings.swift +++ b/secant/Util/Strings.swift @@ -8,3 +8,57 @@ extension String { self.init(stringLiteral: output) } } + +extension String { + // TODO: Issue #245 Add Validation Regex that support localization + private static let floatingPointRegex = "^[0-9]*.?[0-9]+" + private static let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}" + private static let phoneRegex = "^^\\+(?:[0-9]?){6,14}[0-9]$" + + public enum ValidationType: Equatable { + case custom(String) + case email + case floatingPoint + case maxLength(Int) + case minLength(Int) + case phoneNumber + + func isValid(text: String) -> Bool { + switch self { + case .custom(let regex): + return text.validate(using: regex) + + case .email: + return text.validate(using: .emailRegex) + + case .floatingPoint: + return text.validate(using: .floatingPointRegex) + + case .maxLength(let length): + return text.count <= length && !text.isEmpty + + case .minLength(let length): + return text.count >= length + + case .phoneNumber: + return text.validate(using: .phoneRegex) + } + } + } + + private func validate(using regex: String) -> Bool { + guard let regex = try? NSRegularExpression(pattern: regex) else { return false } + + return regex.firstMatch( + in: self, + options: [], + range: NSRange(location: 0, length: self.utf16.count) + ) != nil + } + + public func isValid(for validationType: ValidationType?) -> Bool { + guard let validationType = validationType else { return true } + + return validationType.isValid(text: self) + } +}