Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 0 additions & 23 deletions ios/Cove/CoinControlManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ private enum CoinControlManagerError: LocalizedError {
var lockStateLoadFailed: Bool
var unit: Unit = .sat

private var updateSendFlowManagerTask: Task<Void, Never>? = nil

private var rust: RustCoinControlManager? {
rustState.withLock { $0.rust }
}
Expand Down Expand Up @@ -93,8 +91,6 @@ private enum CoinControlManagerError: LocalizedError {
guard takeRustForClose() != nil else { return }

logger.debug("Closing CoinControlManager")
updateSendFlowManagerTask?.cancel()
updateSendFlowManagerTask = nil
}

private func takeRustForClose() -> RustCoinControlManager? {
Expand Down Expand Up @@ -149,24 +145,6 @@ private enum CoinControlManagerError: LocalizedError {
Int(self.totalSelected.asSats())
}

public func continuePressed() {
guard let sfm = AppManager.shared.sendFlowManager else { return }
self.updateSendFlowManagerTask?.cancel()
self.updateSendFlowManagerTask = nil

sfm.dispatch(.setCoinControlMode(selectedUtxos()))
}

private func updateSendFlowManager() {
guard let sfm = AppManager.shared.sendFlowManager else { return }
self.updateSendFlowManagerTask?.cancel()
self.updateSendFlowManagerTask = Task {
try? await Task.sleep(for: .milliseconds(100))
guard !Task.isCancelled else { return }
sfm.dispatch(.setCoinControlMode(selectedUtxos()))
}
}

var canApplyReconcileMessages: Bool {
rust != nil
}
Expand All @@ -182,7 +160,6 @@ private enum CoinControlManagerError: LocalizedError {
case let .updateSearch(search):
withAnimation { self.search = search }
case let .updateSelectedUtxos(utxos: selected, totalSelected):
updateSendFlowManager()
withAnimation {
self.selection = SelectionState(
selected: Set(selected),
Expand Down
1 change: 1 addition & 0 deletions ios/Cove/Flows/CoinControlFlow/CoinControlContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ private struct CoinControlLoadedView: View {
guard !Task.isCancelled else { return }

let manager = CoinControlManager(rustManager)

self.manager = manager
app.setCoinControlManager(manager)
} catch {
Expand Down
1 change: 0 additions & 1 deletion ios/Cove/Flows/CoinControlFlow/UtxoListScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ struct UtxoListScreen: View {
}

private func continueToSend() {
manager.continuePressed()
navigate(
RouteFactory()
.coinControlSend(
Expand Down
1 change: 1 addition & 0 deletions ios/Cove/Flows/CoinControlFlow/UtxoRowPreview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct UtxoRowPreview: View {
#Preview("UTXORowPreview") {
AsyncPreview {
let manager = CoinControlManager(RustCoinControlManager.previewNew())

UtxoRowPreview(displayAmount: manager.displayAmount, utxo: manager.utxos[0])
.environment(WalletManager(preview: .only))
}
Expand Down
32 changes: 12 additions & 20 deletions ios/Cove/Flows/SendFlow/SendFlowCoinControlSetAmountScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -159,30 +159,23 @@ struct SendFlowCoinControlSetAmountScreen: View {

private func prepareScreen() async {
let isAlreadyValid = validate()
let shouldShowLoading = !isAlreadyValid || utxos == sendFlowManager.utxos()

if shouldShowLoading {
Task {
await MainActor.run {
withAnimation(
.easeInOut(duration: 1.5).delay(0.4),
completionCriteria: .removed
) {
loadingOpacity = 0
} completion: {
isLoading = false
if validate() { presenter.focusField = .none }
}

Task {
await MainActor.run {
withAnimation(
.easeInOut(duration: 1.5).delay(0.4),
completionCriteria: .removed
) {
loadingOpacity = 0
} completion: {
isLoading = false
if validate() { presenter.focusField = .none }
}
}
} else {
presenter.focusField = .none
}

// HACK: Bug in SwiftUI where keyboard toolbar is broken
if shouldShowLoading {
try? await Task.sleep(for: .milliseconds(700))
}
try? await Task.sleep(for: .milliseconds(700))

await MainActor.run {
if !isAlreadyValid { presenter.focusField = .address }
Expand All @@ -194,7 +187,6 @@ struct SendFlowCoinControlSetAmountScreen: View {
}

private func screenAppeared() {
sendFlowManager.dispatch(.setCoinControlMode(utxos))
if validate(), utxos == sendFlowManager.utxos() {
isLoading = false
loadingOpacity = 0
Expand Down
7 changes: 5 additions & 2 deletions ios/Cove/Flows/SendFlow/SendFlowContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,13 @@ private struct SendFlowLoadedView: View {
)
}

private func applyRouteArguments(to sendFlowManager: SendFlowManager) {
private func applyRouteArguments(to sendFlowManager: SendFlowManager) async {
switch sendRoute {
case let .setAmount(id: _, address: address, amount: amount):
if let address { sendFlowManager.setAddress(address) }
if let amount { sendFlowManager.setAmount(amount) }
case let .coinControlSetAmount(id: _, utxos: utxos):
await sendFlowManager.prepareCoinControl(utxos: utxos)
default:
()
}
Expand All @@ -99,7 +101,8 @@ private struct SendFlowLoadedView: View {
let sendFlowManagerId = ObjectIdentifier(sendFlowManager)

initializedSendFlowManagerId = nil
applyRouteArguments(to: sendFlowManager)
await applyRouteArguments(to: sendFlowManager)
guard !Task.isCancelled else { return }

// rust handles alert + popRoute on failure
if await sendFlowManager.waitForInit() {
Expand Down
16 changes: 16 additions & 0 deletions ios/Cove/Flows/SendFlow/SendFlowManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,22 @@ private enum SendFlowManagerAccessError: LocalizedError {
return await rust.waitForInit()
}

/// Applies the route-owned coin selection before the send screen becomes visible
func prepareCoinControl(utxos: [Utxo]) async {
await dispatchAndWait(.setCoinControlMode(utxos))
}

/// Waits until Rust synchronously handles an action, but not for tasks that the action starts
private func dispatchAndWait(_ action: Action) async {
dispatch(action)

await withCheckedContinuation { continuation in
rustBridge.async {
continuation.resume()
}
}
}

func amountExceedsBalance() -> Bool {
withRustOr(false) { $0.amountExceedsBalance() }
}
Expand Down
34 changes: 33 additions & 1 deletion ios/CoveTests/WalletTransitionTests.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@testable import Cove
import CoveCore
import os
import XCTest

final class WalletTransitionTests: XCTestCase {
Expand Down Expand Up @@ -424,6 +425,30 @@ final class WalletTransitionTests: XCTestCase {
}
}

@MainActor
func testPreparingCoinControlDispatchesRouteUtxosBeforeReturning() async {
let walletManager = WalletManager(preview: .only)
defer { walletManager.close() }

let rustManager = TestSendFlowRustManager(walletId: walletManager.id)
let presenter = SendFlowPresenter(
routing: TestSendFlowRouting(),
manager: walletManager
)
let manager = SendFlowManager(rustManager, presenter: presenter)
defer { manager.close() }

let utxos = RustCoinControlManager.previewNew(outputCount: 2, changeCount: 0).utxos()
await manager.prepareCoinControl(utxos: utxos)

guard case let .setCoinControlMode(dispatchedUtxos)? = rustManager.actions.last else {
XCTFail("expected coin-control route preparation")
return
}

XCTAssertEqual(dispatchedUtxos, utxos)
}

@MainActor
func testWalletCacheClearInvalidatesMatchingRetainedSendFlowManagers() throws {
let (walletManager, retainedManager) = try makeSendFlowManager()
Expand Down Expand Up @@ -474,6 +499,11 @@ private final class TestSendFlowRouting: SendFlowRouting {

private final class TestSendFlowRustManager: SendFlowRustManaging {
private let id: WalletId
private let dispatchedActions = OSAllocatedUnfairLock(initialState: [SendFlowManagerAction]())

var actions: [SendFlowManagerAction] {
dispatchedActions.withLock { $0 }
}

init(walletId: WalletId) {
self.id = walletId
Expand Down Expand Up @@ -504,7 +534,9 @@ private final class TestSendFlowRustManager: SendFlowRustManaging {
func utxos() -> [Utxo]? { nil }
func maxSendMinusFees() -> Amount? { nil }
func maxSendMinusFeesAndSmallUtxo() -> Amount? { nil }
func dispatch(action _: SendFlowManagerAction) {}
func dispatch(action: SendFlowManagerAction) {
dispatchedActions.withLock { $0.append(action) }
}
}

@MainActor
Expand Down
Loading