Skip to content

Show the QR scanner scam warning after the camera permission is granted - #6192

Merged
peachbits merged 4 commits into
developfrom
matthew/scan-scam-warning-permission
Sep 15, 2026
Merged

peachbits merged 4 commits into
developfrom
matthew/scan-scam-warning-permission

Conversation

@peachbits

@peachbits peachbits commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Description

Asana: QR Scanner - Modal disappears after permission grant

ScanModal chose its entire body from one ternary keyed on state.permissions.camera, and requested that permission from its own mount effect. The scam warning lived only in the not-granted branch, alongside the "enable Camera access in Settings" recovery text.

Redux seeds camera: 'denied', so on a fresh install the modal opened straight into the Settings guidance before the user had ever been asked, the OS prompt landed on top of that, and answering it flipped the ternary. The warning's subtree was then unmounted by React rather than through bridge.resolve, so it disappeared without its exit animation — the reported flash. The only way to read the warning at all was to deny the camera.

showScanModal now runs the three steps in sequence, so none of them can overlap:

  1. Request the OS camera permission, with no Edge modal on screen behind it.
  2. If denied or blocked, show the Settings recovery guidance and stop.
  3. On the first camera use only, wait for an explicit acknowledgement of the scam warning.
  4. Only then mount the scanner.

Waiting for the app to return to the foreground keeps the warning from being presented while it is still coming back from the OS prompt. ScanModal is left as just the camera sheet, with no permission branch to swap.

Notes for review:

  • The scam warning is a non-skippable ConfirmContinueModal, so backdrop tap, swipe-down, hardware back and the close button are all disabled — it takes the checkbox and confirm button to dismiss. That also means it passes no onCancel, so EdgeModal ignores Airship's global clear event; the modal handles clear itself rather than outliving a logout and leaving the caller awaiting a promise that never settles.
  • The acknowledgement is persisted per account as cameraScamWarningShown, written after the modal resolves, so a force-quit mid-warning shows it again.
  • ScanModal deliberately does not re-check the permission on foreground to swap back to the recovery UI. That branch swap is the flash bug in reverse.
  • WcConnectionsScene picks up return-type and catch-variable fixes that its removal from the ESLint warnings list now requires.

Verification: tsc, eslint and the full jest suite pass.

Walked through on the iOS simulator and on Android (Pixel 9 Pro, API 36): the OS prompt appears with no Edge modal behind it; on Allow the scam warning appears and stays until the checkbox and Confirm & Finish are tapped; the scanner then opens. With camera access revoked, only the Settings guidance shows -- no scam card. The first-use gate was confirmed by reading cameraScamWarningShown: true back out of the device-local Settings.json after acknowledgement.

The Android pass is the case Jon raised on the ticket -- "Android, if tapping allow, does not allow the modal to persist for the user" -- and the warning does persist here.

Entry points exercised: Side Menu -> Scan QR on both platforms, Send -> Scan on iOS. WalletConnect -> New Connection was not clicked through, though all three now route through the same showScanModal thunk, and an eslint rule prevents any call site from mounting ScanModal directly and skipping the sequencing.

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Requirements

If you have made any visual changes to the GUI. Make sure you have:

  • Tested on iOS device
  • Tested on Android device
  • Tested on small-screen device (iPod Touch)
  • Tested on large-screen device (tablet)

Note

Medium Risk
Changes the shared QR scan flow (permission, first-use safety modal, and persistence) across several entry points; regressions would affect camera access or whether users see the scam warning.

Overview
Fixes the QR scam warning flashing away when users tap Allow on the OS camera prompt. ScanModal no longer requests permission or switches UI based on Redux camera state; it is only the camera sheet once access is granted.

showScanModal in ScanActions is now the required entry point: request camera permission with no Edge modal behind the OS dialog, waitForForeground after the prompt, then either CameraPermissionDeniedModal (Settings guidance only) or, on first use per account, a non-skippable ScanScamWarningModal whose acknowledgement is stored as cameraScamWarningShown, and finally the scanner. Side menu, send address scan, and WalletConnect new connection dispatch showScanModal instead of mounting ScanModal directly; eslint blocks direct ScanModal imports except from ScanActions.

Reviewed by Cursor Bugbot for commit 74dfc47. Bugbot is set up for automated code reviews on this repo. Configure here.

@peachbits
peachbits marked this pull request as ready for review September 3, 2026 21:36
@peachbits
peachbits marked this pull request as draft September 7, 2026 18:09
@peachbits
peachbits marked this pull request as ready for review September 7, 2026 18:45
Tracks whether the QR scanner scam warning has been acknowledged, so it
can be shown once on the first use of the camera.
Splits the scam warning apart from the 'enable Camera access' recovery
guidance so the two have independent triggers and lifecycles.

The scam warning is a non-skippable ConfirmContinueModal, so it stays up
until the user ticks the checkbox and confirms. That also leaves it with
no onCancel, which means EdgeModal ignores Airship's global clear event,
so it handles 'clear' itself rather than outliving a logout.
ScanModal chose its whole body from one ternary keyed on the redux camera
permission, and requested that permission from its own mount effect. The
scam warning lived only in the not-granted branch, so on a fresh install
the modal opened straight into 'enable Camera access in Settings' before
the user had ever been asked, the OS prompt landed on top of it, and
answering it flipped the ternary. The warning's subtree was unmounted by
React rather than through bridge.resolve, so it vanished without its exit
animation -- the reported flash. The only way to read the warning was to
deny the camera.

showScanModal now runs the three steps in order, so none of them overlap:
request the permission with no Edge modal on screen behind it, show the
Settings recovery guidance if it was denied or blocked, then on the first
camera use only, wait for an explicit acknowledgement of the scam warning
before mounting the scanner. Waiting for the foreground keeps the warning
from being presented while the app is still returning from the OS prompt.

ScanModal is left as just the camera sheet, with no permission branch to
swap. WcConnectionsScene picks up the return-type and catch-variable
fixes its removal from the ESLint warnings list now requires.
Answering the OS prompt returns the app to the foreground, and the
recovery modal is as capable of flashing past during that transition as
the warning is -- so wait for the foreground on both branches, not just
the granted one.

Bound that wait with a timeout, so an app that never returns to the
foreground cannot leave the caller awaiting a promise that never settles,
and drop the AppState listener once it has.

Recording the acknowledgement is best-effort: a logout between the
acknowledgement and the write would reject, and re-showing the warning
next time beats an error banner. In WcConnectionsScene that rejection had
nowhere to go at all.

Restrict importing ScanModal outside ScanActions, so a future call site
cannot mount the scanner directly and skip both the permission request
and the warning.
@peachbits
peachbits force-pushed the matthew/scan-scam-warning-permission branch from f1cc5a4 to 74dfc47 Compare September 15, 2026 22:36

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

@peachbits
peachbits merged commit 25ef9f2 into develop Sep 15, 2026
7 checks passed
@peachbits
peachbits deleted the matthew/scan-scam-warning-permission branch September 15, 2026 22:37
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.

2 participants