Show the QR scanner scam warning after the camera permission is granted - #6192
Merged
Merged
Conversation
peachbits
marked this pull request as ready for review
September 3, 2026 21:36
peachbits
marked this pull request as draft
September 7, 2026 18:09
peachbits
marked this pull request as ready for review
September 7, 2026 18:45
j0ntz
approved these changes
Sep 10, 2026
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
force-pushed
the
matthew/scan-scam-warning-permission
branch
from
September 15, 2026 22:36
f1cc5a4 to
74dfc47
Compare
There was a problem hiding this comment.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Asana: QR Scanner - Modal disappears after permission grant
ScanModalchose its entire body from one ternary keyed onstate.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 throughbridge.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.showScanModalnow runs the three steps in sequence, so none of them can overlap: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.
ScanModalis left as just the camera sheet, with no permission branch to swap.Notes for review:
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 noonCancel, soEdgeModalignores Airship's globalclearevent; the modal handlesclearitself rather than outliving a logout and leaving the caller awaiting a promise that never settles.cameraScamWarningShown, written after the modal resolves, so a force-quit mid-warning shows it again.ScanModaldeliberately does not re-check the permission on foreground to swap back to the recovery UI. That branch swap is the flash bug in reverse.WcConnectionsScenepicks 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: trueback out of the device-localSettings.jsonafter 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
showScanModalthunk, and an eslint rule prevents any call site from mountingScanModaldirectly and skipping the sequencing.CHANGELOG
Does this branch warrant an entry to the CHANGELOG?
Dependencies
noneRequirements
If you have made any visual changes to the GUI. Make sure you have:
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.
ScanModalno longer requests permission or switches UI based on Redux camera state; it is only the camera sheet once access is granted.showScanModalinScanActionsis now the required entry point: request camera permission with no Edge modal behind the OS dialog,waitForForegroundafter the prompt, then eitherCameraPermissionDeniedModal(Settings guidance only) or, on first use per account, a non-skippableScanScamWarningModalwhose acknowledgement is stored ascameraScamWarningShown, and finally the scanner. Side menu, send address scan, and WalletConnect new connection dispatchshowScanModalinstead of mountingScanModaldirectly; eslint blocks directScanModalimports except fromScanActions.Reviewed by Cursor Bugbot for commit 74dfc47. Bugbot is set up for automated code reviews on this repo. Configure here.