@spencerls/react-native-nfc 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +46 -0
- package/dist/index.d.mts +133 -0
- package/dist/index.d.ts +133 -0
- package/dist/index.js +292 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +257 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Spencer Smith
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# @spencer/nfc
|
|
2
|
+
|
|
3
|
+
A clean, easy, React-friendly NFC service built on `react-native-nfc-manager`.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @spencer/nfc
|
|
9
|
+
# or
|
|
10
|
+
yarn add @spencer/nfc
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Requires:
|
|
14
|
+
|
|
15
|
+
`react-native-nfc-manager`
|
|
16
|
+
|
|
17
|
+
React Native 0.74+ or Expo (Bare / Prebuild)
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { nfcService, useNfcState, NfcUtils } from "@spencer/nfc";
|
|
23
|
+
|
|
24
|
+
export default function Example() {
|
|
25
|
+
const { state, isWriting } = useNfcState();
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
nfcService.startReader(
|
|
29
|
+
NfcAdapter.FLAG_READER_NFC_V | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
|
|
30
|
+
async (tag) => {
|
|
31
|
+
console.log("Tag:", tag);
|
|
32
|
+
await nfcService.stopReader();
|
|
33
|
+
await nfcService.writeNdef([NfcUtils.textRecord("Hello NFC!")]);
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return () => nfcService.stopReader();
|
|
38
|
+
}, []);
|
|
39
|
+
|
|
40
|
+
return <Text>NFC state: {state}</Text>;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## License
|
|
45
|
+
|
|
46
|
+
MIT © Spencer Smith
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import * as react_native_nfc_manager from 'react-native-nfc-manager';
|
|
2
|
+
import { TagEvent, NdefRecord } from 'react-native-nfc-manager';
|
|
3
|
+
|
|
4
|
+
type NfcState = "idle" | "starting" | "active" | "stopping" | "writing";
|
|
5
|
+
type NfcListener = (state: NfcState) => void;
|
|
6
|
+
declare class NfcService {
|
|
7
|
+
private state;
|
|
8
|
+
private lastTag;
|
|
9
|
+
private listeners;
|
|
10
|
+
constructor();
|
|
11
|
+
private setState;
|
|
12
|
+
getState(): NfcState;
|
|
13
|
+
getLastTag(): TagEvent | null;
|
|
14
|
+
subscribe(fn: NfcListener): () => void;
|
|
15
|
+
startReader(readerModeFlags: number, onTag?: (tag: TagEvent) => void): Promise<void>;
|
|
16
|
+
stopReader(): Promise<void>;
|
|
17
|
+
writeNdef(records: NdefRecord[]): Promise<{
|
|
18
|
+
success: boolean;
|
|
19
|
+
error: undefined;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
declare const nfcService: NfcService;
|
|
23
|
+
|
|
24
|
+
declare function decodeRecord(record: {
|
|
25
|
+
tnf: number;
|
|
26
|
+
type: number[] | string;
|
|
27
|
+
id?: number[];
|
|
28
|
+
payload: number[];
|
|
29
|
+
}): {
|
|
30
|
+
kind: string;
|
|
31
|
+
text: string;
|
|
32
|
+
lang: string;
|
|
33
|
+
uri?: undefined;
|
|
34
|
+
mimeType?: undefined;
|
|
35
|
+
data?: undefined;
|
|
36
|
+
type?: undefined;
|
|
37
|
+
tnf?: undefined;
|
|
38
|
+
payload?: undefined;
|
|
39
|
+
} | {
|
|
40
|
+
kind: string;
|
|
41
|
+
uri: string;
|
|
42
|
+
text?: undefined;
|
|
43
|
+
lang?: undefined;
|
|
44
|
+
mimeType?: undefined;
|
|
45
|
+
data?: undefined;
|
|
46
|
+
type?: undefined;
|
|
47
|
+
tnf?: undefined;
|
|
48
|
+
payload?: undefined;
|
|
49
|
+
} | {
|
|
50
|
+
kind: string;
|
|
51
|
+
mimeType: string;
|
|
52
|
+
data: any;
|
|
53
|
+
text?: undefined;
|
|
54
|
+
lang?: undefined;
|
|
55
|
+
uri?: undefined;
|
|
56
|
+
type?: undefined;
|
|
57
|
+
tnf?: undefined;
|
|
58
|
+
payload?: undefined;
|
|
59
|
+
} | {
|
|
60
|
+
kind: string;
|
|
61
|
+
mimeType: string;
|
|
62
|
+
data: Uint8Array<ArrayBufferLike>;
|
|
63
|
+
text: string | null;
|
|
64
|
+
lang?: undefined;
|
|
65
|
+
uri?: undefined;
|
|
66
|
+
type?: undefined;
|
|
67
|
+
tnf?: undefined;
|
|
68
|
+
payload?: undefined;
|
|
69
|
+
} | {
|
|
70
|
+
kind: string;
|
|
71
|
+
uri: string;
|
|
72
|
+
data: Uint8Array<ArrayBufferLike>;
|
|
73
|
+
text?: undefined;
|
|
74
|
+
lang?: undefined;
|
|
75
|
+
mimeType?: undefined;
|
|
76
|
+
type?: undefined;
|
|
77
|
+
tnf?: undefined;
|
|
78
|
+
payload?: undefined;
|
|
79
|
+
} | {
|
|
80
|
+
kind: string;
|
|
81
|
+
type: string;
|
|
82
|
+
data: Uint8Array<ArrayBufferLike>;
|
|
83
|
+
text?: undefined;
|
|
84
|
+
lang?: undefined;
|
|
85
|
+
uri?: undefined;
|
|
86
|
+
mimeType?: undefined;
|
|
87
|
+
tnf?: undefined;
|
|
88
|
+
payload?: undefined;
|
|
89
|
+
} | {
|
|
90
|
+
kind: string;
|
|
91
|
+
tnf: number;
|
|
92
|
+
type: string;
|
|
93
|
+
payload: Uint8Array<ArrayBufferLike>;
|
|
94
|
+
text?: undefined;
|
|
95
|
+
lang?: undefined;
|
|
96
|
+
uri?: undefined;
|
|
97
|
+
mimeType?: undefined;
|
|
98
|
+
data?: undefined;
|
|
99
|
+
};
|
|
100
|
+
declare function decodeJson(record: NdefRecord): any;
|
|
101
|
+
declare function encodeJson(obj: any): {
|
|
102
|
+
tnf: 2;
|
|
103
|
+
type: number[];
|
|
104
|
+
id: never[];
|
|
105
|
+
payload: Uint8Array<ArrayBuffer>;
|
|
106
|
+
};
|
|
107
|
+
declare function encodeMime(mimeType: string, data: Uint8Array): {
|
|
108
|
+
tnf: 2;
|
|
109
|
+
type: number[];
|
|
110
|
+
id: never[];
|
|
111
|
+
payload: Uint8Array<ArrayBufferLike>;
|
|
112
|
+
};
|
|
113
|
+
declare function willFit(tag: TagEvent, records: NdefRecord[]): boolean | null;
|
|
114
|
+
declare function spaceLeft(tag: TagEvent, records: NdefRecord[]): number | null;
|
|
115
|
+
declare const NfcUtils: {
|
|
116
|
+
Ndef: {
|
|
117
|
+
decodeRecord: typeof decodeRecord;
|
|
118
|
+
decodeJson: typeof decodeJson;
|
|
119
|
+
encodeText: (text: string, lang?: string) => NdefRecord;
|
|
120
|
+
encodeUri: (uri: string) => NdefRecord;
|
|
121
|
+
encodeJson: typeof encodeJson;
|
|
122
|
+
encodeMime: typeof encodeMime;
|
|
123
|
+
willFit: typeof willFit;
|
|
124
|
+
spaceLeft: typeof spaceLeft;
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
declare function useNfcState(): {
|
|
129
|
+
state: NfcState;
|
|
130
|
+
lastTag: react_native_nfc_manager.TagEvent | null;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export { type NfcListener, type NfcState, NfcUtils, nfcService, useNfcState };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import * as react_native_nfc_manager from 'react-native-nfc-manager';
|
|
2
|
+
import { TagEvent, NdefRecord } from 'react-native-nfc-manager';
|
|
3
|
+
|
|
4
|
+
type NfcState = "idle" | "starting" | "active" | "stopping" | "writing";
|
|
5
|
+
type NfcListener = (state: NfcState) => void;
|
|
6
|
+
declare class NfcService {
|
|
7
|
+
private state;
|
|
8
|
+
private lastTag;
|
|
9
|
+
private listeners;
|
|
10
|
+
constructor();
|
|
11
|
+
private setState;
|
|
12
|
+
getState(): NfcState;
|
|
13
|
+
getLastTag(): TagEvent | null;
|
|
14
|
+
subscribe(fn: NfcListener): () => void;
|
|
15
|
+
startReader(readerModeFlags: number, onTag?: (tag: TagEvent) => void): Promise<void>;
|
|
16
|
+
stopReader(): Promise<void>;
|
|
17
|
+
writeNdef(records: NdefRecord[]): Promise<{
|
|
18
|
+
success: boolean;
|
|
19
|
+
error: undefined;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
declare const nfcService: NfcService;
|
|
23
|
+
|
|
24
|
+
declare function decodeRecord(record: {
|
|
25
|
+
tnf: number;
|
|
26
|
+
type: number[] | string;
|
|
27
|
+
id?: number[];
|
|
28
|
+
payload: number[];
|
|
29
|
+
}): {
|
|
30
|
+
kind: string;
|
|
31
|
+
text: string;
|
|
32
|
+
lang: string;
|
|
33
|
+
uri?: undefined;
|
|
34
|
+
mimeType?: undefined;
|
|
35
|
+
data?: undefined;
|
|
36
|
+
type?: undefined;
|
|
37
|
+
tnf?: undefined;
|
|
38
|
+
payload?: undefined;
|
|
39
|
+
} | {
|
|
40
|
+
kind: string;
|
|
41
|
+
uri: string;
|
|
42
|
+
text?: undefined;
|
|
43
|
+
lang?: undefined;
|
|
44
|
+
mimeType?: undefined;
|
|
45
|
+
data?: undefined;
|
|
46
|
+
type?: undefined;
|
|
47
|
+
tnf?: undefined;
|
|
48
|
+
payload?: undefined;
|
|
49
|
+
} | {
|
|
50
|
+
kind: string;
|
|
51
|
+
mimeType: string;
|
|
52
|
+
data: any;
|
|
53
|
+
text?: undefined;
|
|
54
|
+
lang?: undefined;
|
|
55
|
+
uri?: undefined;
|
|
56
|
+
type?: undefined;
|
|
57
|
+
tnf?: undefined;
|
|
58
|
+
payload?: undefined;
|
|
59
|
+
} | {
|
|
60
|
+
kind: string;
|
|
61
|
+
mimeType: string;
|
|
62
|
+
data: Uint8Array<ArrayBufferLike>;
|
|
63
|
+
text: string | null;
|
|
64
|
+
lang?: undefined;
|
|
65
|
+
uri?: undefined;
|
|
66
|
+
type?: undefined;
|
|
67
|
+
tnf?: undefined;
|
|
68
|
+
payload?: undefined;
|
|
69
|
+
} | {
|
|
70
|
+
kind: string;
|
|
71
|
+
uri: string;
|
|
72
|
+
data: Uint8Array<ArrayBufferLike>;
|
|
73
|
+
text?: undefined;
|
|
74
|
+
lang?: undefined;
|
|
75
|
+
mimeType?: undefined;
|
|
76
|
+
type?: undefined;
|
|
77
|
+
tnf?: undefined;
|
|
78
|
+
payload?: undefined;
|
|
79
|
+
} | {
|
|
80
|
+
kind: string;
|
|
81
|
+
type: string;
|
|
82
|
+
data: Uint8Array<ArrayBufferLike>;
|
|
83
|
+
text?: undefined;
|
|
84
|
+
lang?: undefined;
|
|
85
|
+
uri?: undefined;
|
|
86
|
+
mimeType?: undefined;
|
|
87
|
+
tnf?: undefined;
|
|
88
|
+
payload?: undefined;
|
|
89
|
+
} | {
|
|
90
|
+
kind: string;
|
|
91
|
+
tnf: number;
|
|
92
|
+
type: string;
|
|
93
|
+
payload: Uint8Array<ArrayBufferLike>;
|
|
94
|
+
text?: undefined;
|
|
95
|
+
lang?: undefined;
|
|
96
|
+
uri?: undefined;
|
|
97
|
+
mimeType?: undefined;
|
|
98
|
+
data?: undefined;
|
|
99
|
+
};
|
|
100
|
+
declare function decodeJson(record: NdefRecord): any;
|
|
101
|
+
declare function encodeJson(obj: any): {
|
|
102
|
+
tnf: 2;
|
|
103
|
+
type: number[];
|
|
104
|
+
id: never[];
|
|
105
|
+
payload: Uint8Array<ArrayBuffer>;
|
|
106
|
+
};
|
|
107
|
+
declare function encodeMime(mimeType: string, data: Uint8Array): {
|
|
108
|
+
tnf: 2;
|
|
109
|
+
type: number[];
|
|
110
|
+
id: never[];
|
|
111
|
+
payload: Uint8Array<ArrayBufferLike>;
|
|
112
|
+
};
|
|
113
|
+
declare function willFit(tag: TagEvent, records: NdefRecord[]): boolean | null;
|
|
114
|
+
declare function spaceLeft(tag: TagEvent, records: NdefRecord[]): number | null;
|
|
115
|
+
declare const NfcUtils: {
|
|
116
|
+
Ndef: {
|
|
117
|
+
decodeRecord: typeof decodeRecord;
|
|
118
|
+
decodeJson: typeof decodeJson;
|
|
119
|
+
encodeText: (text: string, lang?: string) => NdefRecord;
|
|
120
|
+
encodeUri: (uri: string) => NdefRecord;
|
|
121
|
+
encodeJson: typeof encodeJson;
|
|
122
|
+
encodeMime: typeof encodeMime;
|
|
123
|
+
willFit: typeof willFit;
|
|
124
|
+
spaceLeft: typeof spaceLeft;
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
declare function useNfcState(): {
|
|
129
|
+
state: NfcState;
|
|
130
|
+
lastTag: react_native_nfc_manager.TagEvent | null;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export { type NfcListener, type NfcState, NfcUtils, nfcService, useNfcState };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/services/nfc/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
NfcUtils: () => NfcUtils,
|
|
34
|
+
nfcService: () => nfcService,
|
|
35
|
+
useNfcState: () => useNfcState
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(index_exports);
|
|
38
|
+
|
|
39
|
+
// src/services/nfc/NfcService.ts
|
|
40
|
+
var import_react_native = require("react-native");
|
|
41
|
+
var import_react_native_nfc_manager = __toESM(require("react-native-nfc-manager"));
|
|
42
|
+
var NfcService = class {
|
|
43
|
+
constructor() {
|
|
44
|
+
this.state = "idle";
|
|
45
|
+
this.lastTag = null;
|
|
46
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
47
|
+
import_react_native_nfc_manager.default.start();
|
|
48
|
+
}
|
|
49
|
+
// --- internal state mgmt ---
|
|
50
|
+
setState(next) {
|
|
51
|
+
if (this.state !== next) {
|
|
52
|
+
this.state = next;
|
|
53
|
+
for (const listener of this.listeners) listener(next);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
getState() {
|
|
57
|
+
return this.state;
|
|
58
|
+
}
|
|
59
|
+
getLastTag() {
|
|
60
|
+
return this.lastTag;
|
|
61
|
+
}
|
|
62
|
+
subscribe(fn) {
|
|
63
|
+
this.listeners.add(fn);
|
|
64
|
+
fn(this.state);
|
|
65
|
+
return () => {
|
|
66
|
+
this.listeners.delete(fn);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// --- Reader lifecycle ---
|
|
70
|
+
async startReader(readerModeFlags, onTag) {
|
|
71
|
+
if (this.state !== "idle") {
|
|
72
|
+
console.warn(`[NFC] Cannot start reader while ${this.state}`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
this.setState("starting");
|
|
76
|
+
import_react_native_nfc_manager.default.setEventListener(import_react_native_nfc_manager.NfcEvents.DiscoverTag, (tag) => {
|
|
77
|
+
this.lastTag = tag;
|
|
78
|
+
onTag == null ? void 0 : onTag(tag);
|
|
79
|
+
});
|
|
80
|
+
try {
|
|
81
|
+
await import_react_native_nfc_manager.default.registerTagEvent({
|
|
82
|
+
isReaderModeEnabled: true,
|
|
83
|
+
readerModeFlags
|
|
84
|
+
});
|
|
85
|
+
if (this.state === "starting") {
|
|
86
|
+
this.setState("active");
|
|
87
|
+
}
|
|
88
|
+
} catch (err) {
|
|
89
|
+
console.warn("[NFC] startReader error:", err);
|
|
90
|
+
this.setState("idle");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async stopReader() {
|
|
94
|
+
if (["idle", "stopping"].includes(this.state)) return;
|
|
95
|
+
this.setState("stopping");
|
|
96
|
+
try {
|
|
97
|
+
await import_react_native_nfc_manager.default.unregisterTagEvent();
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
this.setState("idle");
|
|
101
|
+
}
|
|
102
|
+
// --- Writer ---
|
|
103
|
+
async writeNdef(records) {
|
|
104
|
+
if (this.state !== "idle") {
|
|
105
|
+
throw new Error(`Cannot write while reader is ${this.state}`);
|
|
106
|
+
}
|
|
107
|
+
this.setState("writing");
|
|
108
|
+
let res = {
|
|
109
|
+
success: false,
|
|
110
|
+
error: void 0
|
|
111
|
+
};
|
|
112
|
+
try {
|
|
113
|
+
await import_react_native_nfc_manager.default.requestTechnology(import_react_native_nfc_manager.NfcTech.Ndef, {
|
|
114
|
+
alertMessage: "Hold near NFC tag to write"
|
|
115
|
+
});
|
|
116
|
+
const bytes = import_react_native_nfc_manager.Ndef.encodeMessage(records);
|
|
117
|
+
await import_react_native_nfc_manager.default.ndefHandler.writeNdefMessage(bytes);
|
|
118
|
+
if (import_react_native.Platform.OS === "ios")
|
|
119
|
+
import_react_native_nfc_manager.default.setAlertMessageIOS("Write successful");
|
|
120
|
+
} catch (err) {
|
|
121
|
+
console.warn("[NFC] writeNdef error:", err);
|
|
122
|
+
res.success = false;
|
|
123
|
+
res.error = err;
|
|
124
|
+
} finally {
|
|
125
|
+
try {
|
|
126
|
+
await import_react_native_nfc_manager.default.cancelTechnologyRequest();
|
|
127
|
+
} catch {
|
|
128
|
+
} finally {
|
|
129
|
+
this.setState("idle");
|
|
130
|
+
return res;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
var nfcService = new NfcService();
|
|
136
|
+
|
|
137
|
+
// src/services/nfc/NfcUtils.ts
|
|
138
|
+
var import_react_native_nfc_manager2 = require("react-native-nfc-manager");
|
|
139
|
+
var toU8 = (v) => {
|
|
140
|
+
if (v instanceof Uint8Array) return v;
|
|
141
|
+
if (Array.isArray(v)) return Uint8Array.from(v);
|
|
142
|
+
return new Uint8Array(v);
|
|
143
|
+
};
|
|
144
|
+
var toAscii = (u8) => String.fromCharCode(...u8);
|
|
145
|
+
function decodeRecord(record) {
|
|
146
|
+
const { tnf } = record;
|
|
147
|
+
let typeStr;
|
|
148
|
+
if (typeof record.type === "string") {
|
|
149
|
+
typeStr = record.type;
|
|
150
|
+
} else {
|
|
151
|
+
typeStr = toAscii(toU8(record.type));
|
|
152
|
+
}
|
|
153
|
+
const payloadU8 = toU8(record.payload);
|
|
154
|
+
if (tnf === import_react_native_nfc_manager2.Ndef.TNF_WELL_KNOWN && typeStr === "T") {
|
|
155
|
+
const status = payloadU8[0];
|
|
156
|
+
const langLen = status & 63;
|
|
157
|
+
const lang = toAscii(payloadU8.subarray(1, 1 + langLen));
|
|
158
|
+
const textBytes = payloadU8.subarray(1 + langLen);
|
|
159
|
+
const text = new TextDecoder().decode(textBytes);
|
|
160
|
+
return { kind: "text", text, lang };
|
|
161
|
+
}
|
|
162
|
+
if (tnf === import_react_native_nfc_manager2.Ndef.TNF_WELL_KNOWN && typeStr === "U") {
|
|
163
|
+
const uri = import_react_native_nfc_manager2.Ndef.uri.decodePayload(payloadU8);
|
|
164
|
+
return { kind: "uri", uri };
|
|
165
|
+
}
|
|
166
|
+
if (tnf === import_react_native_nfc_manager2.Ndef.TNF_MIME_MEDIA) {
|
|
167
|
+
let tryDecodeAsText2 = function(u8) {
|
|
168
|
+
try {
|
|
169
|
+
return new TextDecoder().decode(u8);
|
|
170
|
+
} catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
var tryDecodeAsText = tryDecodeAsText2;
|
|
175
|
+
if (typeStr === "application/json") {
|
|
176
|
+
return {
|
|
177
|
+
kind: "mime",
|
|
178
|
+
mimeType: typeStr,
|
|
179
|
+
data: JSON.parse(new TextDecoder().decode(payloadU8))
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
kind: "mime",
|
|
184
|
+
mimeType: typeStr,
|
|
185
|
+
data: payloadU8,
|
|
186
|
+
text: typeStr.startsWith("text/") ? tryDecodeAsText2(payloadU8) : null
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (tnf === import_react_native_nfc_manager2.Ndef.TNF_ABSOLUTE_URI) {
|
|
190
|
+
return {
|
|
191
|
+
kind: "abs-uri",
|
|
192
|
+
uri: typeStr,
|
|
193
|
+
data: payloadU8
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (tnf === import_react_native_nfc_manager2.Ndef.TNF_EXTERNAL_TYPE) {
|
|
197
|
+
return {
|
|
198
|
+
kind: "external",
|
|
199
|
+
type: typeStr,
|
|
200
|
+
data: payloadU8
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (tnf === import_react_native_nfc_manager2.Ndef.TNF_UNKNOWN) {
|
|
204
|
+
return {
|
|
205
|
+
kind: "unknown",
|
|
206
|
+
type: typeStr,
|
|
207
|
+
data: payloadU8
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
kind: "unknown",
|
|
212
|
+
tnf,
|
|
213
|
+
type: typeStr,
|
|
214
|
+
payload: payloadU8
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function decodeJson(record) {
|
|
218
|
+
if (record.tnf !== import_react_native_nfc_manager2.Ndef.TNF_MIME_MEDIA) return null;
|
|
219
|
+
const typeStr = typeof record.type === "string" ? record.type : toAscii(toU8(record.type));
|
|
220
|
+
if (typeStr !== "application/json") return null;
|
|
221
|
+
const payloadU8 = toU8(record.payload);
|
|
222
|
+
const jsonText = new TextDecoder().decode(payloadU8);
|
|
223
|
+
try {
|
|
224
|
+
return JSON.parse(jsonText);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
console.warn("Invalid JSON in NDEF:", err);
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
var encodeText = (text, lang = "en") => import_react_native_nfc_manager2.Ndef.textRecord(text, lang);
|
|
231
|
+
var encodeUri = (uri) => import_react_native_nfc_manager2.Ndef.uriRecord(uri);
|
|
232
|
+
function encodeJson(obj) {
|
|
233
|
+
const json = JSON.stringify(obj);
|
|
234
|
+
const payload = new TextEncoder().encode(json);
|
|
235
|
+
const typeArray = Array.from(new TextEncoder().encode("application/json"));
|
|
236
|
+
return {
|
|
237
|
+
tnf: import_react_native_nfc_manager2.Ndef.TNF_MIME_MEDIA,
|
|
238
|
+
type: typeArray,
|
|
239
|
+
// MUST be a number[] of ASCII bytes
|
|
240
|
+
id: [],
|
|
241
|
+
payload
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function encodeMime(mimeType, data) {
|
|
245
|
+
const typeBytes = Array.from(new TextEncoder().encode(mimeType));
|
|
246
|
+
return {
|
|
247
|
+
tnf: import_react_native_nfc_manager2.Ndef.TNF_MIME_MEDIA,
|
|
248
|
+
type: typeBytes,
|
|
249
|
+
id: [],
|
|
250
|
+
payload: data
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function willFit(tag, records) {
|
|
254
|
+
if (tag.maxSize === void 0) return null;
|
|
255
|
+
const totalSize = import_react_native_nfc_manager2.Ndef.encodeMessage(records).length;
|
|
256
|
+
return totalSize <= tag.maxSize;
|
|
257
|
+
}
|
|
258
|
+
function spaceLeft(tag, records) {
|
|
259
|
+
if (tag.maxSize === void 0) return null;
|
|
260
|
+
const totalSize = import_react_native_nfc_manager2.Ndef.encodeMessage(records).length;
|
|
261
|
+
return tag.maxSize - totalSize;
|
|
262
|
+
}
|
|
263
|
+
var NfcUtils = {
|
|
264
|
+
Ndef: {
|
|
265
|
+
decodeRecord,
|
|
266
|
+
decodeJson,
|
|
267
|
+
encodeText,
|
|
268
|
+
encodeUri,
|
|
269
|
+
encodeJson,
|
|
270
|
+
encodeMime,
|
|
271
|
+
willFit,
|
|
272
|
+
spaceLeft
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
// src/services/nfc/useNfcState.ts
|
|
277
|
+
var import_react = require("react");
|
|
278
|
+
function useNfcState() {
|
|
279
|
+
const [state, setState] = (0, import_react.useState)(nfcService.getState());
|
|
280
|
+
(0, import_react.useEffect)(() => nfcService.subscribe(setState), []);
|
|
281
|
+
return {
|
|
282
|
+
state,
|
|
283
|
+
lastTag: nfcService.getLastTag()
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
287
|
+
0 && (module.exports = {
|
|
288
|
+
NfcUtils,
|
|
289
|
+
nfcService,
|
|
290
|
+
useNfcState
|
|
291
|
+
});
|
|
292
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/nfc/index.ts","../src/services/nfc/NfcService.ts","../src/services/nfc/NfcUtils.ts","../src/services/nfc/useNfcState.ts"],"sourcesContent":["export * from \"./NfcService\";\r\nexport * from \"./NfcUtils\";\r\nexport * from \"./useNfcState\";\r\n","// NfcService.ts\r\nimport { Platform } from \"react-native\";\r\nimport NfcManager, {\r\n Ndef,\r\n NdefRecord,\r\n NfcEvents,\r\n NfcTech,\r\n TagEvent,\r\n} from \"react-native-nfc-manager\";\r\n\r\nexport type NfcState = \"idle\" | \"starting\" | \"active\" | \"stopping\" | \"writing\";\r\n\r\nexport type NfcListener = (state: NfcState) => void;\r\n\r\nclass NfcService {\r\n private state: NfcState = \"idle\";\r\n private lastTag: TagEvent | null = null;\r\n private listeners = new Set<NfcListener>();\r\n\r\n constructor() {\r\n NfcManager.start();\r\n }\r\n\r\n // --- internal state mgmt ---\r\n private setState(next: NfcState) {\r\n if (this.state !== next) {\r\n this.state = next;\r\n for (const listener of this.listeners) listener(next);\r\n }\r\n }\r\n\r\n getState() {\r\n return this.state;\r\n }\r\n\r\n getLastTag() {\r\n return this.lastTag;\r\n }\r\n\r\n subscribe(fn: NfcListener) {\r\n this.listeners.add(fn);\r\n fn(this.state); // emit current state immediately\r\n return () => {\r\n this.listeners.delete(fn);\r\n };\r\n }\r\n\r\n // --- Reader lifecycle ---\r\n async startReader(readerModeFlags: number, onTag?: (tag: TagEvent) => void) {\r\n if (this.state !== \"idle\") {\r\n console.warn(`[NFC] Cannot start reader while ${this.state}`);\r\n return;\r\n }\r\n\r\n this.setState(\"starting\");\r\n\r\n NfcManager.setEventListener(NfcEvents.DiscoverTag, (tag: TagEvent) => {\r\n this.lastTag = tag;\r\n onTag?.(tag);\r\n });\r\n\r\n try {\r\n await NfcManager.registerTagEvent({\r\n isReaderModeEnabled: true,\r\n readerModeFlags,\r\n });\r\n if ((this.state as NfcState) === \"starting\") {\r\n this.setState(\"active\");\r\n }\r\n } catch (err) {\r\n console.warn(\"[NFC] startReader error:\", err);\r\n this.setState(\"idle\");\r\n }\r\n }\r\n\r\n async stopReader() {\r\n if ([\"idle\", \"stopping\"].includes(this.state)) return;\r\n this.setState(\"stopping\");\r\n try {\r\n await NfcManager.unregisterTagEvent();\r\n } catch {}\r\n this.setState(\"idle\");\r\n }\r\n\r\n // --- Writer ---\r\n async writeNdef(records: NdefRecord[]) {\r\n if (this.state !== \"idle\") {\r\n throw new Error(`Cannot write while reader is ${this.state}`);\r\n }\r\n\r\n this.setState(\"writing\");\r\n\r\n let res = {\r\n success: false,\r\n error: undefined,\r\n };\r\n\r\n try {\r\n await NfcManager.requestTechnology(NfcTech.Ndef, {\r\n alertMessage: \"Hold near NFC tag to write\",\r\n });\r\n const bytes = Ndef.encodeMessage(records);\r\n await NfcManager.ndefHandler.writeNdefMessage(bytes);\r\n if (Platform.OS === \"ios\")\r\n NfcManager.setAlertMessageIOS(\"Write successful\");\r\n } catch (err: any) {\r\n console.warn(\"[NFC] writeNdef error:\", err);\r\n\r\n res.success = false;\r\n res.error = err;\r\n } finally {\r\n try {\r\n await NfcManager.cancelTechnologyRequest();\r\n } catch {\r\n } finally {\r\n this.setState(\"idle\");\r\n return res;\r\n }\r\n }\r\n }\r\n}\r\n\r\n// Export one stable instance\r\nexport const nfcService = new NfcService();\r\n","import { Ndef, NdefRecord, TagEvent } from \"react-native-nfc-manager\";\r\n\r\nconst toU8 = (v: number[] | Uint8Array | ArrayBuffer): Uint8Array => {\r\n if (v instanceof Uint8Array) return v;\r\n if (Array.isArray(v)) return Uint8Array.from(v);\r\n return new Uint8Array(v);\r\n};\r\n\r\nconst toAscii = (u8: Uint8Array): string => String.fromCharCode(...u8);\r\n\r\nfunction decodeRecord(record: {\r\n tnf: number;\r\n type: number[] | string;\r\n id?: number[];\r\n payload: number[];\r\n}) {\r\n const { tnf } = record;\r\n\r\n // --- TYPE STRING -----------------------------------------------------\r\n let typeStr: string;\r\n if (typeof record.type === \"string\") {\r\n // iOS\r\n typeStr = record.type;\r\n } else {\r\n // Android\r\n typeStr = toAscii(toU8(record.type));\r\n }\r\n\r\n // --- PAYLOAD ---------------------------------------------------------\r\n const payloadU8 = toU8(record.payload);\r\n\r\n // ✅ TEXT RECORD\r\n if (tnf === Ndef.TNF_WELL_KNOWN && typeStr === \"T\") {\r\n const status = payloadU8[0];\r\n const langLen = status & 0x3f;\r\n const lang = toAscii(payloadU8.subarray(1, 1 + langLen));\r\n const textBytes = payloadU8.subarray(1 + langLen);\r\n const text = new TextDecoder().decode(textBytes);\r\n return { kind: \"text\", text, lang };\r\n }\r\n\r\n // ✅ URI RECORD\r\n if (tnf === Ndef.TNF_WELL_KNOWN && typeStr === \"U\") {\r\n const uri = Ndef.uri.decodePayload(payloadU8);\r\n return { kind: \"uri\", uri };\r\n }\r\n\r\n // ✅ MIME RECORD\r\n if (tnf === Ndef.TNF_MIME_MEDIA) {\r\n if (typeStr === \"application/json\") {\r\n return {\r\n kind: \"mime\",\r\n mimeType: typeStr,\r\n data: JSON.parse(new TextDecoder().decode(payloadU8)),\r\n };\r\n }\r\n\r\n function tryDecodeAsText(u8: Uint8Array): string | null {\r\n try {\r\n return new TextDecoder().decode(u8);\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n return {\r\n kind: \"mime\",\r\n mimeType: typeStr,\r\n data: payloadU8,\r\n text: typeStr.startsWith(\"text/\") ? tryDecodeAsText(payloadU8) : null,\r\n };\r\n }\r\n\r\n // ✅ ABSOLUTE URI RECORD\r\n if (tnf === Ndef.TNF_ABSOLUTE_URI) {\r\n return {\r\n kind: \"abs-uri\",\r\n uri: typeStr,\r\n data: payloadU8,\r\n };\r\n }\r\n\r\n // ✅ EXTERNAL RECORD\r\n if (tnf === Ndef.TNF_EXTERNAL_TYPE) {\r\n return {\r\n kind: \"external\",\r\n type: typeStr,\r\n data: payloadU8,\r\n };\r\n }\r\n\r\n // ✅ UNKNOWN RECORD\r\n if (tnf === Ndef.TNF_UNKNOWN) {\r\n return {\r\n kind: \"unknown\",\r\n type: typeStr,\r\n data: payloadU8,\r\n };\r\n }\r\n\r\n // ✅ FALLBACK\r\n return {\r\n kind: \"unknown\",\r\n tnf,\r\n type: typeStr,\r\n payload: payloadU8,\r\n };\r\n}\r\n\r\nfunction decodeJson(record: NdefRecord) {\r\n if (record.tnf !== Ndef.TNF_MIME_MEDIA) return null;\r\n\r\n // Convert type to string\r\n const typeStr =\r\n typeof record.type === \"string\" ? record.type : toAscii(toU8(record.type));\r\n\r\n if (typeStr !== \"application/json\") return null;\r\n\r\n const payloadU8 = toU8(record.payload);\r\n const jsonText = new TextDecoder().decode(payloadU8);\r\n\r\n try {\r\n return JSON.parse(jsonText);\r\n } catch (err) {\r\n console.warn(\"Invalid JSON in NDEF:\", err);\r\n return null;\r\n }\r\n}\r\n\r\nconst encodeText = (text: string, lang = \"en\") => Ndef.textRecord(text, lang);\r\n\r\nconst encodeUri = (uri: string) => Ndef.uriRecord(uri);\r\n\r\nfunction encodeJson(obj: any) {\r\n const json = JSON.stringify(obj);\r\n const payload = new TextEncoder().encode(json); // Uint8Array\r\n const typeArray = Array.from(new TextEncoder().encode(\"application/json\"));\r\n\r\n return {\r\n tnf: Ndef.TNF_MIME_MEDIA,\r\n type: typeArray, // MUST be a number[] of ASCII bytes\r\n id: [],\r\n payload,\r\n };\r\n}\r\n\r\nfunction encodeMime(mimeType: string, data: Uint8Array) {\r\n const typeBytes = Array.from(new TextEncoder().encode(mimeType));\r\n\r\n return {\r\n tnf: Ndef.TNF_MIME_MEDIA,\r\n type: typeBytes,\r\n id: [],\r\n payload: data,\r\n };\r\n}\r\n\r\n// Android only\r\nfunction willFit(tag: TagEvent, records: NdefRecord[]) {\r\n if (tag.maxSize === undefined) return null;\r\n\r\n const totalSize = Ndef.encodeMessage(records).length;\r\n return totalSize <= tag.maxSize;\r\n}\r\n\r\n// Android only\r\nfunction spaceLeft(tag: TagEvent, records: NdefRecord[]) {\r\n if (tag.maxSize === undefined) return null;\r\n\r\n const totalSize = Ndef.encodeMessage(records).length;\r\n return tag.maxSize - totalSize;\r\n}\r\n\r\nexport const NfcUtils = {\r\n Ndef: {\r\n decodeRecord,\r\n decodeJson,\r\n encodeText,\r\n encodeUri,\r\n encodeJson,\r\n encodeMime,\r\n willFit,\r\n spaceLeft,\r\n },\r\n};\r\n","import { useEffect, useState } from \"react\";\r\nimport { nfcService, NfcState } from \"./NfcService\";\r\n\r\nexport function useNfcState() {\r\n const [state, setState] = useState<NfcState>(nfcService.getState());\r\n\r\n useEffect(() => nfcService.subscribe(setState), []);\r\n\r\n return {\r\n state,\r\n lastTag: nfcService.getLastTag(),\r\n };\r\n}\r\n"],"mappings":"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","names":["NfcManager","import_react_native_nfc_manager","tryDecodeAsText"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// src/services/nfc/NfcService.ts
|
|
2
|
+
import { Platform } from "react-native";
|
|
3
|
+
import NfcManager, {
|
|
4
|
+
Ndef,
|
|
5
|
+
NfcEvents,
|
|
6
|
+
NfcTech
|
|
7
|
+
} from "react-native-nfc-manager";
|
|
8
|
+
var NfcService = class {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.state = "idle";
|
|
11
|
+
this.lastTag = null;
|
|
12
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
13
|
+
NfcManager.start();
|
|
14
|
+
}
|
|
15
|
+
// --- internal state mgmt ---
|
|
16
|
+
setState(next) {
|
|
17
|
+
if (this.state !== next) {
|
|
18
|
+
this.state = next;
|
|
19
|
+
for (const listener of this.listeners) listener(next);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
getState() {
|
|
23
|
+
return this.state;
|
|
24
|
+
}
|
|
25
|
+
getLastTag() {
|
|
26
|
+
return this.lastTag;
|
|
27
|
+
}
|
|
28
|
+
subscribe(fn) {
|
|
29
|
+
this.listeners.add(fn);
|
|
30
|
+
fn(this.state);
|
|
31
|
+
return () => {
|
|
32
|
+
this.listeners.delete(fn);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// --- Reader lifecycle ---
|
|
36
|
+
async startReader(readerModeFlags, onTag) {
|
|
37
|
+
if (this.state !== "idle") {
|
|
38
|
+
console.warn(`[NFC] Cannot start reader while ${this.state}`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this.setState("starting");
|
|
42
|
+
NfcManager.setEventListener(NfcEvents.DiscoverTag, (tag) => {
|
|
43
|
+
this.lastTag = tag;
|
|
44
|
+
onTag == null ? void 0 : onTag(tag);
|
|
45
|
+
});
|
|
46
|
+
try {
|
|
47
|
+
await NfcManager.registerTagEvent({
|
|
48
|
+
isReaderModeEnabled: true,
|
|
49
|
+
readerModeFlags
|
|
50
|
+
});
|
|
51
|
+
if (this.state === "starting") {
|
|
52
|
+
this.setState("active");
|
|
53
|
+
}
|
|
54
|
+
} catch (err) {
|
|
55
|
+
console.warn("[NFC] startReader error:", err);
|
|
56
|
+
this.setState("idle");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async stopReader() {
|
|
60
|
+
if (["idle", "stopping"].includes(this.state)) return;
|
|
61
|
+
this.setState("stopping");
|
|
62
|
+
try {
|
|
63
|
+
await NfcManager.unregisterTagEvent();
|
|
64
|
+
} catch {
|
|
65
|
+
}
|
|
66
|
+
this.setState("idle");
|
|
67
|
+
}
|
|
68
|
+
// --- Writer ---
|
|
69
|
+
async writeNdef(records) {
|
|
70
|
+
if (this.state !== "idle") {
|
|
71
|
+
throw new Error(`Cannot write while reader is ${this.state}`);
|
|
72
|
+
}
|
|
73
|
+
this.setState("writing");
|
|
74
|
+
let res = {
|
|
75
|
+
success: false,
|
|
76
|
+
error: void 0
|
|
77
|
+
};
|
|
78
|
+
try {
|
|
79
|
+
await NfcManager.requestTechnology(NfcTech.Ndef, {
|
|
80
|
+
alertMessage: "Hold near NFC tag to write"
|
|
81
|
+
});
|
|
82
|
+
const bytes = Ndef.encodeMessage(records);
|
|
83
|
+
await NfcManager.ndefHandler.writeNdefMessage(bytes);
|
|
84
|
+
if (Platform.OS === "ios")
|
|
85
|
+
NfcManager.setAlertMessageIOS("Write successful");
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.warn("[NFC] writeNdef error:", err);
|
|
88
|
+
res.success = false;
|
|
89
|
+
res.error = err;
|
|
90
|
+
} finally {
|
|
91
|
+
try {
|
|
92
|
+
await NfcManager.cancelTechnologyRequest();
|
|
93
|
+
} catch {
|
|
94
|
+
} finally {
|
|
95
|
+
this.setState("idle");
|
|
96
|
+
return res;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
var nfcService = new NfcService();
|
|
102
|
+
|
|
103
|
+
// src/services/nfc/NfcUtils.ts
|
|
104
|
+
import { Ndef as Ndef2 } from "react-native-nfc-manager";
|
|
105
|
+
var toU8 = (v) => {
|
|
106
|
+
if (v instanceof Uint8Array) return v;
|
|
107
|
+
if (Array.isArray(v)) return Uint8Array.from(v);
|
|
108
|
+
return new Uint8Array(v);
|
|
109
|
+
};
|
|
110
|
+
var toAscii = (u8) => String.fromCharCode(...u8);
|
|
111
|
+
function decodeRecord(record) {
|
|
112
|
+
const { tnf } = record;
|
|
113
|
+
let typeStr;
|
|
114
|
+
if (typeof record.type === "string") {
|
|
115
|
+
typeStr = record.type;
|
|
116
|
+
} else {
|
|
117
|
+
typeStr = toAscii(toU8(record.type));
|
|
118
|
+
}
|
|
119
|
+
const payloadU8 = toU8(record.payload);
|
|
120
|
+
if (tnf === Ndef2.TNF_WELL_KNOWN && typeStr === "T") {
|
|
121
|
+
const status = payloadU8[0];
|
|
122
|
+
const langLen = status & 63;
|
|
123
|
+
const lang = toAscii(payloadU8.subarray(1, 1 + langLen));
|
|
124
|
+
const textBytes = payloadU8.subarray(1 + langLen);
|
|
125
|
+
const text = new TextDecoder().decode(textBytes);
|
|
126
|
+
return { kind: "text", text, lang };
|
|
127
|
+
}
|
|
128
|
+
if (tnf === Ndef2.TNF_WELL_KNOWN && typeStr === "U") {
|
|
129
|
+
const uri = Ndef2.uri.decodePayload(payloadU8);
|
|
130
|
+
return { kind: "uri", uri };
|
|
131
|
+
}
|
|
132
|
+
if (tnf === Ndef2.TNF_MIME_MEDIA) {
|
|
133
|
+
let tryDecodeAsText2 = function(u8) {
|
|
134
|
+
try {
|
|
135
|
+
return new TextDecoder().decode(u8);
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
var tryDecodeAsText = tryDecodeAsText2;
|
|
141
|
+
if (typeStr === "application/json") {
|
|
142
|
+
return {
|
|
143
|
+
kind: "mime",
|
|
144
|
+
mimeType: typeStr,
|
|
145
|
+
data: JSON.parse(new TextDecoder().decode(payloadU8))
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
kind: "mime",
|
|
150
|
+
mimeType: typeStr,
|
|
151
|
+
data: payloadU8,
|
|
152
|
+
text: typeStr.startsWith("text/") ? tryDecodeAsText2(payloadU8) : null
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (tnf === Ndef2.TNF_ABSOLUTE_URI) {
|
|
156
|
+
return {
|
|
157
|
+
kind: "abs-uri",
|
|
158
|
+
uri: typeStr,
|
|
159
|
+
data: payloadU8
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (tnf === Ndef2.TNF_EXTERNAL_TYPE) {
|
|
163
|
+
return {
|
|
164
|
+
kind: "external",
|
|
165
|
+
type: typeStr,
|
|
166
|
+
data: payloadU8
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (tnf === Ndef2.TNF_UNKNOWN) {
|
|
170
|
+
return {
|
|
171
|
+
kind: "unknown",
|
|
172
|
+
type: typeStr,
|
|
173
|
+
data: payloadU8
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
kind: "unknown",
|
|
178
|
+
tnf,
|
|
179
|
+
type: typeStr,
|
|
180
|
+
payload: payloadU8
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function decodeJson(record) {
|
|
184
|
+
if (record.tnf !== Ndef2.TNF_MIME_MEDIA) return null;
|
|
185
|
+
const typeStr = typeof record.type === "string" ? record.type : toAscii(toU8(record.type));
|
|
186
|
+
if (typeStr !== "application/json") return null;
|
|
187
|
+
const payloadU8 = toU8(record.payload);
|
|
188
|
+
const jsonText = new TextDecoder().decode(payloadU8);
|
|
189
|
+
try {
|
|
190
|
+
return JSON.parse(jsonText);
|
|
191
|
+
} catch (err) {
|
|
192
|
+
console.warn("Invalid JSON in NDEF:", err);
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
var encodeText = (text, lang = "en") => Ndef2.textRecord(text, lang);
|
|
197
|
+
var encodeUri = (uri) => Ndef2.uriRecord(uri);
|
|
198
|
+
function encodeJson(obj) {
|
|
199
|
+
const json = JSON.stringify(obj);
|
|
200
|
+
const payload = new TextEncoder().encode(json);
|
|
201
|
+
const typeArray = Array.from(new TextEncoder().encode("application/json"));
|
|
202
|
+
return {
|
|
203
|
+
tnf: Ndef2.TNF_MIME_MEDIA,
|
|
204
|
+
type: typeArray,
|
|
205
|
+
// MUST be a number[] of ASCII bytes
|
|
206
|
+
id: [],
|
|
207
|
+
payload
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function encodeMime(mimeType, data) {
|
|
211
|
+
const typeBytes = Array.from(new TextEncoder().encode(mimeType));
|
|
212
|
+
return {
|
|
213
|
+
tnf: Ndef2.TNF_MIME_MEDIA,
|
|
214
|
+
type: typeBytes,
|
|
215
|
+
id: [],
|
|
216
|
+
payload: data
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function willFit(tag, records) {
|
|
220
|
+
if (tag.maxSize === void 0) return null;
|
|
221
|
+
const totalSize = Ndef2.encodeMessage(records).length;
|
|
222
|
+
return totalSize <= tag.maxSize;
|
|
223
|
+
}
|
|
224
|
+
function spaceLeft(tag, records) {
|
|
225
|
+
if (tag.maxSize === void 0) return null;
|
|
226
|
+
const totalSize = Ndef2.encodeMessage(records).length;
|
|
227
|
+
return tag.maxSize - totalSize;
|
|
228
|
+
}
|
|
229
|
+
var NfcUtils = {
|
|
230
|
+
Ndef: {
|
|
231
|
+
decodeRecord,
|
|
232
|
+
decodeJson,
|
|
233
|
+
encodeText,
|
|
234
|
+
encodeUri,
|
|
235
|
+
encodeJson,
|
|
236
|
+
encodeMime,
|
|
237
|
+
willFit,
|
|
238
|
+
spaceLeft
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// src/services/nfc/useNfcState.ts
|
|
243
|
+
import { useEffect, useState } from "react";
|
|
244
|
+
function useNfcState() {
|
|
245
|
+
const [state, setState] = useState(nfcService.getState());
|
|
246
|
+
useEffect(() => nfcService.subscribe(setState), []);
|
|
247
|
+
return {
|
|
248
|
+
state,
|
|
249
|
+
lastTag: nfcService.getLastTag()
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
export {
|
|
253
|
+
NfcUtils,
|
|
254
|
+
nfcService,
|
|
255
|
+
useNfcState
|
|
256
|
+
};
|
|
257
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/nfc/NfcService.ts","../src/services/nfc/NfcUtils.ts","../src/services/nfc/useNfcState.ts"],"sourcesContent":["// NfcService.ts\r\nimport { Platform } from \"react-native\";\r\nimport NfcManager, {\r\n Ndef,\r\n NdefRecord,\r\n NfcEvents,\r\n NfcTech,\r\n TagEvent,\r\n} from \"react-native-nfc-manager\";\r\n\r\nexport type NfcState = \"idle\" | \"starting\" | \"active\" | \"stopping\" | \"writing\";\r\n\r\nexport type NfcListener = (state: NfcState) => void;\r\n\r\nclass NfcService {\r\n private state: NfcState = \"idle\";\r\n private lastTag: TagEvent | null = null;\r\n private listeners = new Set<NfcListener>();\r\n\r\n constructor() {\r\n NfcManager.start();\r\n }\r\n\r\n // --- internal state mgmt ---\r\n private setState(next: NfcState) {\r\n if (this.state !== next) {\r\n this.state = next;\r\n for (const listener of this.listeners) listener(next);\r\n }\r\n }\r\n\r\n getState() {\r\n return this.state;\r\n }\r\n\r\n getLastTag() {\r\n return this.lastTag;\r\n }\r\n\r\n subscribe(fn: NfcListener) {\r\n this.listeners.add(fn);\r\n fn(this.state); // emit current state immediately\r\n return () => {\r\n this.listeners.delete(fn);\r\n };\r\n }\r\n\r\n // --- Reader lifecycle ---\r\n async startReader(readerModeFlags: number, onTag?: (tag: TagEvent) => void) {\r\n if (this.state !== \"idle\") {\r\n console.warn(`[NFC] Cannot start reader while ${this.state}`);\r\n return;\r\n }\r\n\r\n this.setState(\"starting\");\r\n\r\n NfcManager.setEventListener(NfcEvents.DiscoverTag, (tag: TagEvent) => {\r\n this.lastTag = tag;\r\n onTag?.(tag);\r\n });\r\n\r\n try {\r\n await NfcManager.registerTagEvent({\r\n isReaderModeEnabled: true,\r\n readerModeFlags,\r\n });\r\n if ((this.state as NfcState) === \"starting\") {\r\n this.setState(\"active\");\r\n }\r\n } catch (err) {\r\n console.warn(\"[NFC] startReader error:\", err);\r\n this.setState(\"idle\");\r\n }\r\n }\r\n\r\n async stopReader() {\r\n if ([\"idle\", \"stopping\"].includes(this.state)) return;\r\n this.setState(\"stopping\");\r\n try {\r\n await NfcManager.unregisterTagEvent();\r\n } catch {}\r\n this.setState(\"idle\");\r\n }\r\n\r\n // --- Writer ---\r\n async writeNdef(records: NdefRecord[]) {\r\n if (this.state !== \"idle\") {\r\n throw new Error(`Cannot write while reader is ${this.state}`);\r\n }\r\n\r\n this.setState(\"writing\");\r\n\r\n let res = {\r\n success: false,\r\n error: undefined,\r\n };\r\n\r\n try {\r\n await NfcManager.requestTechnology(NfcTech.Ndef, {\r\n alertMessage: \"Hold near NFC tag to write\",\r\n });\r\n const bytes = Ndef.encodeMessage(records);\r\n await NfcManager.ndefHandler.writeNdefMessage(bytes);\r\n if (Platform.OS === \"ios\")\r\n NfcManager.setAlertMessageIOS(\"Write successful\");\r\n } catch (err: any) {\r\n console.warn(\"[NFC] writeNdef error:\", err);\r\n\r\n res.success = false;\r\n res.error = err;\r\n } finally {\r\n try {\r\n await NfcManager.cancelTechnologyRequest();\r\n } catch {\r\n } finally {\r\n this.setState(\"idle\");\r\n return res;\r\n }\r\n }\r\n }\r\n}\r\n\r\n// Export one stable instance\r\nexport const nfcService = new NfcService();\r\n","import { Ndef, NdefRecord, TagEvent } from \"react-native-nfc-manager\";\r\n\r\nconst toU8 = (v: number[] | Uint8Array | ArrayBuffer): Uint8Array => {\r\n if (v instanceof Uint8Array) return v;\r\n if (Array.isArray(v)) return Uint8Array.from(v);\r\n return new Uint8Array(v);\r\n};\r\n\r\nconst toAscii = (u8: Uint8Array): string => String.fromCharCode(...u8);\r\n\r\nfunction decodeRecord(record: {\r\n tnf: number;\r\n type: number[] | string;\r\n id?: number[];\r\n payload: number[];\r\n}) {\r\n const { tnf } = record;\r\n\r\n // --- TYPE STRING -----------------------------------------------------\r\n let typeStr: string;\r\n if (typeof record.type === \"string\") {\r\n // iOS\r\n typeStr = record.type;\r\n } else {\r\n // Android\r\n typeStr = toAscii(toU8(record.type));\r\n }\r\n\r\n // --- PAYLOAD ---------------------------------------------------------\r\n const payloadU8 = toU8(record.payload);\r\n\r\n // ✅ TEXT RECORD\r\n if (tnf === Ndef.TNF_WELL_KNOWN && typeStr === \"T\") {\r\n const status = payloadU8[0];\r\n const langLen = status & 0x3f;\r\n const lang = toAscii(payloadU8.subarray(1, 1 + langLen));\r\n const textBytes = payloadU8.subarray(1 + langLen);\r\n const text = new TextDecoder().decode(textBytes);\r\n return { kind: \"text\", text, lang };\r\n }\r\n\r\n // ✅ URI RECORD\r\n if (tnf === Ndef.TNF_WELL_KNOWN && typeStr === \"U\") {\r\n const uri = Ndef.uri.decodePayload(payloadU8);\r\n return { kind: \"uri\", uri };\r\n }\r\n\r\n // ✅ MIME RECORD\r\n if (tnf === Ndef.TNF_MIME_MEDIA) {\r\n if (typeStr === \"application/json\") {\r\n return {\r\n kind: \"mime\",\r\n mimeType: typeStr,\r\n data: JSON.parse(new TextDecoder().decode(payloadU8)),\r\n };\r\n }\r\n\r\n function tryDecodeAsText(u8: Uint8Array): string | null {\r\n try {\r\n return new TextDecoder().decode(u8);\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n return {\r\n kind: \"mime\",\r\n mimeType: typeStr,\r\n data: payloadU8,\r\n text: typeStr.startsWith(\"text/\") ? tryDecodeAsText(payloadU8) : null,\r\n };\r\n }\r\n\r\n // ✅ ABSOLUTE URI RECORD\r\n if (tnf === Ndef.TNF_ABSOLUTE_URI) {\r\n return {\r\n kind: \"abs-uri\",\r\n uri: typeStr,\r\n data: payloadU8,\r\n };\r\n }\r\n\r\n // ✅ EXTERNAL RECORD\r\n if (tnf === Ndef.TNF_EXTERNAL_TYPE) {\r\n return {\r\n kind: \"external\",\r\n type: typeStr,\r\n data: payloadU8,\r\n };\r\n }\r\n\r\n // ✅ UNKNOWN RECORD\r\n if (tnf === Ndef.TNF_UNKNOWN) {\r\n return {\r\n kind: \"unknown\",\r\n type: typeStr,\r\n data: payloadU8,\r\n };\r\n }\r\n\r\n // ✅ FALLBACK\r\n return {\r\n kind: \"unknown\",\r\n tnf,\r\n type: typeStr,\r\n payload: payloadU8,\r\n };\r\n}\r\n\r\nfunction decodeJson(record: NdefRecord) {\r\n if (record.tnf !== Ndef.TNF_MIME_MEDIA) return null;\r\n\r\n // Convert type to string\r\n const typeStr =\r\n typeof record.type === \"string\" ? record.type : toAscii(toU8(record.type));\r\n\r\n if (typeStr !== \"application/json\") return null;\r\n\r\n const payloadU8 = toU8(record.payload);\r\n const jsonText = new TextDecoder().decode(payloadU8);\r\n\r\n try {\r\n return JSON.parse(jsonText);\r\n } catch (err) {\r\n console.warn(\"Invalid JSON in NDEF:\", err);\r\n return null;\r\n }\r\n}\r\n\r\nconst encodeText = (text: string, lang = \"en\") => Ndef.textRecord(text, lang);\r\n\r\nconst encodeUri = (uri: string) => Ndef.uriRecord(uri);\r\n\r\nfunction encodeJson(obj: any) {\r\n const json = JSON.stringify(obj);\r\n const payload = new TextEncoder().encode(json); // Uint8Array\r\n const typeArray = Array.from(new TextEncoder().encode(\"application/json\"));\r\n\r\n return {\r\n tnf: Ndef.TNF_MIME_MEDIA,\r\n type: typeArray, // MUST be a number[] of ASCII bytes\r\n id: [],\r\n payload,\r\n };\r\n}\r\n\r\nfunction encodeMime(mimeType: string, data: Uint8Array) {\r\n const typeBytes = Array.from(new TextEncoder().encode(mimeType));\r\n\r\n return {\r\n tnf: Ndef.TNF_MIME_MEDIA,\r\n type: typeBytes,\r\n id: [],\r\n payload: data,\r\n };\r\n}\r\n\r\n// Android only\r\nfunction willFit(tag: TagEvent, records: NdefRecord[]) {\r\n if (tag.maxSize === undefined) return null;\r\n\r\n const totalSize = Ndef.encodeMessage(records).length;\r\n return totalSize <= tag.maxSize;\r\n}\r\n\r\n// Android only\r\nfunction spaceLeft(tag: TagEvent, records: NdefRecord[]) {\r\n if (tag.maxSize === undefined) return null;\r\n\r\n const totalSize = Ndef.encodeMessage(records).length;\r\n return tag.maxSize - totalSize;\r\n}\r\n\r\nexport const NfcUtils = {\r\n Ndef: {\r\n decodeRecord,\r\n decodeJson,\r\n encodeText,\r\n encodeUri,\r\n encodeJson,\r\n encodeMime,\r\n willFit,\r\n spaceLeft,\r\n },\r\n};\r\n","import { useEffect, useState } from \"react\";\r\nimport { nfcService, NfcState } from \"./NfcService\";\r\n\r\nexport function useNfcState() {\r\n const [state, setState] = useState<NfcState>(nfcService.getState());\r\n\r\n useEffect(() => nfcService.subscribe(setState), []);\r\n\r\n return {\r\n state,\r\n lastTag: nfcService.getLastTag(),\r\n };\r\n}\r\n"],"mappings":"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","names":["Ndef","tryDecodeAsText"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@spencerls/react-native-nfc",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight NFC manager for React Native projects using react-native-nfc-manager.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsup",
|
|
9
|
+
"clean": "rm -rf dist"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"nfc",
|
|
13
|
+
"react-native",
|
|
14
|
+
"expo",
|
|
15
|
+
"nfc-manager",
|
|
16
|
+
"capacitor"
|
|
17
|
+
],
|
|
18
|
+
"author": "Spencer Smith",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/Spencer1O1/react-native-nfc.git"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"react": ">=18",
|
|
26
|
+
"react-native": ">=0.74",
|
|
27
|
+
"react-native-nfc-manager": ">=3.17.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/react": "^19.2.3",
|
|
31
|
+
"@types/react-native": "^0.72.8",
|
|
32
|
+
"tsup": "^8.0.0",
|
|
33
|
+
"typescript": "^5.3.0"
|
|
34
|
+
}
|
|
35
|
+
}
|