Skip to content

feat(mobile): add QR scanner component using expo-camera - #548

Merged
Miracle656 merged 2 commits into
Miracle656:mainfrom
iredis:fix/468-qr-scanner-mobile
Jul 30, 2026
Merged

feat(mobile): add QR scanner component using expo-camera#548
Miracle656 merged 2 commits into
Miracle656:mainfrom
iredis:fix/468-qr-scanner-mobile

Conversation

@iredis

@iredis iredis commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Overview

This PR adds a QR Scanner component for the mobile app that replaces the web's BarcodeDetector/getUserMedia approach with expo-camera's native CameraView + onBarcodeScanned. The component handles camera permissions via useCameraPermissions, scans Stellar/SEP-7 QR codes, and includes a manual address entry fallback for users who cannot use the camera.

Related Issue

Closes #468

Changes

📦 New Files

  • [ADD] frontend/mobile/components/QrScanner.tsx — Full QR scanner component with:
    • CameraView from expo-camera with QR barcode scanning via onBarcodeScanned
    • Camera permission handling (useCameraPermissions): grants on first use, shows settings prompt if denied
    • Gold corner-bracket viewfinder overlay
    • Manual address entry fallback (type/paste G… or C… address with validation)
    • Stellar address validation (isValidStellarAddressG/C prefix, 56 chars)
    • Controlled via visible/onScan/onClose props using React Native Modal

📝 Modified Files

  • [MODIFY] frontend/mobile/package.json — Added expo-camera dependency
  • [MODIFY] frontend/mobile/app.json — Added camera plugin config with permission string

Verification

Acceptance Criteria Status
Scanning a Stellar/SEP-7 QR fills the recipient field onBarcodeScanned callback returns decoded data; component calls onScan with the address
Camera permission is handled useCameraPermissions hook; shows permission prompt or settings redirect
Manual address entry works as fallback ✅ Text input with validation; submits via onScan
Non-Stellar QR codes are ignored ✅ Only addresses matching G…/C… (56 chars) trigger onScan

Creates QrScanner component that replaces the web BarcodeDetector approach
with expo-camera's CameraView + onBarcodeScanned for native mobile QR scanning.
Includes camera permission handling, viewfinder overlay, and manual address
entry fallback.

Closes Miracle656#468
@iredis
iredis requested a review from Miracle656 as a code owner July 28, 2026 18:24
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

@iredis is attempting to deploy a commit to the miracle656's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Jul 28, 2026

Copy link
Copy Markdown

@iredis Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

- Drop app.json (main uses app.config.ts, which already registers the
  expo-camera plugin with its permission string) and take main's dependencies.
- The scanner only accepted a bare G…/C… address, so a SEP-7 payment-request QR
  — the form most payment QR codes actually take — was silently ignored and the
  camera just kept scanning. It now goes through parseQrValue from lib/sep7.ts,
  which handles both, falling back to the bare-address path.
- Replaced the local isValidStellarAddress (a startsWith + length === 56 check)
  with lib/address.ts's StrKey-based one, so a corrupted address of the right
  shape is rejected rather than accepted and handed to the send form.
- Wired the scanner into (tabs)/send.tsx behind a "Scan QR" action next to
  "Choose contact", which is what makes the acceptance criterion true —
  nothing rendered QrScanner before.

tsc clean; jest 12 suites / 206 tests; expo lint clean.
@Miracle656

Copy link
Copy Markdown
Owner

Merging. The permission handling is the part I'd highlight — distinguishing "undetermined" from "denied" and offering a manual-entry fallback when the camera isn't available is more than most scanner implementations bother with, and it means the screen is still usable if a user declines.

Three changes before merge:

SEP-7 QR codes were being ignored. The scan handler only accepted a bare address:

const addr = data.trim();
if (isValidStellarAddress(addr)) { ... }

So pointing the camera at a web+stellar:pay?destination=…&amount=… code — which is what a payment-request QR actually contains, and what the wallet's own receive screen generates — did nothing at all. Worse, it failed silently: the camera just kept scanning with no feedback. #468 asks for "scanning a Stellar/SEP-7 QR", so this was half the feature.

It now goes through parseQrValue from lib/sep7.ts (added in #518, which handles exactly this bare-address-or-URI branch), falling back to the plain address path.

Validation swapped for the checksum version. The local helper was:

return (v.startsWith('G') || v.startsWith('C')) && v.length === 56;

lib/address.ts landed in #540 with StrKey-backed validation, so a mistyped or corrupted address of the right shape is now rejected instead of being accepted and passed to the send form.

Wired it into the send screen. Nothing rendered QrScanner, so the acceptance criterion couldn't hold. It's now behind a "Scan QR" action beside "Choose contact" on (tabs)/send.tsx, and a successful scan fills the recipient field.

Also dropped the re-added app.jsonmain uses app.config.ts, which already registers the expo-camera plugin along with its permission string ("Veil uses the camera to scan WalletConnect QR codes"), so the native permission prompt is configured.

Verified: tsc --noEmit clean, jest 12 suites / 206 tests, expo lint clean, CI green.

One follow-up: a SEP-7 code carries amount, asset_code and memo alongside the destination, and parseQrValue already returns them — right now only the destination is used. Passing the rest through to prefill the amount and memo fields would make scanning a payment request genuinely one-tap. Small change, worth its own issue.

@Miracle656
Miracle656 merged commit b553f17 into Miracle656:main Jul 30, 2026
10 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

40. QR scanner

3 participants