From c271efbc953994564f9386c9c6a4b78c083064b4 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 16 Sep 2024 17:11:12 +0200 Subject: [PATCH 1/3] Check for p2plinks on Ledger factor source access --- .../FactorSourceAccess+View.swift | 22 +++++ .../FactorSourceAccess.swift | 99 ++++++++++++++++++- 2 files changed, 117 insertions(+), 4 deletions(-) diff --git a/RadixWallet/Features/FactorSourceAccess/FactorSourceAccess+View.swift b/RadixWallet/Features/FactorSourceAccess/FactorSourceAccess+View.swift index e1b66ecb80..d0a6dc2778 100644 --- a/RadixWallet/Features/FactorSourceAccess/FactorSourceAccess+View.swift +++ b/RadixWallet/Features/FactorSourceAccess/FactorSourceAccess+View.swift @@ -30,6 +30,7 @@ public extension FactorSourceAccess { .onFirstTask { @MainActor in await store.send(.view(.onFirstTask)).finish() } + .destinations(with: store) } } @@ -86,3 +87,24 @@ public extension FactorSourceAccess { } } } + +private extension StoreOf { + var destination: PresentationStoreOf { + func scopeState(state: State) -> PresentationState { + state.$destination + } + return scope(state: scopeState, action: Action.destination) + } +} + +@MainActor +private extension View { + func destinations(with store: StoreOf) -> some View { + let destinationStore = store.destination + return noP2PLinkAlert(with: destinationStore) + } + + private func noP2PLinkAlert(with destinationStore: PresentationStoreOf) -> some View { + alert(store: destinationStore.scope(state: \.noP2PLink, action: \.noP2PLink)) + } +} diff --git a/RadixWallet/Features/FactorSourceAccess/FactorSourceAccess.swift b/RadixWallet/Features/FactorSourceAccess/FactorSourceAccess.swift index 532bd1e785..3fcff5a44b 100644 --- a/RadixWallet/Features/FactorSourceAccess/FactorSourceAccess.swift +++ b/RadixWallet/Features/FactorSourceAccess/FactorSourceAccess.swift @@ -4,33 +4,110 @@ public struct FactorSourceAccess: Sendable, FeatureReducer { public let kind: Kind public let purpose: Purpose + @PresentationState + public var destination: Destination.State? = nil + public init(kind: Kind, purpose: Purpose) { self.kind = kind self.purpose = purpose } } - public enum ViewAction: Sendable, Equatable { + public enum ViewAction: Sendable, Hashable { case onFirstTask case retryButtonTapped case closeButtonTapped } - public enum DelegateAction: Sendable, Equatable { + public enum InternalAction: Sendable, Hashable { + case isConnectedToAnyConnectorExtension(Bool) + } + + public enum DelegateAction: Sendable, Hashable { case perform case cancel } + public struct Destination: DestinationReducer { + @CasePathable + public enum State: Sendable, Hashable { + case noP2PLink(AlertState) + } + + @CasePathable + public enum Action: Sendable, Hashable { + case noP2PLink(NoP2PLinkAlert) + } + + public var body: some ReducerOf { + EmptyReducer() + } + + public enum NoP2PLinkAlert: Sendable, Hashable { + case okTapped + } + } + + @Dependency(\.ledgerHardwareWalletClient) var ledgerHardwareWalletClient + + public var body: some ReducerOf { + Reduce(core) + .ifLet(destinationPath, action: /Action.destination) { + Destination() + } + } + public init() {} - public func reduce(into _: inout State, viewAction: ViewAction) -> Effect { + private let destinationPath: WritableKeyPath> = \.$destination + + public func reduce(into state: inout State, viewAction: ViewAction) -> Effect { switch viewAction { - case .onFirstTask, .retryButtonTapped: + case .onFirstTask: + .send(.delegate(.perform)) + .merge(with: checkP2PLinksEffect(state: state)) + case .retryButtonTapped: .send(.delegate(.perform)) case .closeButtonTapped: .send(.delegate(.cancel)) } } + + public func reduce(into state: inout State, internalAction: InternalAction) -> Effect { + switch internalAction { + case let .isConnectedToAnyConnectorExtension(isConnected): + if !isConnected { + state.destination = .noP2PLink(.noP2Plink) + } + return .none + } + } + + public func reduce(into state: inout State, presentedAction: Destination.Action) -> Effect { + switch presentedAction { + case .noP2PLink(.okTapped): + state.destination = nil + return .run { send in + // Dispatching this in an async process is enough for it to take place after alert has been dismissed. + // No need to place an actual delay with continuousClock. + await send(.delegate(.cancel)) + } + } + } + + private func checkP2PLinksEffect(state: State) -> Effect { + guard case .ledger = state.kind else { + return .none + } + return .run { send in + for try await isConnected in await ledgerHardwareWalletClient.isConnectedToAnyConnectorExtension() { + guard !Task.isCancelled else { return } + await send(.internal(.isConnectedToAnyConnectorExtension(isConnected))) + } + } catch: { error, _ in + loggerGlobal.error("failed to get links updates, error: \(error)") + } + } } extension FactorSourceAccess.State { @@ -62,3 +139,17 @@ extension FactorSourceAccess.State { case createKey } } + +private extension AlertState { + static var noP2Plink: AlertState { + AlertState { + TextState(L10n.LedgerHardwareDevices.LinkConnectorAlert.title) + } actions: { + ButtonState(action: .okTapped) { + TextState(L10n.Common.ok) + } + } message: { + TextState(L10n.LedgerHardwareDevices.LinkConnectorAlert.message) + } + } +} From 72349dd4064afdd35c253895b52a3d49ffc9c86e Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Mon, 16 Sep 2024 17:34:53 +0200 Subject: [PATCH 2/3] set first ledger --- .../Features/Signing/Children/SignWithFactorSource.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RadixWallet/Features/Signing/Children/SignWithFactorSource.swift b/RadixWallet/Features/Signing/Children/SignWithFactorSource.swift index 1cff57cae6..2335207093 100644 --- a/RadixWallet/Features/Signing/Children/SignWithFactorSource.swift +++ b/RadixWallet/Features/Signing/Children/SignWithFactorSource.swift @@ -24,7 +24,8 @@ public struct SignWithFactorSource: Sendable, FeatureReducer { self.factorSourceAccess = .init(kind: .device, purpose: .signature) case .ledger: assert(signingFactors.allSatisfy { $0.factorSource.kind == LedgerHardwareWalletFactorSource.kind }) - self.factorSourceAccess = .init(kind: .ledger(nil), purpose: .signature) + let ledger: LedgerHardwareWalletFactorSource? = signingFactors.first?.factorSource.extract() + self.factorSourceAccess = .init(kind: .ledger(ledger), purpose: .signature) } } } From 4573e47eff97150687d55dd055b2b37661e0c1d7 Mon Sep 17 00:00:00 2001 From: Matias Bzurovski Date: Thu, 19 Sep 2024 11:42:28 +0200 Subject: [PATCH 3/3] check if there are p2p links (ignroing if they are connected or not) --- .../P2PLinksClient+Interface.swift | 6 ++++++ .../FactorSourceAccess/FactorSourceAccess.swift | 16 +++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/RadixWallet/Clients/P2PLinksClient/P2PLinksClient+Interface.swift b/RadixWallet/Clients/P2PLinksClient/P2PLinksClient+Interface.swift index 294e464019..dce0fd99a0 100644 --- a/RadixWallet/Clients/P2PLinksClient/P2PLinksClient+Interface.swift +++ b/RadixWallet/Clients/P2PLinksClient/P2PLinksClient+Interface.swift @@ -32,3 +32,9 @@ extension P2PLinksClient { public typealias GetP2PLinkPrivateKey = @Sendable () async throws -> (privateKey: Curve25519.PrivateKey, isNew: Bool) public typealias StoreP2PLinkPrivateKey = @Sendable (Curve25519.PrivateKey) async throws -> Void } + +extension P2PLinksClient { + func hasP2PLinks() async -> Bool { + await !getP2PLinks().isEmpty + } +} diff --git a/RadixWallet/Features/FactorSourceAccess/FactorSourceAccess.swift b/RadixWallet/Features/FactorSourceAccess/FactorSourceAccess.swift index 3fcff5a44b..ca04a4ddc0 100644 --- a/RadixWallet/Features/FactorSourceAccess/FactorSourceAccess.swift +++ b/RadixWallet/Features/FactorSourceAccess/FactorSourceAccess.swift @@ -20,7 +20,7 @@ public struct FactorSourceAccess: Sendable, FeatureReducer { } public enum InternalAction: Sendable, Hashable { - case isConnectedToAnyConnectorExtension(Bool) + case hasP2PLinks(Bool) } public enum DelegateAction: Sendable, Hashable { @@ -48,7 +48,7 @@ public struct FactorSourceAccess: Sendable, FeatureReducer { } } - @Dependency(\.ledgerHardwareWalletClient) var ledgerHardwareWalletClient + @Dependency(\.p2pLinksClient) var p2pLinksClient public var body: some ReducerOf { Reduce(core) @@ -75,8 +75,8 @@ public struct FactorSourceAccess: Sendable, FeatureReducer { public func reduce(into state: inout State, internalAction: InternalAction) -> Effect { switch internalAction { - case let .isConnectedToAnyConnectorExtension(isConnected): - if !isConnected { + case let .hasP2PLinks(hasP2PLinks): + if !hasP2PLinks { state.destination = .noP2PLink(.noP2Plink) } return .none @@ -100,12 +100,10 @@ public struct FactorSourceAccess: Sendable, FeatureReducer { return .none } return .run { send in - for try await isConnected in await ledgerHardwareWalletClient.isConnectedToAnyConnectorExtension() { - guard !Task.isCancelled else { return } - await send(.internal(.isConnectedToAnyConnectorExtension(isConnected))) - } + let result = await p2pLinksClient.hasP2PLinks() + await send(.internal(.hasP2PLinks(result))) } catch: { error, _ in - loggerGlobal.error("failed to get links updates, error: \(error)") + loggerGlobal.error("failed to check if has p2p links, error: \(error)") } } }