Skip to content

fix(amicus): silent fallback for AmicusFab ErrorBoundary + provider-tree smoke test#1558

Merged
CraigBuckmaster merged 1 commit into
masterfrom
claude/amicus-fab-silent-fallback
Apr 20, 2026
Merged

fix(amicus): silent fallback for AmicusFab ErrorBoundary + provider-tree smoke test#1558
CraigBuckmaster merged 1 commit into
masterfrom
claude/amicus-fab-silent-fallback

Conversation

@CraigBuckmaster
Copy link
Copy Markdown
Owner

Summary

Follow-up polish to #1557. The architectural move (<AmicusFab /> inside <NavigationContainer>) and the defensive try/catch in AmicusFab.tsx both landed in #1557. This PR adds two pieces that were outlined in the original plan but deferred:

1. Silent fallback for the AmicusFab ErrorBoundary

The default ErrorBoundary fallback is a flex: 1 centered "Something went wrong" view with a Retry button. If the FAB ever errors during render — say, a future regression puts it outside NavigationContainer again — that full-screen fallback would displace real UI. A FAB failure should be invisible to the user. Telemetry still captures it via logger.error in componentDidCatch.

  • ErrorBoundary.tsx — extend Props with an optional renderFallback render-prop. When provided, it is used instead of the default centered fallback. Default behavior and all existing call sites unchanged.
  • AmicusFabErrorFallback.tsx — new component that renders null.
  • App.tsx — the AmicusFab ErrorBoundary now uses renderFallback={AmicusFabErrorFallback} in place of fallbackMessage.

2. Provider-tree smoke test

app/__tests__/App.providerTree.test.tsx — mounts <App /> with the real @react-navigation/native (not the non-throwing jest.setup.js stub), waits for dbStatus -> 'ready', and asserts the full tree composes without throwing. This is the test that would have caught #1557's root cause before it reached TestFlight.

Heavy / native-bound / out-of-scope dependencies (RootNavigator, stores, DB init, Sentry, the notification router, the crash handler) are mocked. The NavigationContainer composition itself runs for real — that's the point.

Validation

  • npx tsc --noEmit — clean
  • npm run lint — clean
  • npm test — 466 suites, 3433 tests pass (1 new smoke test, no regressions)
  • Targeted: npx jest --testPathPattern "AmicusFab" — 9 tests pass
  • Targeted: npx jest --testPathPattern "App.providerTree" — 1 test passes

What this PR does NOT do

  • No changes to AmicusFab.tsx — the defensive try/catch from fix(amicus): prevent AmicusFab crash when mounted outside NavigationContainer #1557 stays as belt-and-suspenders regression prevention.
  • No changes to AmicusPeekSheet.tsx — its useNavigationState call now succeeds naturally because AmicusFab (and therefore the sheet, rendered as its child) is inside NavigationContainer.
  • No changes to Sentry, OTA, DB, or navigation internals.
  • No new runtime dependencies.

Follow-ups that remain open

  1. Audit other components for unguarded useNavigation() / useNavigationState() / useRoute() calls.
  2. Document the "hook must be inside its required provider" rule in DEV_GUIDE.md with a code-review checklist item.

…ree smoke test

Follow-up polish to #1557. The architectural move (AmicusFab inside
NavigationContainer) and the defensive try/catch in AmicusFab.tsx both
landed in #1557. This PR adds two pieces that were outlined in the
original plan but deferred:

- Silent fallback for the ErrorBoundary that wraps <AmicusFab />. The
  default ErrorBoundary fallback is a full-screen "Something went wrong"
  view with a Retry button (flex:1), which would displace real UI if the
  FAB ever errored during render. A FAB failure should be invisible to
  the user — telemetry still captures it via logger.error in
  componentDidCatch. This is done by:
  1. Extending ErrorBoundary Props with an optional `renderFallback`
     render-prop. When provided, it is used INSTEAD of the default
     centered fallback. Default behavior and existing call sites
     unchanged.
  2. Adding AmicusFabErrorFallback, a component that renders null.
  3. Wiring it into AppShell's AmicusFab boundary via `renderFallback`.

- Provider-tree smoke test: mounts <App /> with the real
  @react-navigation/native (not the non-throwing jest.setup.js stub),
  waits for dbStatus -> 'ready', and asserts the full tree composes
  without throwing. This is the test that would have caught #1557's
  root cause before it reached TestFlight. Dependencies that are
  heavy, native-bound, or outside the provider-composition surface
  (RootNavigator, stores, DB init path, Sentry) are mocked; the
  NavigationContainer composition itself runs for real.
@github-actions
Copy link
Copy Markdown

Test Results

✅ All tests passed

Passed Failed Total
Tests ✅ 3433 ❌ 0 3433
Suites ✅ 466 ❌ 0 466

Coverage

Statements Branches Functions Lines

⏱️ Duration: 86.3s

@CraigBuckmaster CraigBuckmaster merged commit 0788712 into master Apr 20, 2026
6 checks passed
@CraigBuckmaster CraigBuckmaster deleted the claude/amicus-fab-silent-fallback branch April 20, 2026 22:44
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