| 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,6 +22,7 @@ import { RootStack } from "./Stack"; |
| 22 | import { appAtomRegistry } from "./state/atom-registry"; | 22 | import { appAtomRegistry } from "./state/atom-registry"; |
| 23 | import { OverlayPortalHost } from "./components/OverlayPortal"; | 23 | import { OverlayPortalHost } from "./components/OverlayPortal"; |
| 24 | import { appBlurTargetRef } from "./lib/appBlurTarget"; | 24 | import { appBlurTargetRef } from "./lib/appBlurTarget"; |
| 25 | import { shouldHandleAppLink } from "./lib/appLinking"; | ||
| 25 | import { useMobileNavigationTheme } from "./lib/useMobileNavigationTheme"; | 26 | import { useMobileNavigationTheme } from "./lib/useMobileNavigationTheme"; |
| 26 | 27 | ||
| 27 | import { SubscriptionUsageCoordinator } from "./widgets/SubscriptionUsageCoordinator"; | 28 | import { SubscriptionUsageCoordinator } from "./widgets/SubscriptionUsageCoordinator"; |
| ... | @@ -40,14 +41,7 @@ const appLinking = { | ... | @@ -40,14 +41,7 @@ const appLinking = { |
| 40 | prefixes: [Linking.createURL("/"), "t3code://", "t3code-dev://", "t3code-preview://"], | 41 | prefixes: [Linking.createURL("/"), "t3code://", "t3code-dev://", "t3code-preview://"], |
| 41 | // Keep the compact thread list available beneath a directly opened thread. | 42 | // Keep the compact thread list available beneath a directly opened thread. |
| 42 | config: { initialRouteName: "Home" }, | 43 | config: { initialRouteName: "Home" }, |
| 43 | // The Expo dev client launches the app via | 44 | filter: shouldHandleAppLink, |
| 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"), | ||
| 51 | }; | 45 | }; |
| 52 | 46 | ||
| 53 | const Navigation = createStaticNavigation(RootStack); | 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 | } | ||