feat(apple): collect transfers held for this device

Adds the opt-in foreground check and an explicit Check now, the waiting-to-
be-delivered list on the sender side, and honest reporting when a send could
not be delivered: a closed app is a delay, not a success nobody received.

The setting is off by default and its footer states that checking reveals
app-open times to remembered devices, since that is the reason it is a
setting at all.

Records in the design doc that this shipped as one global toggle rather than
the per-contact opt-in originally specified.
This commit is contained in:
2026-08-06 19:00:04 +02:00
parent 94a8ba2103
commit 0f70663263
52 changed files with 492 additions and 21 deletions

View File

@@ -335,7 +335,7 @@ final class CoreRepository: ObservableObject, CoreGateway {
sources: [ShareSource],
transferName: String,
senderName: String
) async -> Result<Share, Error> {
) async -> Result<ContactSendOutcome, Error> {
guard !isNetworkTransitionInProgress else {
return .failure(CoreNetworkLifecycleError.transitionInProgress)
}
@@ -355,10 +355,18 @@ final class CoreRepository: ObservableObject, CoreGateway {
accessMode: .approvalRequired
)
)
return result.toModel()
return ContactSendOutcome(share: result.share.toModel(), delivered: result.delivered)
}
}
func heldOffers() async -> Result<[HeldOfferModel], Error> {
await runCore { try self.requireCore().listHeldOffers().map { $0.toModel() } }
}
func pollContactsForOffers() async -> Result<UInt64, Error> {
await runCore { try self.requireCore().pollContactsForOffers() }
}
func forgetContact(endpointId: String) async -> Result<Void, Error> {
await runCore { try self.requireCore().forgetContact(endpointId: endpointId) }
}
@@ -657,6 +665,20 @@ extension IncomingOffer {
}
}
extension HeldOfferSummary {
func toModel() -> HeldOfferModel {
HeldOfferModel(
offerId: offerId,
endpointId: endpointId,
transferId: transferId,
transferName: transferName,
fileCount: fileCount,
totalBytes: totalBytes,
createdAt: createdAt
)
}
}
extension GrantLifetimeOption {
func toNative() -> GrantLifetimeSetting {
switch self {