[#1036] Wipe wallet in advanced settings

- Wipe wallet feature integrated into advanced settings
This commit is contained in:
Lukas Korba 2024-03-27 16:20:56 +01:00
parent ecc65f9a83
commit da3687b9d1
8 changed files with 236 additions and 6 deletions

View File

@ -19,6 +19,7 @@ let package = Package(
.library(name: "DatabaseFiles", targets: ["DatabaseFiles"]),
.library(name: "Date", targets: ["Date"]),
.library(name: "Deeplink", targets: ["Deeplink"]),
.library(name: "DeleteWallet", targets: ["DeleteWallet"]),
.library(name: "DerivationTool", targets: ["DerivationTool"]),
.library(name: "DiskSpaceChecker", targets: ["DiskSpaceChecker"]),
.library(name: "ExportLogs", targets: ["ExportLogs"]),
@ -172,6 +173,18 @@ let package = Package(
],
path: "Sources/Dependencies/Deeplink"
),
.target(
name: "DeleteWallet",
dependencies: [
"Generated",
"SDKSynchronizer",
"UIComponents",
"Utils",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/DeleteWallet"
),
.target(
name: "DerivationTool",
dependencies: [
@ -504,6 +517,7 @@ let package = Package(
name: "Settings",
dependencies: [
"AppVersion",
"DeleteWallet",
"Generated",
"LocalAuthenticationHandler",
"Models",

View File

@ -0,0 +1,54 @@
//
// DeleteWalletStore.swift
// secant-testnet
//
// Created by Lukáš Korba on 03-27-2024
//
import ComposableArchitecture
import Generated
import SDKSynchronizer
import Utils
import ZcashLightClientKit
@Reducer
public struct DeleteWallet {
@ObservableState
public struct State: Equatable {
public var isAcknowledged: Bool = false
public var isProcessing: Bool = false
public init(
isAcknowledged: Bool = false,
isProcessing: Bool = false
) {
self.isAcknowledged = isAcknowledged
self.isProcessing = isProcessing
}
}
public enum Action: BindableAction, Equatable {
case binding(BindingAction<DeleteWallet.State>)
case deleteTapped
}
@Dependency(\.sdkSynchronizer) var sdkSynchronizer
public init() { }
public var body: some Reducer<State, Action> {
BindingReducer()
Reduce { state, action in
switch action {
case .binding:
return .none
case .deleteTapped:
state.isProcessing = true
return .none
}
}
}
}

View File

@ -0,0 +1,87 @@
//
// DeleteWalletView.swift
// secant-testnet
//
// Created by Lukáš Korba on 03-27-2024
//
import SwiftUI
import ComposableArchitecture
import Generated
import UIComponents
public struct DeleteWalletView: View {
@Perception.Bindable var store: StoreOf<DeleteWallet>
public init(store: StoreOf<DeleteWallet>) {
self.store = store
}
public var body: some View {
WithPerceptionTracking {
ScrollView {
Group {
ZashiIcon()
Text(L10n.DeleteWallet.title)
.font(.custom(FontFamily.Archivo.semiBold.name, size: 25))
.padding(.bottom, 15)
VStack(alignment: .leading) {
Text(L10n.DeleteWallet.message1)
.font(.custom(FontFamily.Inter.bold.name, size: 16))
Text(L10n.DeleteWallet.message2)
.font(.custom(FontFamily.Inter.medium.name, size: 16))
.padding(.top, 20)
}
HStack {
Toggle(isOn: $store.isAcknowledged, label: {
Text(L10n.DeleteWallet.iUnderstand)
.font(.custom(FontFamily.Inter.medium.name, size: 14))
})
.toggleStyle(CheckboxToggleStyle())
Spacer()
}
.padding(.top, 30)
Button(L10n.DeleteWallet.actionButtonTitle.uppercased()) {
store.send(.deleteTapped)
}
.zcashStyle()
.disabled(!store.isAcknowledged || store.isProcessing)
.padding(.vertical, 50)
}
.padding(.horizontal, 60)
}
.padding(.vertical, 1)
.zashiBack(store.isProcessing)
}
.navigationBarTitleDisplayMode(.inline)
.applyScreenBackground(withPattern: true)
}
}
// MARK: - Previews
#Preview {
DeleteWalletView(store: DeleteWallet.demo)
}
// MARK: - Store
extension DeleteWallet {
public static var demo = StoreOf<DeleteWallet>(
initialState: .initial
) {
DeleteWallet()
}
}
// MARK: - Placeholders
extension DeleteWallet.State {
public static let initial = DeleteWallet.State()
}

View File

@ -295,7 +295,7 @@ extension RootReducer {
state.alert = AlertState.wipeRequest()
return .none
case .initialization(.nukeWallet):
case .initialization(.nukeWallet), .tabs(.settings(.advancedSettings(.deleteWallet(.deleteTapped)))):
guard let wipePublisher = sdkSynchronizer.wipe() else {
return Effect.send(.nukeWalletFailed)
}

View File

@ -2,6 +2,7 @@ import SwiftUI
import ComposableArchitecture
import MessageUI
import DeleteWallet
import Generated
import LocalAuthenticationHandler
import Models
@ -18,10 +19,12 @@ public struct AdvancedSettingsReducer: Reducer {
public struct State: Equatable {
public enum Destination {
case backupPhrase
case deleteWallet
case privateDataConsent
case serverSetup
}
public var deleteWallet: DeleteWallet.State
public var destination: Destination?
public var isRestoringWallet = false
public var phraseDisplayState: RecoveryPhraseDisplay.State
@ -29,12 +32,14 @@ public struct AdvancedSettingsReducer: Reducer {
public var serverSetupState: ServerSetup.State
public init(
deleteWallet: DeleteWallet.State,
destination: Destination? = nil,
isRestoringWallet: Bool = false,
phraseDisplayState: RecoveryPhraseDisplay.State,
privateDataConsentState: PrivateDataConsentReducer.State,
serverSetupState: ServerSetup.State
) {
self.deleteWallet = deleteWallet
self.destination = destination
self.isRestoringWallet = isRestoringWallet
self.phraseDisplayState = phraseDisplayState
@ -45,6 +50,7 @@ public struct AdvancedSettingsReducer: Reducer {
public enum Action: Equatable {
case backupWalletAccessRequest
case deleteWallet(DeleteWallet.Action)
case phraseDisplay(RecoveryPhraseDisplay.Action)
case privateDataConsent(PrivateDataConsentReducer.Action)
case restoreWalletTask
@ -67,7 +73,10 @@ public struct AdvancedSettingsReducer: Reducer {
await send(.updateDestination(.backupPhrase))
}
}
case .deleteWallet:
return .none
case .phraseDisplay(.finishedPressed):
state.destination = nil
return .none
@ -122,6 +131,10 @@ public struct AdvancedSettingsReducer: Reducer {
Scope(state: \.serverSetupState, action: /Action.serverSetup) {
ServerSetup()
}
Scope(state: \.deleteWallet, action: /Action.deleteWallet) {
DeleteWallet()
}
}
}
@ -134,14 +147,14 @@ extension AdvancedSettingsViewStore {
send: AdvancedSettingsReducer.Action.updateDestination
)
}
var bindingForBackupPhrase: Binding<Bool> {
self.destinationBinding.map(
extract: { $0 == .backupPhrase },
embed: { $0 ? .backupPhrase : nil }
)
}
var bindingForPrivateDataConsent: Binding<Bool> {
self.destinationBinding.map(
extract: { $0 == .privateDataConsent },
@ -155,6 +168,13 @@ extension AdvancedSettingsViewStore {
embed: { $0 ? .serverSetup : nil }
)
}
var bindingDeleteWallet: Binding<Bool> {
self.destinationBinding.map(
extract: { $0 == .deleteWallet },
embed: { $0 ? .deleteWallet : nil }
)
}
}
// MARK: - Store
@ -180,12 +200,20 @@ extension AdvancedSettingsStore {
action: AdvancedSettingsReducer.Action.serverSetup
)
}
func deleteWalletStore() -> StoreOf<DeleteWallet> {
self.scope(
state: \.deleteWallet,
action: AdvancedSettingsReducer.Action.deleteWallet
)
}
}
// MARK: Placeholders
extension AdvancedSettingsReducer.State {
public static let initial = AdvancedSettingsReducer.State(
deleteWallet: .initial,
phraseDisplayState: RecoveryPhraseDisplay.State(
phrase: nil,
showBackButton: false,
@ -205,6 +233,7 @@ extension AdvancedSettingsStore {
public static let demo = AdvancedSettingsStore(
initialState: .init(
deleteWallet: .initial,
phraseDisplayState: RecoveryPhraseDisplay.State(
phrase: nil,
birthday: nil

View File

@ -7,6 +7,8 @@
import SwiftUI
import ComposableArchitecture
import DeleteWallet
import Generated
import RecoveryPhraseDisplay
import UIComponents
@ -49,26 +51,45 @@ public struct AdvancedSettingsView: View {
ServerSetupView(store: store.serverSetupStore())
}
)
.navigationLinkEmpty(
isActive: viewStore.bindingDeleteWallet,
destination: {
DeleteWalletView(store: store.deleteWalletStore())
}
)
.onAppear {
isRestoringWalletBadgeOn = viewStore.isRestoringWallet
}
.onChange(of: viewStore.isRestoringWallet) { isRestoringWalletBadgeOn = $0 }
.padding(.horizontal, 70)
Button(L10n.Settings.exportPrivateData.uppercased()) {
viewStore.send(.updateDestination(.privateDataConsent))
}
.zcashStyle()
.padding(.bottom, 25)
.padding(.horizontal, 70)
Button(L10n.Settings.chooseServer.uppercased()) {
viewStore.send(.updateDestination(.serverSetup))
}
.zcashStyle()
.padding(.bottom, 80)
.padding(.horizontal, 70)
Spacer()
Button(L10n.Settings.deleteZashi.uppercased()) {
viewStore.send(.updateDestination(.deleteWallet))
}
.zcashStyle()
.padding(.bottom, 20)
.padding(.horizontal, 70)
Text(L10n.Settings.deleteZashiWarning)
.font(.custom(FontFamily.Inter.medium.name, size: 11))
.padding(.bottom, 50)
.padding(.horizontal, 20)
}
.padding(.horizontal, 70)
}
.navigationBarTitleDisplayMode(.inline)
.applyScreenBackground()

View File

@ -84,6 +84,18 @@ public enum L10n {
public static let message = L10n.tr("Localizable", "balances.hintBox.message", fallback: "Zashi uses the latest network upgrade and does not support sending transparent (unshielded) ZEC. Use the Shield and Consolidate button to shield your funds, which will add to your available balance and make your ZEC spendable.")
}
}
public enum DeleteWallet {
/// Delete Zashi
public static let actionButtonTitle = L10n.tr("Localizable", "deleteWallet.actionButtonTitle", fallback: "Delete Zashi")
/// I understand
public static let iUnderstand = L10n.tr("Localizable", "deleteWallet.iUnderstand", fallback: "I understand")
/// Please don't delete this app unless you're sure you understand the effects.
public static let message1 = L10n.tr("Localizable", "deleteWallet.message1", fallback: "Please don't delete this app unless you're sure you understand the effects.")
/// Deleting the Zashi app will delete the database and cached data. Any funds you have in this wallet will be lost and can only be recovered by using your Zashi secret recovery phrase in Zashi or another Zcash wallet.
public static let message2 = L10n.tr("Localizable", "deleteWallet.message2", fallback: "Deleting the Zashi app will delete the database and cached data. Any funds you have in this wallet will be lost and can only be recovered by using your Zashi secret recovery phrase in Zashi or another Zcash wallet.")
/// Delete Zashi
public static let title = L10n.tr("Localizable", "deleteWallet.title", fallback: "Delete Zashi")
}
public enum Error {
/// possible roll back
public static let rollBack = L10n.tr("Localizable", "error.rollBack", fallback: "possible roll back")
@ -609,6 +621,10 @@ public enum L10n {
public static let advanced = L10n.tr("Localizable", "settings.advanced", fallback: "Advanced")
/// Choose a server
public static let chooseServer = L10n.tr("Localizable", "settings.chooseServer", fallback: "Choose a server")
/// Delete Zashi
public static let deleteZashi = L10n.tr("Localizable", "settings.deleteZashi", fallback: "Delete Zashi")
/// (You will be asked to confirm on next screen)
public static let deleteZashiWarning = L10n.tr("Localizable", "settings.deleteZashiWarning", fallback: "(You will be asked to confirm on next screen)")
/// Export logs only
public static let exportLogsOnly = L10n.tr("Localizable", "settings.exportLogsOnly", fallback: "Export logs only")
/// Export private data

View File

@ -169,6 +169,13 @@
"send.alert.failure.title" = "Failed to send funds";
"send.alert.failure.message" = "Error: %@";
// MARK: - Delete Wallet
"deleteWallet.title" = "Delete Zashi";
"deleteWallet.message1" = "Please don't delete this app unless you're sure you understand the effects.";
"deleteWallet.message2" = "Deleting the Zashi app will delete the database and cached data. Any funds you have in this wallet will be lost and can only be recovered by using your Zashi secret recovery phrase in Zashi or another Zcash wallet.";
"deleteWallet.iUnderstand" = "I understand";
"deleteWallet.actionButtonTitle" = "Delete Zashi";
// MARK: - Settings
"settings.feedback" = "Send us feedback";
"settings.advanced" = "Advanced";
@ -177,6 +184,8 @@
"settings.exportPrivateData" = "Export private data";
"settings.chooseServer" = "Choose a server";
"settings.exportLogsOnly" = "Export logs only";
"settings.deleteZashi" = "Delete Zashi";
"settings.deleteZashiWarning" = "(You will be asked to confirm on next screen)";
"settings.about.info" = "Send and receive ZEC on Zashi!
Zashi is a minimal-design, self-custody, ZEC-only shielded wallet that keeps your transaction history and wallet balance private. Built by Zcashers, for Zcashers. Developed and maintained by Electric Coin Co., the inventor of Zcash, Zashi features a built-in user-feedback mechanism to enable more features, more quickly.";
"settings.version" = "Version %@ (%@)";