// // RootDebug.swift // secant // // Created by Lukáš Korba on 02.03.2023. // import Combine import ComposableArchitecture import Foundation import ZcashLightClientKit import Generated import Models import Pasteboard /// In this file is a collection of helpers that control all state and action related operations /// for the `Root` with a connection to the UI navigation. extension Root { public struct DebugState { } public indirect enum DebugAction { case cancelRescan case cantStartSync(ZcashError) case copySeedToPasteboard case flagUpdated case rateTheApp case rescanBlockchain case rewindDone(ZcashError?, Root.Action) case updateFlag(FeatureFlag, Bool) case walletConfigLoaded(WalletConfig) } // swiftlint:disable:next cyclomatic_complexity public func debugReduce() -> Reduce { Reduce { state, action in switch action { case .debug(.rescanBlockchain): state.confirmationDialog = ConfirmationDialogState.rescanRequest() return .none case .debug(.cancelRescan): state.confirmationDialog = nil return .none case .confirmationDialog(.presented(.quickRescan)): state.destinationState.destination = .home return rewind(policy: .quick, sourceAction: .quickRescan) case .confirmationDialog(.presented(.fullRescan)): state.destinationState.destination = .home return rewind(policy: .birthday, sourceAction: .fullRescan) case let .debug(.rewindDone(error, _)): if let error { state.alert = AlertState.rewindFailed(error.toZcashError()) return .none } else { return .run { send in do { try await sdkSynchronizer.start(false) } catch { await send(.debug(.cantStartSync(error.toZcashError()))) } } } case let .debug(.updateFlag(flag, isEnabled)): return .publisher { walletConfigProvider.update(flag, !isEnabled) .receive(on: mainQueue) .map { _ in return Action.debug(.flagUpdated) } } .cancellable(id: WalletConfigCancelId, cancelInFlight: true) case .debug(.flagUpdated): return .publisher { walletConfigProvider.load() .receive(on: mainQueue) .map { Action.debug(.walletConfigLoaded($0)) } } .cancellable(id: WalletConfigCancelId, cancelInFlight: true) case .debug(.copySeedToPasteboard): let storedWallet = try? walletStorage.exportWallet() guard let phrase = storedWallet?.seedPhrase.value() else { return .none } pasteboard.setString(phrase.redacted) return .none case let .debug(.walletConfigLoaded(walletConfig)): return .send(.updateStateAfterConfigUpdate(walletConfig)) case .debug(.cantStartSync(let error)): state.alert = AlertState.cantStartSync(error) return .none case .debug(.rateTheApp): return .none default: return .none } } } private func rewind(policy: RewindPolicy, sourceAction: Action.ConfirmationDialog) -> Effect { Effect.publisher { sdkSynchronizer.rewind(policy) .replaceEmpty(with: Void()) .map { _ in Root.Action.debug(.rewindDone(nil, .confirmationDialog(.presented(sourceAction)))) } .catch { error in Just( Root.Action.debug(.rewindDone(error.toZcashError(), .confirmationDialog(.presented(sourceAction)))) ) .eraseToAnyPublisher() } .receive(on: mainQueue) } .cancellable(id: SynchronizerCancelId, cancelInFlight: true) } } // MARK: Placeholders extension Root.DebugState { public static var initial: Self { .init() } }