diff --git a/apple/Tests/ReceiveModelTests.swift b/apple/Tests/ReceiveModelTests.swift index 0a83491..cb4975e 100644 --- a/apple/Tests/ReceiveModelTests.swift +++ b/apple/Tests/ReceiveModelTests.swift @@ -24,6 +24,9 @@ final class ReceiveModelTests: XCTestCase { XCTAssertEqual(model.state.historyDeleteTarget, .transfer(transferId: 5)) model.confirmHistoryDelete() + // Must close immediately (not after the async delete) so the alert can't + // re-present on macOS. + XCTAssertNil(model.state.historyDeleteTarget) await waitUntil { core.deletedTransfers.contains(5) } XCTAssertEqual(core.deletedTransfers, [5]) XCTAssertNil(model.state.historyDeleteTarget) diff --git a/apple/Tests/SendModelTests.swift b/apple/Tests/SendModelTests.swift index 616ae67..12b1801 100644 --- a/apple/Tests/SendModelTests.swift +++ b/apple/Tests/SendModelTests.swift @@ -32,6 +32,9 @@ final class SendModelTests: XCTestCase { XCTAssertTrue(model.state.isDeleteConfirmationOpen) model.confirmDeleteTransfer() + // Must close immediately (not after the async delete) so the alert can't + // re-present on macOS. + XCTAssertFalse(model.state.isDeleteConfirmationOpen) await waitUntil { core.deletedTransfers.contains(3) } XCTAssertEqual(core.deletedTransfers, [3]) XCTAssertNil(model.state.selectedTransferId) diff --git a/apple/VniDrop/Features/Receive/ReceiveModel.swift b/apple/VniDrop/Features/Receive/ReceiveModel.swift index 31c95d2..3927e92 100644 --- a/apple/VniDrop/Features/Receive/ReceiveModel.swift +++ b/apple/VniDrop/Features/Receive/ReceiveModel.swift @@ -121,6 +121,10 @@ final class ReceiveModel: ObservableObject { func confirmHistoryDelete() { guard let target = state.historyDeleteTarget, !state.isDeletingHistory else { return } state.isDeletingHistory = true + // Close synchronously: the alert's dismiss binding runs async and no-ops while + // `isDeletingHistory`, which would otherwise leave the target set and macOS + // re-present it. + state.historyDeleteTarget = nil Task { let result: Result switch target { diff --git a/apple/VniDrop/Features/Send/SendModel.swift b/apple/VniDrop/Features/Send/SendModel.swift index b9d74ed..325fe5c 100644 --- a/apple/VniDrop/Features/Send/SendModel.swift +++ b/apple/VniDrop/Features/Send/SendModel.swift @@ -207,6 +207,9 @@ final class SendModel: ObservableObject { func confirmDeleteTransfer() { guard let transferId = state.selectedTransferId, !state.isDeleting else { return } state.isDeleting = true + // Close synchronously: the alert's dismiss binding runs async and no-ops while + // `isDeleting`, which would otherwise leave the flag true and macOS re-present it. + state.isDeleteConfirmationOpen = false Task { let result = await repository.delete(transferId: transferId) switch result {