Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions apps/mobile/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { RootStack } from "./Stack";
import { appAtomRegistry } from "./state/atom-registry";
import { OverlayPortalHost } from "./components/OverlayPortal";
import { appBlurTargetRef } from "./lib/appBlurTarget";
import { shouldHandleAppLink } from "./lib/appLinking";
import { useMobileNavigationTheme } from "./lib/useMobileNavigationTheme";

import { SubscriptionUsageCoordinator } from "./widgets/SubscriptionUsageCoordinator";
Expand All @@ -40,14 +41,7 @@ const appLinking = {
prefixes: [Linking.createURL("/"), "t3code://", "t3code-dev://", "t3code-preview://"],
// Keep the compact thread list available beneath a directly opened thread.
config: { initialRouteName: "Home" },
// The Expo dev client launches the app via
// <scheme>://expo-development-client/?url=<packager> — that URL addresses
// the launcher, not app navigation. Without this filter it falls through
// to the NotFound wildcard route on every dev launch.
// expo-sharing uses a private lifecycle URL only to wake the app. The
// persisted share inbox below owns navigation once the payload is durable.
filter: (url: string) =>
!url.includes("expo-development-client") && !url.includes("://expo-sharing"),
filter: shouldHandleAppLink,
};

const Navigation = createStaticNavigation(RootStack);
Expand Down
27 changes: 27 additions & 0 deletions apps/mobile/src/lib/appLinking.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { describe, expect, it } from "vite-plus/test";

import { shouldHandleAppLink } from "./appLinking";

describe("shouldHandleAppLink", () => {
it.each(["t3code://", "t3code:///", "t3code-dev://", "t3code-preview://"])(
"ignores scheme-only URL %s",
(url) => {
expect(shouldHandleAppLink(url)).toBe(false);
},
);

it.each([
"t3code://threads/env-1/thread-1",
"t3code://pair?pairingUrl=x",
"t3code-dev://settings/usage?tab=limits",
])("handles path-bearing URL %s", (url) => {
expect(shouldHandleAppLink(url)).toBe(true);
});

it.each(["t3code://expo-development-client/?url=x", "t3code://expo-sharing/anything"])(
"ignores lifecycle URL %s",
(url) => {
expect(shouldHandleAppLink(url)).toBe(false);
},
);
});
17 changes: 17 additions & 0 deletions apps/mobile/src/lib/appLinking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* The Expo dev client launches the app via
* <scheme>://expo-development-client/?url=<packager> — that URL addresses
* the launcher, not app navigation. Without this filter it falls through
* to the NotFound wildcard route on every dev launch.
* expo-sharing uses a private lifecycle URL only to wake the app. The
* persisted share inbox in App.tsx owns navigation once the payload is durable.
* A scheme-only URL, as sent by iOS dictation keyboards returning to the app,
* only wakes the app and must not reset navigation to Home.
*/
export function shouldHandleAppLink(url: string): boolean {
return (
!url.includes("expo-development-client") &&
!url.includes("://expo-sharing") &&
!/^t3code(-dev|-preview)?:\/*$/.test(url)
);
}
Loading