Files
Your NameandClaude Opus 5 91ce15cba6 fix(links): stop losing the invite between three owners
An invite link could be consumed and then silently dropped, so the join sheet
appeared only sometimes (TODO #148), and the legacy schemes dead-ended on a
blank screen that only a force-stop cleared (TODO #144). Both are deep-link
delivery, so they are fixed together.

#148 - delivery crosses three owners and each lets go before the next has hold:

  1. native LinkModule captures the VIEW intent into `pendingLink`
  2. the RN poller reads it - and getPendingLink() NULLS it on read, so native
     has now forgotten it
  3. the shell stores it, then clears that state and injects
     `if (window.__pearHandleInvite) { … }`

Step 3 is the hole. `webViewReady` means the DOM loaded, not that the bundle has
run, so the guard can be false - and then the injection is a silent no-op with
the URL already gone from native AND from React state. Nothing retries, because
nothing knows anything was lost. main.jsx already buffered a LATER version of
this race (an invite arriving before <App> mounts its listener), but that only
helps once the handler exists.

The injected snippet is now total: deliver if it can, park the URL on
`window.__pearEarlyInvites` if it cannot, and the bundle drains the park the
moment it defines the handler. No polling, no retry state machine.

#144 - app/join.tsx rendered an empty View, waited 2s, emitted a `pearLink`
DeviceEvent and never navigated. Two things wrong: NOTHING LISTENS FOR
`pearLink` - there is not one addListener for it in the app, so the route
delivered nothing at all and the URL only ever arrived via the native queue -
and it never navigated away, so on a cold start it was the only stack entry and
Back exited the app. The route now replaces straight to index, whose poller does
the actual delivery.

313 unit tests, 10 new. They do not test a string: they EVALUATE the injected
snippet against a stub window in both orders, and the bundle-ready-second order
is the bug. Confirmed by reverting the injection to its old form, where 6 of
them fail including the regression itself. Also covers order preservation, no
double delivery on a second drain, and that a URL carrying quotes, backslashes,
newlines or U+2028/U+2029 cannot break out of the snippet - it arrives from a
link someone else sent, so that is script injection into our own WebView.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HG8ayyquJuDMPSVLKDQKVh
2026-07-27 09:02:10 -05:00
..
2026-02-25 13:58:53 -06:00