| author | |
| committer | |
| log | 0ec2b08a9a1db6fa35fab8d505b5bdc2d3709a8a |
| tree | 5cecd8fc10abd8a9d1045c0c4430061b07cf5a9e |
| parent | 8c18b5bb21a5349fbadba8c77456ea34c41b7339 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>3 files changed, 46 insertions(+), 8 deletions(-)
apps/mobile/src/App.tsx+2-8| ... | ... | @@ -22,6 +22,7 @@ import { RootStack } from "./Stack"; |
| 22 | 22 | import { appAtomRegistry } from "./state/atom-registry"; |
| 23 | 23 | import { OverlayPortalHost } from "./components/OverlayPortal"; |
| 24 | 24 | import { appBlurTargetRef } from "./lib/appBlurTarget"; |
| 25 | import { shouldHandleAppLink } from "./lib/appLinking"; | |
| 25 | 26 | import { useMobileNavigationTheme } from "./lib/useMobileNavigationTheme"; |
| 26 | 27 | |
| 27 | 28 | import { SubscriptionUsageCoordinator } from "./widgets/SubscriptionUsageCoordinator"; |
| ... | ... | @@ -40,14 +41,7 @@ const appLinking = { |
| 40 | 41 | prefixes: [Linking.createURL("/"), "t3code://", "t3code-dev://", "t3code-preview://"], |
| 41 | 42 | // Keep the compact thread list available beneath a directly opened thread. |
| 42 | 43 | config: { initialRouteName: "Home" }, |
| 43 | // The Expo dev client launches the app via | |
| 44 | // <scheme>://expo-development-client/?url=<packager> — that URL addresses | |
| 45 | // the launcher, not app navigation. Without this filter it falls through | |
| 46 | // to the NotFound wildcard route on every dev launch. | |
| 47 | // expo-sharing uses a private lifecycle URL only to wake the app. The | |
| 48 | // persisted share inbox below owns navigation once the payload is durable. | |
| 49 | filter: (url: string) => | |
| 50 | !url.includes("expo-development-client") && !url.includes("://expo-sharing"), | |
| 44 | filter: shouldHandleAppLink, | |
| 51 | 45 | }; |
| 52 | 46 | |
| 53 | 47 | const Navigation = createStaticNavigation(RootStack); |
apps/mobile/src/lib/appLinking.test.ts created+27| ... | ... | @@ -0,0 +1,27 @@ |
| 1 | import { describe, expect, it } from "vite-plus/test"; | |
| 2 | ||
| 3 | import { shouldHandleAppLink } from "./appLinking"; | |
| 4 | ||
| 5 | describe("shouldHandleAppLink", () => { | |
| 6 | it.each(["t3code://", "t3code:///", "t3code-dev://", "t3code-preview://"])( | |
| 7 | "ignores scheme-only URL %s", | |
| 8 | (url) => { | |
| 9 | expect(shouldHandleAppLink(url)).toBe(false); | |
| 10 | }, | |
| 11 | ); | |
| 12 | ||
| 13 | it.each([ | |
| 14 | "t3code://threads/env-1/thread-1", | |
| 15 | "t3code://pair?pairingUrl=x", | |
| 16 | "t3code-dev://settings/usage?tab=limits", | |
| 17 | ])("handles path-bearing URL %s", (url) => { | |
| 18 | expect(shouldHandleAppLink(url)).toBe(true); | |
| 19 | }); | |
| 20 | ||
| 21 | it.each(["t3code://expo-development-client/?url=x", "t3code://expo-sharing/anything"])( | |
| 22 | "ignores lifecycle URL %s", | |
| 23 | (url) => { | |
| 24 | expect(shouldHandleAppLink(url)).toBe(false); | |
| 25 | }, | |
| 26 | ); | |
| 27 | }); |
apps/mobile/src/lib/appLinking.ts created+17| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | /** | |
| 2 | * The Expo dev client launches the app via | |
| 3 | * <scheme>://expo-development-client/?url=<packager> — that URL addresses | |
| 4 | * the launcher, not app navigation. Without this filter it falls through | |
| 5 | * to the NotFound wildcard route on every dev launch. | |
| 6 | * expo-sharing uses a private lifecycle URL only to wake the app. The | |
| 7 | * persisted share inbox in App.tsx owns navigation once the payload is durable. | |
| 8 | * A scheme-only URL, as sent by iOS dictation keyboards returning to the app, | |
| 9 | * only wakes the app and must not reset navigation to Home. | |
| 10 | */ | |
| 11 | export function shouldHandleAppLink(url: string): boolean { | |
| 12 | return ( | |
| 13 | !url.includes("expo-development-client") && | |
| 14 | !url.includes("://expo-sharing") && | |
| 15 | !/^t3code(-dev|-preview)?:\/*$/.test(url) | |
| 16 | ); | |
| 17 | } |