feat: integrate NFC functionality for reading and writing tags

- Added NFCManagerRepository to handle NFC operations using react-native-nfc-manager.
- Implemented ReadNFCTagUseCase and WriteNFCTagUseCase for reading and writing NFC tags.
- Updated app layout to include NFC reader screen.
- Created WriteNFCScreen for writing NFC tags with filament data.
- Enhanced HomeScreen to support NFC reading and added NFC button in FilamentDetailScreen.
- Updated app.json to include react-native-nfc-manager dependency.
- Added animations and user feedback for NFC operations in the UI.
This commit is contained in:
2026-03-14 20:14:06 -03:00
parent 74adc35b53
commit 6a78cb41d8
15 changed files with 797 additions and 23 deletions
@@ -0,0 +1,61 @@
import NfcManager, { Ndef, NfcTech } from 'react-native-nfc-manager';
import { NativeModules } from 'react-native';
import { INFCRepository } from '@ports/INFCRepository';
export class NFCManagerRepository implements INFCRepository {
private started = false;
private get nativeModuleAvailable(): boolean {
return NativeModules.NfcManager != null;
}
private async ensureStarted(): Promise<void> {
if (!this.nativeModuleAvailable) {
throw new Error('Módulo NFC não disponível. Reinstale o app.');
}
if (!this.started) {
await NfcManager.start();
this.started = true;
}
}
async isSupported(): Promise<boolean> {
if (!this.nativeModuleAvailable) return false;
try {
return await NfcManager.isSupported();
} catch {
return false;
}
}
async readTag(): Promise<string | null> {
await this.ensureStarted();
await NfcManager.requestTechnology(NfcTech.Ndef);
try {
const tag = await NfcManager.getTag();
const ndefRecords = tag?.ndefMessage;
if (!ndefRecords || ndefRecords.length === 0) return null;
const firstRecord = ndefRecords[0];
const uri = Ndef.uri.decodePayload(firstRecord.payload as unknown as Uint8Array);
return uri ?? null;
} finally {
NfcManager.cancelTechnologyRequest();
}
}
async writeTag(uri: string): Promise<void> {
await this.ensureStarted();
await NfcManager.requestTechnology(NfcTech.Ndef);
try {
const bytes = Ndef.encodeMessage([Ndef.uriRecord(uri)]);
await NfcManager.ndefHandler.writeNdefMessage(bytes);
} finally {
NfcManager.cancelTechnologyRequest();
}
}
async cancelSession(): Promise<void> {
await NfcManager.cancelTechnologyRequest();
}
}
@@ -0,0 +1,21 @@
import { INFCRepository } from '@ports/INFCRepository';
const FILAMENT_URI_PREFIX = 'meowspool://filament/';
export class ReadNFCTagUseCase {
constructor(private readonly nfcRepository: INFCRepository) {}
async execute(): Promise<string> {
const uri = await this.nfcRepository.readTag();
if (!uri) throw new Error('Tag sem dados NDEF.');
if (!uri.startsWith(FILAMENT_URI_PREFIX)) {
throw new Error('Tag não reconhecida pelo MeowSpool.');
}
const filamentId = uri.slice(FILAMENT_URI_PREFIX.length);
if (!filamentId) throw new Error('ID do filamento inválido na tag.');
return filamentId;
}
}
@@ -0,0 +1,10 @@
import { INFCRepository } from '@ports/INFCRepository';
export class WriteNFCTagUseCase {
constructor(private readonly nfcRepository: INFCRepository) {}
async execute(filamentId: string): Promise<void> {
const uri = `meowspool://filament/${filamentId}`;
await this.nfcRepository.writeTag(uri);
}
}
+9
View File
@@ -1,3 +1,6 @@
import { NFCManagerRepository } from '@adapters/nfc/NFCManagerRepository';
import { ReadNFCTagUseCase } from '@application/nfc/ReadNFCTagUseCase';
import { WriteNFCTagUseCase } from '@application/nfc/WriteNFCTagUseCase';
import { ApiAuthRepository } from '@adapters/remote/ApiAuthRepository';
import { ApiFilamentRepository } from '@adapters/remote/ApiFilamentRepository';
import { ApiSpoolPresetRepository } from '@adapters/remote/ApiSpoolPresetRepository';
@@ -42,3 +45,9 @@ export const listPresetsUseCase = new ListPresetsUseCase(presetRepository);
export const createPresetUseCase = new CreatePresetUseCase(presetRepository);
export const updatePresetUseCase = new UpdatePresetUseCase(presetRepository);
export const deletePresetUseCase = new DeletePresetUseCase(presetRepository);
// NFC
const nfcRepository = new NFCManagerRepository();
export const readNFCTagUseCase = new ReadNFCTagUseCase(nfcRepository);
export const writeNFCTagUseCase = new WriteNFCTagUseCase(nfcRepository);
export { nfcRepository };
+6
View File
@@ -0,0 +1,6 @@
export interface INFCRepository {
isSupported(): Promise<boolean>;
readTag(): Promise<string | null>;
writeTag(uri: string): Promise<void>;
cancelSession(): Promise<void>;
}