Implementation
The scanner is built withexpo-camera and implements several reliability patterns:
Camera permissions
The component uses theuseCameraPermissions hook (src/components/scanner/CameraScanner.tsx:18):
- Permission loading
- Permission denied
- Permission granted
Idempotency guard
The scanner prevents double-scan processing using a ref-based lock (src/components/scanner/CameraScanner.tsx:27):- Successful navigation (automatic after 900ms)
- Error toast dismissal (src/components/scanner/CameraScanner.tsx:37):
Scan states
The scanner uses a state machine with three states (src/components/scanner/ScanFrame.tsx):| State | Trigger | Visual Feedback | Duration |
|---|---|---|---|
idle | Initial render / reset | White animated frame | Continuous |
success | Valid QR detected | Green frame + glow | 900ms |
error | Invalid QR detected | Red frame + shake | Until toast dismissed |
src/components/scanner/CameraScanner.tsx
Multimodal feedback
The scanner provides three layers of feedback for accessibility and user confidence:Visual
- Scan frame animation: A branded frame with corner brackets animates continuously in idle state
- Color changes: Frame turns green on success, red on error
- Toast messages: Error messages slide in from the top
Haptic
src/components/scanner/CameraScanner.tsx
Haptics only trigger on physical devices. They are silently ignored in simulators.
Audio
src/components/scanner/CameraScanner.tsx
SoundService uses expo-av to play a confirmation beep.
UI overlay
The scanner uses a darkened mask with a transparent center frame (src/components/scanner/CameraScanner.tsx:123):rgba(0,0,0,0.58) — dark enough to focus attention, light enough to see surroundings
QR code validation
The scanner validates codes against theTABLES_DATA registry:
src/components/scanner/CameraScanner.tsx
- If the code starts with
TABLE_but doesn’t exist: “This code doesn’t belong to any TableOrder table.” - If the code is completely unrelated: “This QR code is not valid for TableOrder.”
Test QR codes
Use these codes for testing the scanner:Bar table
QR:
TABLE_BAR_01Table: Barra 01Menu: Drinks onlyDining table
QR:
TABLE_HALL_05Table: Salon 05Menu: Full menuBirthday table
QR:
TABLE_BDAY_99Table: Mesa EspecialMenu: Full menu + 15% discountGenerate test QR codes at qr.io — paste the code string, download the image, and scan with the app.
Navigation flow
After a successful scan, the scanner waits 900ms before navigating to allow the user to see the success feedback:src/components/scanner/CameraScanner.tsx
tableId is passed as a route parameter and hydrated into useTableStore if the store is cold (src/lib/modules/menu/useMenuLogic.ts:15):
Error handling
Invalid QR code
Invalid codes trigger:- Visual: Frame turns red, error toast slides in
- Haptic: Warning notification
- Audio: Silent (no beep)
- State: Scanner locked until toast dismissed
Camera errors
If the camera fails to initialize,expo-camera throws an error. This should be caught with an error boundary in production.
Performance considerations
Scan throttling
The idempotency guard serves as a natural throttle — only one scan can be processed at a time.Memory management
The camera view is unmounted when navigating away, releasing hardware resources automatically.Frame rate
The camera runs at native frame rate (30-60 FPS) with real-time QR detection. No manual optimization needed.Accessibility
- Haptic feedback: Helps visually impaired users confirm scan success
- Audio beep: Provides auditory confirmation
- High contrast UI: White frame on dark background with clear visual states
- Error messages: Descriptive text explains what went wrong
Related features
Table Mode
Complete table ordering workflow
Contextual Menus
Menu filtering based on scanned table