Skip to content

fix: don't re-request contacts permission on focus/resume - #2623

Open
kaladivo wants to merge 18 commits into
mainfrom
fix/pr2608-contacts-permission-prompt
Open

fix: don't re-request contacts permission on focus/resume#2623
kaladivo wants to merge 18 commits into
mainfrom
fix/pr2608-contacts-permission-prompt

Conversation

@kaladivo

Copy link
Copy Markdown
Collaborator

Found while reviewing #2608.

Bug

useContactListSelectLifecycle was changed to run importContactsFromPhoneActionAtom from useOnFocusAndAppState — i.e. on every screen focus and every app foregrounding (the app-state branch fires even when the screen isn't focused).

That action chains into loadAndNormalizeContactsFromDeviceActionAtomloadContactsFromDeviceActionAtomgetContactsAndTryToResolveThePermissionsAlongTheWay()areContactsPermissionsGranted(), which calls requestPermissionsAsync() whenever permission is not granted but canAskAgain is true.

User-visible consequence: a user who denied the contacts permission once gets the OS contacts permission prompt re-shown on every focus/resume of the Contact preferences screen. Additionally every focus/resume triggered an unthrottled full getContactsAsync() address-book read.

Before #2608 the focus hook only ran the passive checkContactsAccessPrivilegesActionAtom (getPermissionsAsync, never prompts), and importContactsFromPhoneActionAtom had exactly one caller: the explicit "import contacts" button.

Fix

importContactsFromPhoneActionAtom now takes an explicit {requestPermissions: boolean} param:

  • Lifecycle hook (focus / app resume) passes requestPermissions: false — it first checks the passive areContactsPermissionsAlreadyGranted() and skips loading entirely when access isn't already granted. checkContactsAccessPrivilegesActionAtom still runs via the existing Effect.ensuring, so the UI keeps reflecting the access state exactly as before feat: Features and fixes #2608.
  • The explicit "import from phone" button passes requestPermissions: true, keeping its current prompting behavior.

The flag is required (no default) so any future caller has to state its intent instead of silently inheriting the prompting path. This matches how the other two automatic callers guard themselves — loadContactsFromDeviceActionAtomInAppLoadingTask and refreshDeviceContactsSnapshotActionAtom both gate on areContactsPermissionsAlreadyGranted().

Verification

pnpm turbo typecheck, format and lint all pass for @vexl-next/mobile-app (only pre-existing unrelated deprecation warnings in lint).

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8521b4b8-e1d2-4d4d-8c9f-30ac532acf8b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pr2608-contacts-permission-prompt

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 25, 2026

Copy link
Copy Markdown

Greptile Summary

The PR separates automatic and user-initiated contact imports so focus/resume refreshes remain passive while the explicit import button may request permission.

  • Adds a required requestPermissions parameter to the contact-import action.
  • Skips automatic contact loading unless permission is already granted.
  • Preserves permission prompting for explicit user-initiated imports.

Confidence Score: 5/5

The PR appears safe to merge, with automatic refreshes now avoiding permission prompts while explicit imports retain their intended behavior.

The passive path checks existing permission before entering the contact loader, the explicit button retains the request-capable path, and the existing loader deduplicates overlapping focus and foreground refreshes.

Important Files Changed

Filename Overview
apps/mobile/src/components/ContactPreferencesFlow/components/ContactListSelect/atom.ts Adds an explicit permission-request policy and prevents automatic imports when contacts access is not already granted.
apps/mobile/src/components/ContactPreferencesFlow/components/ContactListSelect/components/ContactsListEmpty.tsx Marks the explicit import-button action as permitted to request contacts access.
apps/mobile/src/components/ContactPreferencesFlow/components/ContactListSelect/hooks/useContactListSelectLifecycle.ts Marks focus and foreground-triggered imports as passive so they do not re-prompt users.

Sequence Diagram

sequenceDiagram
  participant L as Focus / app resume
  participant B as Import button
  participant I as Contact import action
  participant P as Permission API
  participant C as Contact loader

  L->>I: requestPermissions: false
  I->>P: Check existing permission
  alt Permission already granted
    I->>C: Load and normalize contacts
  else Permission not granted
    I-->>L: Skip import without prompting
  end

  B->>I: requestPermissions: true
  I->>C: Load and normalize contacts
  C->>P: Resolve/request permission if needed
Loading

Reviews (1): Last reviewed commit: "fix: do not re-request contacts permissi..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e6fe94f6de

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const permissionsAlreadyGranted = yield* _(
areContactsPermissionsAlreadyGranted()
)
if (!permissionsAlreadyGranted) return false

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep a user-triggered import path for nonempty lists

When permission is denied but canAskAgain remains true and the user already has manual or VCF-imported contacts, this return leaves no way to request access again: ContactsListEmpty exposes the requestPermissions: true action only when normalizedContacts is empty (ContactListSelect/index.tsx:105-132), while ContactsAccessPrivilegesInfoBanner appears only when canAskAgain is false. In that state every focus silently returns here, so importing phone contacts requires finding the OS settings outside this flow; expose an explicit user-initiated import action for the nonempty-list state as well.

Useful? React with 👍 / 👎.

@github-actions

Copy link
Copy Markdown

📱 Preview on the staging app

Open Vexl (stage) → Account → Scan QR code and scan this:

PR preview QR code

Preview link: stagingapp.vexl.it://link/?type=load-pr-preview&channel=pr-2623&version=1.44.2

Channel pr-2623
Commit e6fe94f
Runtime 877ab8d4b6cd1b3aa836dbda494b4c893150919e, b5c07c542487bc7e92539b0b09860763590fefd5
Dashboard update group

The preview only loads into staging builds with a matching runtime — if this PR changes native code, ship a new staging build first. To go back to the staging channel: debug screen → "Clear PR preview".

@kaladivo
kaladivo changed the base branch from feat-features_and_fixes to main July 27, 2026 13:56
@kaladivo kaladivo moved this to Todo in Development Kanban Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

2 participants