Skip to content

Conversation

@DSingh0304
Copy link

@DSingh0304 DSingh0304 commented Nov 12, 2025

Proposed changes

This PR fixes an issue where the iOS app hangs when users attempt to change their passcode in the Screen Lock settings. The hang occurs due to two modals (authentication modal and passcode change modal) opening back-to-back, causing an iOS UI thread conflict.

The fix adds a 300ms delay between the authentication modal closing and the passcode change modal opening, allowing the first modal to fully dismiss before the second one appears. Additionally, try-catch blocks have been added to gracefully handle cases where users cancel authentication.

Issue(s)

Fixes #6313

How to test or reproduce

To reproduce the original bug (on iOS):

  1. Go to Settings → Security & Privacy → Screen Lock
  2. Enable Screen Lock and set a passcode
  3. Tap "Change Passcode"
  4. Authenticate with biometric or existing passcode
  5. App would hang at this point

To verify the fix:

  1. Follow the same steps above
  2. After authentication, there should be a brief (300ms) delay
  3. The passcode change modal should appear without any hang
  4. Complete the passcode change successfully

Testing:

  • Run tests: yarn test app/views/ScreenLockConfigView.test.tsx
  • All 4 unit tests pass, covering:
    • 300ms delay between modals
    • Authentication cancellation handling
    • Direct passcode change when autoLock is disabled
    • Delay duration verification

Files modified:

  • app/views/ScreenLockConfigView.tsx - Added delay and error handling to changePasscode method
  • app/views/SecurityPrivacyView.tsx - Added delay and error handling to navigateToScreenLockConfigView method
  • app/views/ScreenLockConfigView.test.tsx - Added unit tests for the fix

Screenshots

N/A - This is a timing/modal transition fix with no visual UI changes

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change which improves a current function)
  • New feature (non-breaking change which adds functionality)
  • Documentation update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA
  • Lint and unit tests pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works (if applicable)
  • I have added necessary documentation (if applicable)
  • Any dependent changes have been merged and published in downstream modules

Further comments

This is a minimal, targeted fix that addresses the iOS-specific modal timing issue. The 300ms delay is a standard duration used in React Native for modal transitions and should be imperceptible to users while preventing the UI thread hang.

Unit tests have been added to verify:

  • The 300ms delay is properly applied between authentication and passcode change
  • Authentication cancellation is handled gracefully without proceeding to passcode change
  • The flow works correctly when autoLock is disabled (no authentication required)
  • The delay duration constant is correct

Testing environment:

  • ✅ ESLint: Passed
  • ✅ Prettier: Passed
  • ✅ Unit Tests: 4/4 passed
  • ✅ iOS Simulator: Tested and verified - no hang occurs when changing passcode
  • ✅ Android build: Successful

Summary by CodeRabbit

  • Bug Fixes

    • Added a short delay to prevent iOS modal hangs when authentication and passcode prompts appear back-to-back.
    • Authentication failures or cancellations now abort navigation and passcode changes to avoid unintended actions.
    • Improved timing and sequencing of local authentication and security screens for more reliable transitions.
  • Chores

    • Introduced a 300ms modal-transition delay constant to centralize timing behavior.
  • Tests

    • Added tests covering authentication success/failure flows, timing of the modal delay, and passcode-change behavior.

@CLAassistant
Copy link

CLAassistant commented Nov 12, 2025

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 12, 2025

Walkthrough

Wraps local authentication calls in try/catch in two views; on successful authentication waits MODAL_TRANSITION_DELAY_MS (300ms) before proceeding; on failure/cancel returns early. Adds MODAL_TRANSITION_DELAY_MS constant and new tests for timing, cancellation, and autoLock behavior. Exports ScreenLockConfigView publicly.

Changes

Cohort / File(s) Summary
Views — auth timing & error handling
app/views/ScreenLockConfigView.tsx, app/views/SecurityPrivacyView.tsx
Wrap handleLocalAuthentication(true) in try/catch; on success await MODAL_TRANSITION_DELAY_MS before continuing; on failure/cancel return early and do not proceed with navigation or changePasscode. Also export ScreenLockConfigView publicly.
Constants
app/lib/constants/localAuthentication.ts
Add and export MODAL_TRANSITION_DELAY_MS = 300 with comment describing its purpose to prevent iOS modal UI thread hangs during back-to-back modals.
Tests
app/views/ScreenLockConfigView.test.tsx
Add tests that mock localAuthentication and DB: verify 300ms delay after successful auth before changePasscode, verify rejection/cancellation prevents changePasscode, and verify flow when autoLock is false. Uses fake timers.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant View as SecurityPrivacyView / ScreenLockConfigView
    participant Auth as LocalAuthentication
    participant Flow as Navigation / PasscodeFlow

    User->>View: Request change/set passcode
    View->>Auth: handleLocalAuthentication(true)
    rect rgb(220,240,255)
      Note over View,Auth: try/catch around auth call
      Auth-->>View: Success
      View->>View: wait MODAL_TRANSITION_DELAY_MS (300ms)
      Note over View: Prevent iOS modal overlap before next modal
      View->>Flow: navigate / changePasscode
    end
    rect rgb(255,235,235)
      Auth-->>View: Failure / Cancel
      Note over View: return early — abort navigation / passcode change
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Check try/catch correctness and that promise rejections are handled.
  • Verify the delay constant is imported/used consistently.
  • Inspect new tests for reliable fake-timer usage and proper mocks of localAuthentication and DB.
  • Confirm the exported ScreenLockConfigView change doesn't conflict with existing imports.

Suggested reviewers

  • diegolmello

Poem

🐰 I hopped where modals nearly crashed,
I paused a beat so screens unlash.
Three hundred ticks of gentle hush,
Passcodes change without the rush. ✨

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change—fixing an app hang when changing passcode on iOS by addressing modal conflict issues.
Linked Issues check ✅ Passed All coding requirements from #6313 are met: 300ms delay between modals added in both ScreenLockConfigView and SecurityPrivacyView, error handling for cancellation implemented, and comprehensive tests added.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the iOS passcode hang: delay constant, modal transition delay logic, error handling, and related tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f23c341 and 4cb54a7.

📒 Files selected for processing (4)
  • app/lib/constants/localAuthentication.ts (1 hunks)
  • app/views/ScreenLockConfigView.test.tsx (1 hunks)
  • app/views/ScreenLockConfigView.tsx (3 hunks)
  • app/views/SecurityPrivacyView.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
app/views/ScreenLockConfigView.test.tsx (1)
app/views/ScreenLockConfigView.tsx (1)
  • ScreenLockConfigView (307-307)
app/views/SecurityPrivacyView.tsx (2)
app/lib/methods/helpers/localAuthentication.ts (1)
  • handleLocalAuthentication (113-125)
app/lib/constants/localAuthentication.ts (1)
  • MODAL_TRANSITION_DELAY_MS (16-16)
app/views/ScreenLockConfigView.tsx (2)
app/lib/methods/helpers/localAuthentication.ts (1)
  • handleLocalAuthentication (113-125)
app/lib/constants/localAuthentication.ts (1)
  • MODAL_TRANSITION_DELAY_MS (16-16)
🔇 Additional comments (4)
app/lib/constants/localAuthentication.ts (1)

15-16: Centralized modal delay constant.

Moving this delay into a shared constant keeps every caller aligned and makes future tuning trivial.

app/views/SecurityPrivacyView.tsx (1)

61-70: Guarded navigation on auth failure.

The try/catch plus shared delay avoids stacking modals and cleanly bails when auth is cancelled—nice containment of the original hang.

app/views/ScreenLockConfigView.tsx (1)

135-143: Modal sequencing safeguard.

The post-auth delay with graceful early return keeps the passcode flow stable and prevents the iOS hang we were seeing.

app/views/ScreenLockConfigView.test.tsx (1)

37-61: Regression test covers async gap.

Exercising the real component instance and waiting on fake timers ensures we won’t regress the modal spacing behavior.


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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
app/views/ScreenLockConfigView.tsx (2)

135-143: Extract duplicated authentication pattern to a shared utility.

The try-catch-delay pattern around handleLocalAuthentication is duplicated between this file (lines 135-143) and SecurityPrivacyView.tsx (lines 62-69). Consider extracting this to a shared utility function to reduce duplication and ensure consistent behavior.

Example refactor in app/lib/methods/helpers/localAuthentication.ts:

export const handleLocalAuthenticationWithModalDelay = async (canCloseModal = false): Promise<boolean> => {
  try {
    await handleLocalAuthentication(canCloseModal);
    // Add a small delay to ensure the first modal is fully closed before opening the next one
    // This prevents the app from hanging on iOS when two modals open back-to-back
    await new Promise(resolve => setTimeout(resolve, 300));
    return true;
  } catch {
    // User cancelled or authentication failed
    return false;
  }
};

Then simplify both callsites:

-    try {
-      await handleLocalAuthentication(true);
-      // Add a small delay to ensure the first modal is fully closed before opening the next one
-      // This prevents the app from hanging on iOS when two modals open back-to-back
-      await new Promise(resolve => setTimeout(resolve, 300));
-    } catch {
-      // User cancelled or authentication failed
-      return;
-    }
+    const authenticated = await handleLocalAuthenticationWithModalDelay(true);
+    if (!authenticated) {
+      return;
+    }

140-142: Consider logging authentication failures for debugging.

The empty catch block silently handles all errors from handleLocalAuthentication. While user cancellation is expected, other authentication errors might be valuable for debugging. Consider logging non-cancellation errors.

     } catch (error) {
       // User cancelled or authentication failed
+      if (error && error.code !== 'USER_CANCEL') {
+        console.warn('Authentication error:', error);
+      }
       return;
     }
app/views/SecurityPrivacyView.tsx (1)

62-69: Apply the same refactoring as ScreenLockConfigView.tsx.

This code duplicates the authentication-with-delay pattern from ScreenLockConfigView.tsx (lines 135-143). Once you extract the shared utility function suggested in that file, apply the same refactor here.

-    try {
-      await handleLocalAuthentication(true);
-      // Add a small delay to prevent modal conflicts on iOS
-      await new Promise(resolve => setTimeout(resolve, 300));
-    } catch {
-      // User cancelled or authentication failed
-      return;
-    }
+    const authenticated = await handleLocalAuthenticationWithModalDelay(true);
+    if (!authenticated) {
+      return;
+    }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3532447 and 27bfc65.

📒 Files selected for processing (2)
  • app/views/ScreenLockConfigView.tsx (1 hunks)
  • app/views/SecurityPrivacyView.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
app/views/SecurityPrivacyView.tsx (1)
app/lib/methods/helpers/localAuthentication.ts (1)
  • handleLocalAuthentication (113-125)
app/views/ScreenLockConfigView.tsx (1)
app/lib/methods/helpers/localAuthentication.ts (1)
  • handleLocalAuthentication (113-125)
🔇 Additional comments (1)
app/views/ScreenLockConfigView.tsx (1)

139-139: Verify that 300ms delay is sufficient across iOS versions.

The 300ms delay is crucial for preventing the iOS hang. Please verify that this duration is adequate across different iOS versions and device configurations (especially older devices with slower UI rendering).

Consider testing on:

  • iOS 15, 16, 17, and 18
  • Older devices (iPhone 8, iPhone X) alongside newer models
  • Low-power mode scenarios

If 300ms proves insufficient in some cases, consider increasing to 500ms or making it configurable per-device.

@DSingh0304 DSingh0304 force-pushed the fix/passcode-change-hang-ios branch from 27bfc65 to a86091f Compare November 12, 2025 17:53
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
app/views/ScreenLockConfigView.test.tsx (2)

29-40: Extract duplicated simulation function.

The simulatePasscodeChange function is duplicated identically across three tests. This duplication should be eliminated.

However, this becomes moot if you address the critical issue above and test the actual component instead of a simulation function.

Also applies to: 65-76, 94-104


48-49: Consider using advanceTimersByTime(300) for precision.

Using jest.runAllTimers() advances all pending timers regardless of their duration. For more precise verification of the 300ms delay, consider:

-// Fast-forward timers to simulate the 300ms delay
-jest.runAllTimers();
+// Fast-forward timers by exactly 300ms to verify the delay
+jest.advanceTimersByTime(300);

This makes it explicit that you're testing a 300ms delay specifically.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 27bfc65 and a86091f.

📒 Files selected for processing (3)
  • app/views/ScreenLockConfigView.test.tsx (1 hunks)
  • app/views/ScreenLockConfigView.tsx (1 hunks)
  • app/views/SecurityPrivacyView.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/views/ScreenLockConfigView.tsx
  • app/views/SecurityPrivacyView.tsx

- Add 300ms delay between authentication and passcode change modals
- Add try-catch to handle authentication cancellation gracefully
- Prevents iOS UI thread hang when two modals open back-to-back

Fixes RocketChat#6313
@DSingh0304 DSingh0304 force-pushed the fix/passcode-change-hang-ios branch from f23c341 to 4cb54a7 Compare November 12, 2025 18:45
@DSingh0304
Copy link
Author

Hey maintainers, I would love it if someone could review the code and tell me if anything needs to be changed.

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.

bug: App Stops working when change passcode on iPhone

2 participants