diff --git a/modules/Sources/Features/ImportWallet/ImportBirthdayView.swift b/modules/Sources/Features/ImportWallet/ImportBirthdayView.swift index 029569a2..1773c9c7 100644 --- a/modules/Sources/Features/ImportWallet/ImportBirthdayView.swift +++ b/modules/Sources/Features/ImportWallet/ImportBirthdayView.swift @@ -28,9 +28,10 @@ public struct ImportBirthdayView: View { .foregroundColor(Asset.Colors.primary.color) .minimumScaleFactor(0.3) .multilineTextAlignment(.center) - .padding(.bottom, 10) - TextField(L10n.ImportWallet.optionalBirthday, text: viewStore.bindingForRedactableBirthday(viewStore.birthdayHeight)) + Text(L10n.ImportWallet.optionalBirthday) + + TextField("", text: viewStore.bindingForRedactableBirthday(viewStore.birthdayHeight)) .frame(height: 40) .font(.custom(FontFamily.Archivo.semiBold.name, size: 25)) .keyboardType(.numberPad) diff --git a/modules/Sources/Features/ImportWallet/ImportWalletView.swift b/modules/Sources/Features/ImportWallet/ImportWalletView.swift index 1965477f..6e8ef3dc 100644 --- a/modules/Sources/Features/ImportWallet/ImportWalletView.swift +++ b/modules/Sources/Features/ImportWallet/ImportWalletView.swift @@ -9,11 +9,17 @@ import SwiftUI import ComposableArchitecture import Generated import UIComponents +import Utils public struct ImportWalletView: View { + private enum InputID: Hashable { + case seed + } + var store: ImportWalletStore - @FocusState private var seedFieldFocused: Bool + @FocusState public var isFocused: Bool + @State private var message = "" public init(store: ImportWalletStore) { self.store = store @@ -21,62 +27,100 @@ public struct ImportWalletView: View { public var body: some View { ScrollView { - WithViewStore(store) { viewStore in - VStack(alignment: .center) { - ZashiIcon() - .padding(.vertical, 30) - - Text(L10n.ImportWallet.description) - .font(.custom(FontFamily.Archivo.semiBold.name, size: 25)) - .foregroundColor(Asset.Colors.primary.color) - .multilineTextAlignment(.center) - .padding(.bottom, 10) - - Text(L10n.ImportWallet.message) - .font(.custom(FontFamily.Inter.medium.name, size: 14)) - .foregroundColor(Asset.Colors.primary.color) - .multilineTextAlignment(.center) - .padding(.bottom, 20) - .padding(.horizontal, 10) - - ImportSeedEditor(store: store) - .frame(minWidth: 270) - .frame(height: 215) - .focused($seedFieldFocused) - .toolbar { - ToolbarItemGroup(placement: .keyboard) { - Spacer() - - Button(L10n.General.done.uppercased()) { - seedFieldFocused = false + ScrollViewReader { value in + WithViewStore(store) { viewStore in + VStack(alignment: .center) { + ZashiIcon() + .padding(.vertical, 30) + + Text(L10n.ImportWallet.description) + .font(.custom(FontFamily.Archivo.semiBold.name, size: 25)) + .foregroundColor(Asset.Colors.primary.color) + .multilineTextAlignment(.center) + .padding(.bottom, 10) + + Text(L10n.ImportWallet.message) + .font(.custom(FontFamily.Inter.medium.name, size: 14)) + .foregroundColor(Asset.Colors.primary.color) + .multilineTextAlignment(.center) + .padding(.bottom, 20) + .padding(.horizontal, 10) + + TextEditor(text: $message) + .autocapitalization(.none) + .recoveryPhraseShape() + .frame(minWidth: 270) + .frame(height: 215) + .focused($isFocused) + .id(InputID.seed) + .toolbar { + ToolbarItemGroup(placement: .keyboard) { + Spacer() + + Button(L10n.General.done.uppercased()) { + isFocused = false + } + .foregroundColor(Asset.Colors.primary.color) + .font(.custom(FontFamily.Inter.regular.name, size: 14)) } - .foregroundColor(Asset.Colors.primary.color) - .font(.custom(FontFamily.Inter.regular.name, size: 14)) } + .overlay { + if message.isEmpty { + HStack { + VStack { + Text(L10n.ImportWallet.enterPlaceholder) + .font(.custom(FontFamily.Inter.regular.name, size: 13)) + .foregroundColor(Asset.Colors.suppressed72.color) + .onTapGesture { + isFocused = true + } + + Spacer() + } + .padding(.top, 10) + + Spacer() + } + .padding(.leading, 10) + } else { + EmptyView() + } + } + .onChange(of: message) { value in + viewStore.send(.seedPhraseInputChanged(RedactableString(message))) + } + .onChange(of: isFocused) { update in + withAnimation { + if update { + value.scrollTo(InputID.seed, anchor: .center) + } + } + } + + Button(L10n.General.next.uppercased()) { + viewStore.send(.updateDestination(.birthday)) } - - Button(L10n.General.next.uppercased()) { - viewStore.send(.updateDestination(.birthday)) + .zcashStyle() + .frame(width: 236) + .disabled(!viewStore.isValidForm) + .padding(.top, 50) } - .zcashStyle() - .frame(width: 236) - .disabled(!viewStore.isValidForm) - .padding(.top, 50) + .padding(.horizontal, 70) + .onAppear(perform: { viewStore.send(.onAppear) }) + .navigationLinkEmpty( + isActive: viewStore.bindingForDestination(.birthday), + destination: { ImportBirthdayView(store: store) } + ) + .alert(store: store.scope( + state: \.$alert, + action: { .alert($0) } + )) + .zashiBack() } - .applyScreenBackground() - .padding(.horizontal, 70) - .onAppear(perform: { viewStore.send(.onAppear) }) - .navigationLinkEmpty( - isActive: viewStore.bindingForDestination(.birthday), - destination: { ImportBirthdayView(store: store) } - ) - .alert(store: store.scope( - state: \.$alert, - action: { .alert($0) } - )) - .zashiBack() } } + .padding(.vertical, 1) + .applyScreenBackground() } } diff --git a/modules/Sources/Features/ImportWallet/Views/ImportSeedEditor.swift b/modules/Sources/Features/ImportWallet/Views/ImportSeedEditor.swift deleted file mode 100644 index a5505966..00000000 --- a/modules/Sources/Features/ImportWallet/Views/ImportSeedEditor.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// ImportSeedEditor.swift -// secant-testnet -// -// Created by Lukáš Korba on 02/25/2022. -// - -import SwiftUI -import ComposableArchitecture -import Generated - -public struct ImportSeedEditor: View { - var store: ImportWalletStore - - public var body: some View { - WithViewStore(store) { viewStore in - TextEditor(text: viewStore.bindingForRedactableSeedPhrase(viewStore.importedSeedPhrase)) - .autocapitalization(.none) - .importSeedEditorModifier(Asset.Colors.primary.color) - } - } -} - -struct ImportSeedEditorModifier: ViewModifier { - var backgroundColor = Color.white - - func body(content: Content) -> some View { - content - .foregroundColor(Asset.Colors.primary.color) - .padding(1) - .background(backgroundColor) - } -} - -extension View { - func importSeedEditorModifier(_ backgroundColor: Color = .white) -> some View { - modifier(ImportSeedEditorModifier(backgroundColor: backgroundColor)) - } -} - -struct ImportSeedInputField_Previews: PreviewProvider { - static let width: CGFloat = 400 - static let height: CGFloat = 200 - - static var previews: some View { - Group { - ImportSeedEditor(store: .demo) - .frame(width: width, height: height) - .applyScreenBackground() - .preferredColorScheme(.light) - } - .previewLayout(.fixed(width: width + 50, height: height + 50)) - } -} diff --git a/modules/Sources/Features/Tabs/TabsStore.swift b/modules/Sources/Features/Tabs/TabsStore.swift index 49367f2b..3157cabc 100644 --- a/modules/Sources/Features/Tabs/TabsStore.swift +++ b/modules/Sources/Features/Tabs/TabsStore.swift @@ -32,7 +32,7 @@ public struct TabsReducer: ReducerProtocol { case account = 0 case send case receive - case details + case balances public var title: String { switch self { @@ -42,8 +42,8 @@ public struct TabsReducer: ReducerProtocol { return L10n.Tabs.send case .receive: return L10n.Tabs.receive - case .details: - return L10n.Tabs.details + case .balances: + return L10n.Tabs.balances } } } @@ -120,7 +120,7 @@ public struct TabsReducer: ReducerProtocol { return .none case .home(.balanceBreakdown): - state.selectedTab = .details + state.selectedTab = .balances return .none case .home: diff --git a/modules/Sources/Features/Tabs/TabsView.swift b/modules/Sources/Features/Tabs/TabsView.swift index a33211f4..e6eb4232 100644 --- a/modules/Sources/Features/Tabs/TabsView.swift +++ b/modules/Sources/Features/Tabs/TabsView.swift @@ -62,8 +62,10 @@ public struct TabsView: View { ), tokenName: tokenName ) - .tag(TabsReducer.State.Tab.details) + .tag(TabsReducer.State.Tab.balances) } + .tabViewStyle(.page(indexDisplayMode: .never)) + .padding(.bottom, 50) VStack { Spacer() @@ -122,7 +124,7 @@ public struct TabsView: View { .resizable() .frame(width: 62, height: 17) - case .details: + case .balances: Text(L10n.Tabs.balances.uppercased()) .font(.custom(FontFamily.Archivo.bold.name, size: 14)) } diff --git a/modules/Sources/Generated/L10n.swift b/modules/Sources/Generated/L10n.swift index d8521f3e..e53ee124 100644 --- a/modules/Sources/Generated/L10n.swift +++ b/modules/Sources/Generated/L10n.swift @@ -166,10 +166,12 @@ public enum L10n { /// Enter secret /// recovery phrase public static let description = L10n.tr("Localizable", "importWallet.description", fallback: "Enter secret\nrecovery phrase") + /// Enter private seed here… + public static let enterPlaceholder = L10n.tr("Localizable", "importWallet.enterPlaceholder", fallback: "Enter private seed here…") /// Enter your 24-word seed phrase to restore the associated wallet. public static let message = L10n.tr("Localizable", "importWallet.message", fallback: "Enter your 24-word seed phrase to restore the associated wallet.") - /// optional - public static let optionalBirthday = L10n.tr("Localizable", "importWallet.optionalBirthday", fallback: "optional") + /// (optional) + public static let optionalBirthday = L10n.tr("Localizable", "importWallet.optionalBirthday", fallback: "(optional)") /// Wallet Import public static let title = L10n.tr("Localizable", "importWallet.title", fallback: "Wallet Import") public enum Alert { @@ -189,9 +191,8 @@ public enum L10n { } } public enum Birthday { - /// Enter birthday - /// height - public static let title = L10n.tr("Localizable", "importWallet.birthday.title", fallback: "Enter birthday\nheight") + /// Wallet birthday height + public static let title = L10n.tr("Localizable", "importWallet.birthday.title", fallback: "Wallet birthday height") } public enum Button { /// Restore @@ -647,8 +648,6 @@ public enum L10n { public static let account = L10n.tr("Localizable", "tabs.account", fallback: "Account") /// Balances public static let balances = L10n.tr("Localizable", "tabs.balances", fallback: "Balances") - /// Details - public static let details = L10n.tr("Localizable", "tabs.details", fallback: "Details") /// Receive public static let receive = L10n.tr("Localizable", "tabs.receive", fallback: "Receive") /// Send diff --git a/modules/Sources/Generated/Resources/Localizable.strings b/modules/Sources/Generated/Resources/Localizable.strings index e90cb740..3d53bf16 100644 --- a/modules/Sources/Generated/Resources/Localizable.strings +++ b/modules/Sources/Generated/Resources/Localizable.strings @@ -72,19 +72,20 @@ "importWallet.description" = "Enter secret\nrecovery phrase"; "importWallet.message" = "Enter your 24-word seed phrase to restore the associated wallet."; "importWallet.button.restoreWallet" = "Restore"; -"importWallet.birthday.title" = "Enter birthday\nheight"; +"importWallet.birthday.title" = "Wallet birthday height"; "importWallet.seed.valid" = "VALID SEED PHRASE"; "importWallet.alert.success.title" = "Success"; "importWallet.alert.success.message" = "The wallet has been successfully recovered."; "importWallet.alert.failed.title" = "Failed to restore wallet"; "importWallet.alert.failed.message" = "Error: %@ (code: %@)"; -"importWallet.optionalBirthday" = "optional"; +"importWallet.optionalBirthday" = "(optional)"; +"importWallet.enterPlaceholder" = "Enter private seed here…"; + // MARK: - Tabs "tabs.account" = "Account"; "tabs.send" = "Send"; "tabs.receive" = "Receive"; -"tabs.details" = "Details"; "tabs.balances" = "Balances"; // MARK: - Home Screen diff --git a/modules/Sources/UIComponents/Overlays/SplashView.swift b/modules/Sources/UIComponents/Overlays/SplashView.swift index c13ef8ad..3ad5e933 100644 --- a/modules/Sources/UIComponents/Overlays/SplashView.swift +++ b/modules/Sources/UIComponents/Overlays/SplashView.swift @@ -79,7 +79,7 @@ final class SplashManager: ObservableObject { let y = screenSize.height + prevHeight if (allPoints - i) % 2 == 0 { - prevHeight += CGFloat.random(in: 10...40) + prevHeight += CGFloat.random(in: 30...70) } points.append(CGPoint(x: x, y: y)) diff --git a/modules/Sources/UIComponents/Shapes/RecoveryPhraseEditorShape.swift b/modules/Sources/UIComponents/Shapes/RecoveryPhraseEditorShape.swift new file mode 100644 index 00000000..3858034c --- /dev/null +++ b/modules/Sources/UIComponents/Shapes/RecoveryPhraseEditorShape.swift @@ -0,0 +1,43 @@ +// +// RecoveryPhraseEditorShape.swift +// +// +// Created by Lukáš Korba on 30.10.2023. +// + +import SwiftUI +import Generated + +struct RecoveryPhraseEditorShape: Shape { + func path(in rect: CGRect) -> Path { + Path { path in + path.move(to: CGPoint(x: 0, y: 0)) + path.addLine(to: CGPoint(x: 0, y: rect.height)) + path.addLine(to: CGPoint(x: rect.width, y: rect.height)) + path.addLine(to: CGPoint(x: rect.width, y: 0)) + path.closeSubpath() + } + } +} + +struct RecoveryPhraseEditorModifier: ViewModifier { + func body(content: Content) -> some View { + content + .overlay { + RecoveryPhraseEditorShape() + .stroke() + } + } +} + +extension View { + public func recoveryPhraseShape() -> some View { + modifier(RecoveryPhraseEditorModifier()) + } +} + +#Preview { + Text("some message") + .frame(width: 320, height: 145) + .recoveryPhraseShape() +} diff --git a/secantTests/TabsTests/TabsTests.swift b/secantTests/TabsTests/TabsTests.swift index b28ef568..1486a70a 100644 --- a/secantTests/TabsTests/TabsTests.swift +++ b/secantTests/TabsTests/TabsTests.swift @@ -22,7 +22,7 @@ class TabsTests: XCTestCase { ) await store.send(.home(.balanceBreakdown)) { state in - state.selectedTab = .details + state.selectedTab = .balances } } @@ -97,12 +97,12 @@ class TabsTests: XCTestCase { func testDetailsTabTitle() { var tabsState = TabsReducer.State.placeholder - tabsState.selectedTab = .details + tabsState.selectedTab = .balances XCTAssertEqual( tabsState.selectedTab.title, - L10n.Tabs.details, - "Name of the details tab should be '\(L10n.Tabs.details)' but received \(tabsState.selectedTab.title)" + L10n.Tabs.balances, + "Name of the balances tab should be '\(L10n.Tabs.balances)' but received \(tabsState.selectedTab.title)" ) }