diff --git a/secant.xcodeproj/project.pbxproj b/secant.xcodeproj/project.pbxproj index d876652..86b586b 100644 --- a/secant.xcodeproj/project.pbxproj +++ b/secant.xcodeproj/project.pbxproj @@ -400,6 +400,7 @@ 9E486DF429B9EEC4003E6945 /* UIResponder+Current.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E486DF229B9EEC4003E6945 /* UIResponder+Current.swift */; }; 9E486DF929BA09C2003E6945 /* UIKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E486DF829BA09C2003E6945 /* UIKit+Extensions.swift */; }; 9E486DFA29BA09C2003E6945 /* UIKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E486DF829BA09C2003E6945 /* UIKit+Extensions.swift */; }; + 9E4AA4F829BF76BB00752BB3 /* About.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4AA4F729BF76BB00752BB3 /* About.swift */; }; 9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */; }; 9E5BF63F2819542C00BA3F17 /* WalletEventsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF63E2819542C00BA3F17 /* WalletEventsTests.swift */; }; 9E5BF641281FD7B600BA3F17 /* TransactionFailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF640281FD7B600BA3F17 /* TransactionFailedView.swift */; }; @@ -723,6 +724,7 @@ 9E486DEF29B9EE84003E6945 /* KeyboardAdaptive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardAdaptive.swift; sourceTree = ""; }; 9E486DF229B9EEC4003E6945 /* UIResponder+Current.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIResponder+Current.swift"; sourceTree = ""; }; 9E486DF829BA09C2003E6945 /* UIKit+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIKit+Extensions.swift"; sourceTree = ""; }; + 9E4AA4F729BF76BB00752BB3 /* About.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = About.swift; sourceTree = ""; }; 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecantButtonStyles.swift; sourceTree = ""; }; 9E5BF63B2818305D00BA3F17 /* TransactionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionState.swift; sourceTree = ""; }; 9E5BF63E2819542C00BA3F17 /* WalletEventsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletEventsTests.swift; sourceTree = ""; }; @@ -1449,6 +1451,14 @@ path = SnapshotTests; sourceTree = ""; }; + 9E4AA4F629BF76AA00752BB3 /* Views */ = { + isa = PBXGroup; + children = ( + 9E4AA4F729BF76BB00752BB3 /* About.swift */, + ); + path = Views; + sourceTree = ""; + }; 9E5BF63D281953F900BA3F17 /* WalletEventsTests */ = { isa = PBXGroup; children = ( @@ -2132,6 +2142,7 @@ children = ( F9971A6227680DFE00A2DB75 /* SettingsStore.swift */, F9971A6427680DFE00A2DB75 /* SettingsView.swift */, + 9E4AA4F629BF76AA00752BB3 /* Views */, 9E612C6D2987A96500D09B09 /* UIKitBridge */, ); path = Settings; @@ -2802,6 +2813,7 @@ 9E153A612920CE2700112F41 /* MnemonicMocks.swift in Sources */, 34DA414728E4385800F8CC61 /* TransactionSendingView.swift in Sources */, F96B41E9273B501F0021B49A /* WalletEventsFlowView.swift in Sources */, + 9E4AA4F829BF76BB00752BB3 /* About.swift in Sources */, 9EBDF96E291ECED4000A1A05 /* CaptureDeviceLiveKey.swift in Sources */, 3467319929AE374300974482 /* SupportDataGeneratorInterface.swift in Sources */, 9EBDF968291ECDA2000A1A05 /* AudioServicesInterface.swift in Sources */, diff --git a/secant/Features/Settings/SettingsStore.swift b/secant/Features/Settings/SettingsStore.swift index 3a99698..7da33fd 100644 --- a/secant/Features/Settings/SettingsStore.swift +++ b/secant/Features/Settings/SettingsStore.swift @@ -8,10 +8,13 @@ typealias SettingsViewStore = ViewStore? + var appVersion = "" + var appBuild = "" var destination: Destination? var exportLogsState: ExportLogsReducer.State @BindingState var isCrashReportingOn: Bool @@ -32,6 +35,7 @@ struct SettingsReducer: ReducerProtocol { case updateDestination(SettingsReducer.State.Destination?) } + @Dependency(\.appVersion) var appVersion @Dependency(\.localAuthentication) var localAuthentication @Dependency(\.mnemonic) var mnemonic @Dependency(\.sdkSynchronizer) var sdkSynchronizer @@ -45,6 +49,8 @@ struct SettingsReducer: ReducerProtocol { switch action { case .onAppear: state.isCrashReportingOn = !userStoredPreferences.isUserOptedOutOfCrashReporting() + state.appVersion = appVersion.appVersion() + state.appBuild = appVersion.appBuild() return .none case .backupWalletAccessRequest: return .run { send in @@ -144,6 +150,13 @@ extension SettingsViewStore { embed: { $0 ? .backupPhrase : nil } ) } + + var bindingForAbout: Binding { + self.destinationBinding.map( + extract: { $0 == .about }, + embed: { $0 ? .about : nil } + ) + } } // MARK: - Store diff --git a/secant/Features/Settings/SettingsView.swift b/secant/Features/Settings/SettingsView.swift index bf49485..d8e0825 100644 --- a/secant/Features/Settings/SettingsView.swift +++ b/secant/Features/Settings/SettingsView.swift @@ -43,6 +43,14 @@ struct SettingsView: View { .frame(height: 50) Spacer() + + Button( + action: { viewStore.send(.updateDestination(.about)) }, + label: { Text(L10n.Settings.about) } + ) + .activeButtonStyle + .frame(maxHeight: 50) + .padding(.bottom, 50) } .padding(.horizontal, 30) .navigationTitle(L10n.Settings.title) @@ -53,6 +61,12 @@ struct SettingsView: View { RecoveryPhraseDisplayView(store: store.backupPhraseStore()) } ) + .navigationLinkEmpty( + isActive: viewStore.bindingForAbout, + destination: { + About(store: store) + } + ) .onAppear { viewStore.send(.onAppear) } .alert(self.store.scope(state: \.alert), dismiss: .dismissAlert) .alert( diff --git a/secant/Features/Settings/Views/About.swift b/secant/Features/Settings/Views/About.swift new file mode 100644 index 0000000..9f05668 --- /dev/null +++ b/secant/Features/Settings/Views/About.swift @@ -0,0 +1,31 @@ +// +// About.swift +// secant-testnet +// +// Created by Lukáš Korba on 13.03.2023. +// + +import SwiftUI +import ComposableArchitecture + +struct About: View { + let store: SettingsStore + + var body: some View { + WithViewStore(store) { viewStore in + VStack { + Text(L10n.Settings.version(viewStore.appVersion, viewStore.appBuild)) + .foregroundColor(Asset.Colors.Mfp.fontDark.color) + + Spacer() + } + .applyScreenBackground() + } + } +} + +struct About_Previews: PreviewProvider { + static var previews: some View { + About(store: .placeholder) + } +} diff --git a/secant/Resources/Generated/L10n.swift b/secant/Resources/Generated/L10n.swift index 9e3cb56..b9db1ce 100644 --- a/secant/Resources/Generated/L10n.swift +++ b/secant/Resources/Generated/L10n.swift @@ -478,6 +478,8 @@ internal enum L10n { internal static let title = L10n.tr("Localizable", "send.title", fallback: "Send Zcash") } internal enum Settings { + /// About + internal static let about = L10n.tr("Localizable", "settings.about", fallback: "About") /// Backup Wallet internal static let backupWallet = L10n.tr("Localizable", "settings.backupWallet", fallback: "Backup Wallet") /// Enable Crash Reporting @@ -490,6 +492,10 @@ internal enum L10n { internal static let feedback = L10n.tr("Localizable", "settings.feedback", fallback: "Send us feedback!") /// Settings internal static let title = L10n.tr("Localizable", "settings.title", fallback: "Settings") + /// Version %@ (%@) + internal static func version(_ p1: Any, _ p2: Any) -> String { + return L10n.tr("Localizable", "settings.version", String(describing: p1), String(describing: p2), fallback: "Version %@ (%@)") + } internal enum Alert { internal enum CantBackupWallet { /// Error: %@ diff --git a/secant/Resources/Localizable.strings b/secant/Resources/Localizable.strings index 2320533..e33bff7 100644 --- a/secant/Resources/Localizable.strings +++ b/secant/Resources/Localizable.strings @@ -119,6 +119,8 @@ "settings.exporting" = "Exporting..."; "settings.exportLogs" = "Export & share logs"; "settings.feedback" = "Send us feedback!"; +"settings.about" = "About"; +"settings.version" = "Version %@ (%@)"; "settings.title" = "Settings"; "settings.alert.cantBackupWallet.title" = "Can't backup wallet"; "settings.alert.cantBackupWallet.message" = "Error: %@"; diff --git a/secantTests/SnapshotTests/SettingsSnapshotTests/SettingsSnapshotTests.swift b/secantTests/SnapshotTests/SettingsSnapshotTests/SettingsSnapshotTests.swift index 4619225..914676e 100644 --- a/secantTests/SnapshotTests/SettingsSnapshotTests/SettingsSnapshotTests.swift +++ b/secantTests/SnapshotTests/SettingsSnapshotTests/SettingsSnapshotTests.swift @@ -18,8 +18,23 @@ class SettingsSnapshotTests: XCTestCase { .dependency(\.localAuthentication, .mockAuthenticationFailed) .dependency(\.sdkSynchronizer, NoopSDKSynchronizer()) .dependency(\.walletStorage, .noOp) + .dependency(\.appVersion, .mock) ) addAttachments(SettingsView(store: store)) } + + func testAboutSnapshot() throws { + let store = Store( + initialState: .placeholder, + reducer: SettingsReducer() + .dependency(\.localAuthentication, .mockAuthenticationFailed) + .dependency(\.sdkSynchronizer, NoopSDKSynchronizer()) + .dependency(\.walletStorage, .noOp) + .dependency(\.appVersion, .liveValue) + ) + + ViewStore(store).send(.onAppear) + addAttachments(About(store: store)) + } }