mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-07 11:19:58 +02:00
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:
@@ -7,6 +7,8 @@ import SwiftUI
|
||||
/// not part of the send/receive flow.
|
||||
struct ContactsScreen: View {
|
||||
@ObservedObject var model: ContactsModel
|
||||
/// Reports an empty result, which a silent refresh cannot convey.
|
||||
let onNothingWaiting: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
@@ -30,6 +32,24 @@ struct ContactsScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
if !model.state.heldOffers.isEmpty {
|
||||
Section(String(localized: L10n.Contacts.waitingTitle)) {
|
||||
ForEach(model.state.heldOffers) { offer in
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(offer.transferName)
|
||||
Text(String(offer.endpointId.prefix(16)))
|
||||
.font(.caption.monospaced())
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.middle)
|
||||
}
|
||||
}
|
||||
Text(String(localized: L10n.Contacts.waitingHint))
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
if !model.state.blocked.isEmpty {
|
||||
Section(String(localized: L10n.Contacts.blockedTitle)) {
|
||||
ForEach(model.state.blocked, id: \.self) { endpointId in
|
||||
@@ -43,6 +63,18 @@ struct ContactsScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
CollectOffersSection(
|
||||
enabled: model.state.checkForOffersOnOpen,
|
||||
isChecking: model.state.isCheckingForOffers,
|
||||
onToggle: model.setCheckForOffersOnOpen,
|
||||
onCheckNow: {
|
||||
Task {
|
||||
let collected = await model.collectWaitingOffers()
|
||||
if collected == 0 { onNothingWaiting() }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
GrantLifetimeSection(
|
||||
selection: model.state.grantLifetime,
|
||||
onSelect: model.setGrantLifetime
|
||||
@@ -129,6 +161,36 @@ private struct BlockedRow: View {
|
||||
}
|
||||
}
|
||||
|
||||
private struct CollectOffersSection: View {
|
||||
let enabled: Bool
|
||||
let isChecking: Bool
|
||||
let onToggle: (Bool) -> Void
|
||||
let onCheckNow: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Section {
|
||||
Toggle(
|
||||
String(localized: L10n.Contacts.checkOnOpen),
|
||||
isOn: Binding(get: { enabled }, set: onToggle)
|
||||
)
|
||||
Button(action: onCheckNow) {
|
||||
HStack {
|
||||
Text(String(localized: L10n.Contacts.checkNow))
|
||||
if isChecking {
|
||||
Spacer()
|
||||
ProgressView().controlSize(.small)
|
||||
}
|
||||
}
|
||||
}
|
||||
.disabled(isChecking)
|
||||
} footer: {
|
||||
// The privacy cost is the point of the setting, so it is stated
|
||||
// where the switch is, not buried elsewhere.
|
||||
Text(String(localized: L10n.Contacts.checkOnOpenHint))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct GrantLifetimeSection: View {
|
||||
let selection: GrantLifetimeOption
|
||||
let onSelect: (GrantLifetimeOption) -> Void
|
||||
|
||||
Reference in New Issue
Block a user