From 6366ae7a400108b3906220311403ca8f400fd531 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 21:14:51 +0000 Subject: [PATCH] Clear the cached initial URL after handling a deep link expo-linking caches the URL that launched the app and replays it to every new `useLinkingURL`/`getLinkingURL` caller. That means an account switch (which remounts the tree via `key={currentAccount?.did}`) or a runtime reload - e.g. the 15-minute-minimize OTA reload - would hand us the same link again and re-run the intent, re-opening a composer or a chat invite dialog the user already dealt with. `useIntentHandler` is the last consumer of the launch URL (the landing entry check gates the splash, and the age assurance provider and `RoutesContainer` both read it while rendering, before this effect runs), so clear it there once the URL has been handled. `clearInitialURL` is a no-op on web, and it only touches expo-linking's own cache - react navigation's deep link routing reads react-native's `getInitialURL`, so it is unaffected. This also covers the case the TODO in `useOTAUpdates` was waiting on: by the time the pull request deployment reloads, the URL that triggered the `apply-ota` intent is already cleared, so drop the commented-out call. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01BNf6FLwWwwYjoC7ZjbDnEb --- src/lib/hooks/useIntentHandler.ts | 13 ++++++++++++- src/lib/hooks/useOTAUpdates.ts | 6 ------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/lib/hooks/useIntentHandler.ts b/src/lib/hooks/useIntentHandler.ts index 71a3437ad2..428ed39f81 100644 --- a/src/lib/hooks/useIntentHandler.ts +++ b/src/lib/hooks/useIntentHandler.ts @@ -109,7 +109,18 @@ export function useIntentHandler() { if (previousIntentUrl === incomingUrl) { return } - handleIncomingURL(incomingUrl) + handleIncomingURL(incomingUrl).finally(() => { + /* + * expo-linking caches the URL that launched the app and replays it to + * every new `useLinkingURL`/`getLinkingURL` caller, so an account + * switch (which remounts the tree, see `key={currentAccount?.did}` in + * `App.native.tsx`) or a runtime reload would hand us the same link + * again. Drop it now that it's been handled - this hook is the last + * consumer of the launch URL, everything else reads it while + * rendering, before this effect runs. No-op on web. + */ + Linking.clearInitialURL() + }) previousIntentUrl = incomingUrl } }, [ diff --git a/src/lib/hooks/useOTAUpdates.ts b/src/lib/hooks/useOTAUpdates.ts index 143eb5c619..e628664b82 100644 --- a/src/lib/hooks/useOTAUpdates.ts +++ b/src/lib/hooks/useOTAUpdates.ts @@ -177,12 +177,6 @@ export function useApplyPullRequestOTAUpdate() { updateId: fetchedUpdate.manifest.id, }) try { - /* - * TODO: once expo-linking is upgraded to >= 57, enable this so the - * re-delivered initial URL doesn't trigger a redundant silent check - * after the reload. - */ - // Linking.clearInitialURL() await reloadAsync({ reloadScreenOptions: splash(t.scheme), })