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:
+85
-10
@@ -2,10 +2,12 @@
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Aplicação mobile do **MeowSpool** construída com **React Native** + **Expo** (SDK ~51), usando **expo-router** para navegação baseada em sistema de arquivos. A arquitetura espelha o backend Rust: **Clean Code / Hexagonal**, com camadas bem delimitadas do domínio até a apresentação.
|
||||
Aplicação mobile do **MeowSpool** construída com **React Native** + **Expo** (SDK ~54), usando **expo-router** para navegação baseada em sistema de arquivos. A arquitetura espelha o backend Rust: **Clean Code / Hexagonal**, com camadas bem delimitadas do domínio até a apresentação.
|
||||
|
||||
O app é **offline-first**: todos os dados são persistidos localmente em **SQLite** (expo-sqlite + SQLCipher) e sincronizados com o backend Rust em background usando estratégia **last-write-wins** via campo `updatedAt`.
|
||||
|
||||
> **Bare Workflow**: o projeto usa `expo prebuild` (diretório `android/` comitado). Qualquer novo pacote com módulo nativo exige `npx expo prebuild --platform android` + rebuild completo do APK.
|
||||
|
||||
---
|
||||
|
||||
## Stack
|
||||
@@ -27,7 +29,9 @@ O app é **offline-first**: todos os dados são persistidos localmente em **SQLi
|
||||
| Gesture Handler | `react-native-gesture-handler` | |
|
||||
| Animations | `react-native-reanimated` | |
|
||||
| QR Code render | `react-native-qrcode-svg` | Render de QR Code em tela |
|
||||
| SVG inline | `react-native-svg` | SvgXml para renderizar SVG como string; sem transformer |
|
||||
| Câmera / Scanner | `expo-camera` ~17 | ML Kit barcode scan; requer development build |
|
||||
| NFC | `react-native-nfc-manager` | Leitura e gravação NDEF (NTAG215); requer build nativo |
|
||||
| File system | `expo-file-system/legacy` | Salvar arquivos no cache; usar import `/legacy` |
|
||||
| Compartilhamento | `expo-sharing` | Sheet nativo de compartilhamento de arquivos |
|
||||
|
||||
@@ -42,7 +46,8 @@ src/
|
||||
├── application/ ← Use Cases (orquestram domínio + ports)
|
||||
├── adapters/
|
||||
│ ├── remote/ ← Implementações HTTP (Axios → API Rust)
|
||||
│ └── local/ ← Implementações SQLite (expo-sqlite)
|
||||
│ ├── local/ ← Implementações SQLite (expo-sqlite)
|
||||
│ └── nfc/ ← Implementação NFC (react-native-nfc-manager)
|
||||
├── store/ ← Estado em memória (Zustand) — cache das queries
|
||||
├── shared/ ← Design tokens, constantes, utilitários
|
||||
└── presentation/
|
||||
@@ -104,10 +109,12 @@ mobile/
|
||||
│ │ └── [id]/
|
||||
│ │ └── edit.tsx ← 1KD-0 Editar Preset
|
||||
│ ├── scanner.tsx ← Scanner de QR Code (expo-camera ML Kit)
|
||||
│ ├── nfc-reader.tsx ← 1RY-0 Bottom sheet modal de leitura NFC
|
||||
│ └── filaments/
|
||||
│ └── [id]/
|
||||
│ ├── qrcode.tsx ← Ver QR Code (react-native-qrcode-svg)
|
||||
│ └── label.tsx ← Exportar Etiqueta (PDF ou SVG)
|
||||
│ ├── label.tsx ← Exportar Etiqueta (PDF ou SVG)
|
||||
│ └── write-nfc.tsx ← Gravar tag NTAG215 com deep link do filamento
|
||||
│
|
||||
└── src/
|
||||
├── domain/
|
||||
@@ -118,7 +125,8 @@ mobile/
|
||||
├── ports/
|
||||
│ ├── FilamentRepository.ts ← interface IFilamentRepository
|
||||
│ ├── SpoolPresetRepository.ts ← interface ISpoolPresetRepository
|
||||
│ └── AuthRepository.ts ← interface IAuthRepository
|
||||
│ ├── AuthRepository.ts ← interface IAuthRepository
|
||||
│ └── INFCRepository.ts ← interface INFCRepository (isSupported, readTag, writeTag, cancelSession)
|
||||
├── application/
|
||||
│ ├── filament/
|
||||
│ │ ├── CreateFilamentUseCase.ts
|
||||
@@ -127,18 +135,23 @@ mobile/
|
||||
│ │ └── DeleteFilamentUseCase.ts
|
||||
│ ├── preset/
|
||||
│ │ └── PresetUseCases.ts ← List, Create, Update, Delete
|
||||
│ └── auth/
|
||||
│ └── AuthUseCases.ts ← Login, Register, Google, Logout, etc.
|
||||
│ ├── auth/
|
||||
│ │ └── AuthUseCases.ts ← Login, Register, Google, Logout, etc.
|
||||
│ └── nfc/
|
||||
│ ├── ReadNFCTagUseCase.ts ← lê URI, valida schema meowspool://, extrai filament ID
|
||||
│ └── WriteNFCTagUseCase.ts ← monta meowspool://filament/{id} e grava na tag
|
||||
├── adapters/
|
||||
│ ├── remote/
|
||||
│ │ ├── httpClient.ts ← Axios + interceptors JWT + refresh
|
||||
│ │ ├── ApiAuthRepository.ts ← /api/v1/auth/*
|
||||
│ │ ├── ApiFilamentRepository.ts ← /api/v1/filaments/*
|
||||
│ │ └── ApiSpoolPresetRepository.ts ← /api/v1/spool-presets/*
|
||||
│ └── local/
|
||||
│ ├── database.ts ← init SQLite, WAL, foreign keys, tabelas
|
||||
│ ├── LocalFilamentRepository.ts
|
||||
│ └── LocalSpoolPresetRepository.ts
|
||||
│ ├── local/
|
||||
│ │ ├── database.ts ← init SQLite, WAL, foreign keys, tabelas
|
||||
│ │ ├── LocalFilamentRepository.ts
|
||||
│ │ └── LocalSpoolPresetRepository.ts
|
||||
│ └── nfc/
|
||||
│ └── NFCManagerRepository.ts ← implementa INFCRepository via react-native-nfc-manager
|
||||
├── store/
|
||||
│ ├── authStore.ts ← Zustand: sessão, SecureStore
|
||||
│ ├── filamentStore.ts ← Zustand: lista + filtros em memória
|
||||
@@ -211,6 +224,7 @@ Arquivo: `src/shared/theme.ts`
|
||||
- Filamento: `meowspool://filament/<id>` → `/(app)/inventory/<id>` (singular, alinhado com backend)
|
||||
- O scanner (`scanner.tsx`) faz match via `/meowspool:\/\/filament\/([^/]+)/` e navega para `/(app)/inventory/<id>`
|
||||
- O QR Code de cada filamento exibe `meowspool://filament/<id>` usando `react-native-qrcode-svg`
|
||||
- As tags NFC NTAG215 gravam a mesma URI `meowspool://filament/<id>` como NDEF URI record — mesmo deep link, infraestrutura compartilhada
|
||||
|
||||
---
|
||||
|
||||
@@ -335,6 +349,9 @@ Base: `EXPO_PUBLIC_API_URL` (padrão: `http://localhost:3000/api/v1`)
|
||||
- [x] Exportar etiqueta SVG via `GET /filaments/:id/label.svg`
|
||||
- [x] Exportar etiqueta PDF via `GET /filaments/:id/label.pdf`
|
||||
- [x] Seletor de formato (PDF/SVG) e controle de campos na tela de etiqueta
|
||||
- [x] NFC leitura NTAG215 → `nfc-reader.tsx` (bottom sheet modal)
|
||||
- [x] NFC gravação NTAG215 → `filaments/[id]/write-nfc.tsx`
|
||||
- [ ] NFC suporte iOS (requer entitlement `com.apple.developer.nfc.readwrite`)
|
||||
- [ ] Expo Notifications para alertas de estoque baixo
|
||||
- [ ] Google OAuth com `expo-auth-session`
|
||||
- [ ] Testes de integração com Jest + Testing Library
|
||||
@@ -343,6 +360,64 @@ Base: `EXPO_PUBLIC_API_URL` (padrão: `http://localhost:3000/api/v1`)
|
||||
|
||||
## Mudanças Recentes (14/03/2026)
|
||||
|
||||
### ✅ NFC Read/Write — NTAG215
|
||||
|
||||
Implementação completa de leitura e gravação NFC para tags NTAG215. O conteúdo gravado é idêntico ao QR Code: `meowspool://filament/{id}` como NDEF URI record.
|
||||
|
||||
**Pacote instalado**: `react-native-nfc-manager`
|
||||
|
||||
**Arquitetura (mesma camada hexagonal do projeto)**:
|
||||
|
||||
| Camada | Arquivo | Responsabilidade |
|
||||
|--------|---------|-----------------|
|
||||
| Port | `src/ports/INFCRepository.ts` | Interface: `isSupported`, `readTag`, `writeTag`, `cancelSession` |
|
||||
| Adapter | `src/adapters/nfc/NFCManagerRepository.ts` | Implementação via `react-native-nfc-manager` |
|
||||
| Use Case | `src/application/nfc/ReadNFCTagUseCase.ts` | Lê URI, valida schema `meowspool://filament/`, extrai ID |
|
||||
| Use Case | `src/application/nfc/WriteNFCTagUseCase.ts` | Monta `meowspool://filament/{id}` e delega ao adapter |
|
||||
| DI | `src/infrastructure/container.ts` | Exporta `nfcRepository`, `readNFCTagUseCase`, `writeNFCTagUseCase` |
|
||||
|
||||
**Telas**:
|
||||
|
||||
- **`app/(app)/nfc-reader.tsx`** (artboard `1RY-0`): bottom sheet modal com animação de ondas pulsantes (3 anéis `Animated`), badge "LENDO...", título/subtítulo e botão Cancelar. Apresentado como `presentation: 'transparentModal'` via `_layout.tsx`. Ao detectar a tag navega diretamente para `/(app)/inventory/<id>`.
|
||||
- **`app/(app)/filaments/[id]/write-nfc.tsx`**: tela de gravação com 3 estados visuais (escrevendo / sucesso / erro), mesma animação de ondas. Botão "Gravar NFC" aparece no card de Identificação do Detalhe do Filamento, ao lado de "Ver QR".
|
||||
|
||||
**Ponto de entrada — Home (`home.tsx`)**: botão NFC adicionado à esquerda do botão QR no header. Checa `isSupported()` antes de abrir o modal; exibe `Alert` se NFC não estiver disponível.
|
||||
|
||||
**Ícone NFC**: renderizado via `SvgXml` do `react-native-svg` (sem SVG transformer). O template string com os 4 arcos SVG é definido no topo de cada arquivo que usa o ícone — não há wrapper de componente.
|
||||
|
||||
**Inicialização do módulo nativo**:
|
||||
|
||||
`NfcManager.start()` deve ser chamado antes de `requestTechnology`. O adapter usa uma flag `started` e chama `start()` de forma lazy (apenas em `readTag`/`writeTag`). **`isSupported()` não chama `start()`** — faz apenas um check do módulo nativo via `NativeModules.NfcManager != null` + try/catch.
|
||||
|
||||
```ts
|
||||
// Padrão correto:
|
||||
async isSupported(): Promise<boolean> {
|
||||
if (!this.nativeModuleAvailable) return false;
|
||||
try { return await NfcManager.isSupported(); } catch { return false; }
|
||||
}
|
||||
|
||||
// start() só é chamado quando vai usar NFC de verdade:
|
||||
private async ensureStarted(): Promise<void> {
|
||||
if (!this.nativeModuleAvailable) throw new Error('Módulo NFC não disponível.');
|
||||
if (!this.started) { await NfcManager.start(); this.started = true; }
|
||||
}
|
||||
```
|
||||
|
||||
**⚠️ Requer build nativo**: `react-native-nfc-manager` usa módulo nativo. Não funciona no Expo Go. Após instalar ou rodar `expo prebuild`, é obrigatório recompilar o APK:
|
||||
|
||||
```bash
|
||||
mise exec -- npx expo run:android
|
||||
```
|
||||
|
||||
**Permissões Android** (adicionadas automaticamente pelo plugin no `app.json`):
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.NFC"/>
|
||||
```
|
||||
|
||||
**iOS**: não implementado nesta fase. Requer entitlement `com.apple.developer.nfc.readwrite` da Apple Developer Program. A arquitetura suporta adição futura sem mudanças nas camadas acima do adapter.
|
||||
|
||||
---
|
||||
|
||||
### ✅ Exportação de Etiqueta PDF e SVG (`label.tsx`)
|
||||
|
||||
- **Pacotes instalados**: `expo-file-system`, `expo-sharing`, `react-native-worklets`
|
||||
|
||||
Reference in New Issue
Block a user