fix: stop the device picker hanging on an offer

Two causes. The connect step had no timeout, so an unreachable device was
retried indefinitely instead of falling through to hold-for-later; it now
gives up after 15s and holds the offer as designed.

The picker also waited on the whole exchange, which includes a person on the
other device deciding — up to two minutes. It now closes on tap and reports
the outcome as a message, and a decline or an unanswered offer is shown as
information rather than an error, since the offer did arrive.
This commit is contained in:
2026-08-07 10:49:32 +02:00
parent 677fc3c6d5
commit 225ff9ad22
15 changed files with 139 additions and 8 deletions

View File

@@ -414,6 +414,35 @@ final class ContactsModelTests: XCTestCase {
XCTAssertFalse(delivered)
}
/// A refusal by the person on the other device is information, not an error.
func testADeclinedOfferIsReportedWithoutAnErrorTone() async {
let gateway = FakeCoreGateway()
gateway.offerTransferResult = .failure(
InvitationError.raw("permission error: device did not accept the transfer: receiver-declined")
)
let defaults = UserDefaults(suiteName: "contacts-declined-\(UUID().uuidString)")!
let preferences = AppPreferencesRepository(
defaults: defaults,
fallback: AppPreferencesDefaults(
username: "tester",
receiveFolder: ReceiveFolder(kind: .fileSystemPath, value: "/tmp", displayName: "Downloads"),
themeMode: .system
)
)
let messages = UiMessageController()
let model = ContactsModel(
repository: gateway,
messages: messages,
preferences: preferences,
fileSystemService: FakeFileSystemService()
)
let delivered = await model.offerTransfer(transferId: 7, to: contact("peer"))
XCTAssertFalse(delivered)
XCTAssertEqual(messages.current?.tone, .info)
}
func testUnreachableContactIsSurfacedForRepairing() async {
let gateway = FakeCoreGateway()
gateway.contactsResult = .success([contact("peer", canSend: false)])