mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-07 19:29:57 +02:00
Send to a device now sits alongside the QR code, NFC, and export actions, since an offer is another way to deliver the same invitation. Picking a device pushes the existing transfer rather than re-sharing the files. The picker lists only devices holding a live grant, so nothing offered there can fail on tap, and it distinguishes accepted from waiting for that device to open the app. Also fixes the deprecated SF Symbol and the two Sendable warnings introduced with the contacts screen: the sections now talk to the model directly rather than storing view callbacks that a Binding setter has to convert.
76 lines
2.9 KiB
Swift
76 lines
2.9 KiB
Swift
import SwiftUI
|
|
|
|
enum NfcShareAvailability { case available, unavailable, hidden }
|
|
|
|
/// Invitation delivery actions, ported from `TransferShareActions` (iosMain).
|
|
/// Platform implementations perform export, native share, and NFC write.
|
|
@MainActor
|
|
protocol TransferShareActions: AnyObject {
|
|
var canUseNativeShare: Bool { get }
|
|
var nfcAvailability: NfcShareAvailability { get }
|
|
|
|
func exportInvitation(ticket: String, transferName: String, onResult: @escaping (Result<Void, Error>) -> Void)
|
|
func shareInvitation(ticket: String, transferName: String, onResult: @escaping (Result<Void, Error>) -> Void)
|
|
func writeInvitationToNfc(ticket: String, onResult: @escaping (Result<Void, Error>) -> Void)
|
|
func cancelNfcWrite()
|
|
}
|
|
|
|
/// The QR + delivery buttons for a transfer's share panel, ported from the button
|
|
/// stack in `TransferSharePanel` (`TransferDetails.kt`).
|
|
struct ShareActionsView: View {
|
|
@Environment(\.vniColors) private var colors
|
|
@ObservedObject var model: SendModel
|
|
@ObservedObject var contacts: ContactsModel
|
|
let transfer: Transfer
|
|
let ticket: String
|
|
|
|
@State private var actions: TransferShareActions = makePlatformShareActions()
|
|
@State private var writingNfc = false
|
|
@State private var choosingDevice = false
|
|
|
|
var body: some View {
|
|
VStack(spacing: 12) {
|
|
// Sending straight to a remembered device is another way to deliver
|
|
// this same invitation, so it belongs with the other delivery
|
|
// methods rather than in a separate flow.
|
|
if contacts.state.contacts.contains(where: \.canSend) {
|
|
SecondaryButton(
|
|
title: String(localized: L10n.Contacts.sendToDevice),
|
|
action: { choosingDevice = true }
|
|
)
|
|
}
|
|
if actions.nfcAvailability != .hidden {
|
|
SecondaryButton(
|
|
title: writingNfc ? String(localized: L10n.Transfer.nfcWaiting) : String(localized: L10n.Button.writeNfc),
|
|
action: {
|
|
writingNfc = true
|
|
actions.writeInvitationToNfc(ticket: ticket) { result in
|
|
writingNfc = false
|
|
model.onInvitationResult(.nfc, result)
|
|
}
|
|
},
|
|
enabled: actions.nfcAvailability == .available && !writingNfc
|
|
)
|
|
if actions.nfcAvailability == .unavailable {
|
|
Text(String(localized: L10n.Transfer.nfcUnavailable))
|
|
.font(VniType.bodySmall).foregroundStyle(colors.foregroundLighter)
|
|
}
|
|
}
|
|
SecondaryButton(title: String(localized: L10n.Button.downloadInvitation), action: {
|
|
actions.exportInvitation(ticket: ticket, transferName: transfer.transferName ?? "") {
|
|
model.onInvitationResult(.export, $0)
|
|
}
|
|
})
|
|
PrimaryButton(title: String(localized: L10n.Button.nativeShare), action: {
|
|
actions.shareInvitation(ticket: ticket, transferName: transfer.transferName ?? "") {
|
|
model.onInvitationResult(.share, $0)
|
|
}
|
|
}, enabled: actions.canUseNativeShare)
|
|
}
|
|
.onDisappear { actions.cancelNfcWrite() }
|
|
.sheet(isPresented: $choosingDevice) {
|
|
DevicePickerSheet(model: contacts, transferId: transfer.transferId)
|
|
}
|
|
}
|
|
}
|