Files
vnidrop/apple/VniDrop/Features/Send/TransferShareActions.swift
cdricms ef42875ddb feat(apple): generate type-safe L10n accessors and migrate all key literals
Replaces every stringly-typed localization key in the Apple app with
compile-time-checked accessors generated from localization/strings.json.
A mistyped key is now a build error instead of a silent fallback to the
raw key at runtime. The runtime path is unchanged: plain keys are
String.LocalizationValue constants resolved with String(localized:) and
Apple's String Catalog still does the lookup; keys with arguments become
typed, named functions applying args through String(format:).

Generator: new renderSwiftAccessors emits apple/VniDrop/Generated/L10n.swift,
wired into generate. Renamed generic arg1/arg2 tokens on four keys to
semantic names (receiver, transferName, deviceId) and updated their context
notes; positional output is unchanged so .xcstrings (bar the 4 comments)
and the Android XML regenerate identical.

Migration: every key-carrying value flipped to String.LocalizationValue
end to end, resolved only at the leaf. Zero key literals and zero
LocalizedStringKey remain in app or test code. macOS build passes; iOS
test run pending.
2026-07-23 17:12:43 +02:00

62 lines
2.3 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
let transfer: Transfer
let ticket: String
@State private var actions: TransferShareActions = makePlatformShareActions()
@State private var writingNfc = false
var body: some View {
VStack(spacing: 12) {
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() }
}
}