SkateSpots/src/lib/nfc.ts
SkateSpots 7d51fc60a5 feat(nfc): native Android tag-writer plugin (APK) + Web NFC on web
Web NFC isn't exposed in Capacitor's WebView, so the APK needs a native writer.
Adds packages/skatespots-nfc — a local Capacitor Android plugin (Kotlin,
enableReaderMode + NDEF URL record), registered BY NAME (not bundled). lib/nfc.ts
now uses the plugin on native and Web NFC on the web. iOS can't write tags.
⚠️ UNVERIFIED (needs cap sync + a device). README documents it.
2026-10-02 14:14:31 +07:00

49 lines
1.8 KiB
TypeScript

/**
* NFC tag writing.
*
* Two paths, one purpose — write ONLY the verify URL (the same pointer the QR
* carries). A tag is passive and clonable, exactly like copying a QR; the trust
* still comes from the signature on the relay.
*
* • Native (Capacitor APK): the local `SkateSpotsNfc` Android plugin, registered
* by name — Web NFC isn't exposed in the WebView.
* • Web (Chrome on Android over HTTPS): Web NFC (`NDEFReader`).
*
* iOS can't write tags either way; reading works everywhere by tapping.
*/
import { Capacitor, registerPlugin } from '@capacitor/core';
interface NativeNfc {
isSupported(): Promise<{ supported: boolean }>;
writeUrl(options: { url: string }): Promise<void>;
cancel(): Promise<void>;
}
const NativeNfc = registerPlugin<NativeNfc>('SkateSpotsNfc');
interface NdefWriter {
write(message: { records: Array<{ recordType: string; data: string }> }): Promise<void>;
}
type NdefCtor = new () => NdefWriter;
function ndefCtor(): NdefCtor | undefined {
if (typeof window === 'undefined') return undefined;
return (window as unknown as { NDEFReader?: NdefCtor }).NDEFReader;
}
/** Whether we can attempt a write here. On native the plugin validates the adapter. */
export function isNfcSupported(): boolean {
if (Capacitor.isNativePlatform()) return true;
return !!ndefCtor();
}
/** Write a URL record to an NFC tag. Rejects if unsupported or the write fails. */
export async function writeNfcUrl(url: string): Promise<void> {
if (Capacitor.isNativePlatform()) {
await NativeNfc.writeUrl({ url });
return;
}
const Ctor = ndefCtor();
if (!Ctor) throw new Error('NFC writing is not supported on this device/browser.');
const writer = new Ctor();
await writer.write({ records: [{ recordType: 'url', data: url }] });
}