Skip to content

Commit 7f11739

Browse files
aapelivclaude
andcommitted
Mobile: preserve query params on deep links so email/QR tokens aren't dropped
Universal/app links for the whole couchers.org domain open in-app, but the catch-all route rebuilt the path from slug segments only and silently dropped the query string, so token-bearing links (account deletion confirmation, recovery, unsubscribe, postal verification, strong verification) couldn't complete. Forward the query string through the catch-all and add dedicated top-level routes for the links that must work while logged out. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent ac53a59 commit 7f11739

5 files changed

Lines changed: 64 additions & 2 deletions

File tree

app/mobile/app/(tabs)/[...slug].tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import WebEmbed from "@/components/WebEmbed";
44
import { detailRouteOriginRef } from "@/state/webViewState";
55

66
export default function CatchAllScreen() {
7-
const { slug } = useLocalSearchParams<{ slug?: string[] }>();
7+
const { slug, ...rest } = useLocalSearchParams<{ slug?: string[] }>();
88
const router = useRouter();
99

1010
// slug is undefined/empty when nav state is restored without a valid path
@@ -14,7 +14,17 @@ export default function CatchAllScreen() {
1414
return <Redirect href="/(tabs)/dashboard" />;
1515
}
1616

17-
const path = `/${slug.join("/")}`;
17+
// Forward query params the deep link carried (e.g. ?token=, ?code=); the slug
18+
// segments only cover the path, so without this they'd be silently dropped.
19+
const query = rest as Record<string, string | string[]>;
20+
const queryString = Object.entries(query)
21+
.flatMap(([key, value]) =>
22+
(Array.isArray(value) ? value : [value]).map(
23+
(v) => `${encodeURIComponent(key)}=${encodeURIComponent(v)}`,
24+
),
25+
)
26+
.join("&");
27+
const path = `/${slug.join("/")}${queryString ? `?${queryString}` : ""}`;
1828
return (
1929
<WebEmbed
2030
key={path}

app/mobile/app/delete-account.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { useLocalSearchParams } from "expo-router";
2+
3+
import WebEmbed from "@/components/WebEmbed";
4+
5+
export default function DeleteAccountScreen() {
6+
const { token } = useLocalSearchParams<{ token?: string }>();
7+
const path = token
8+
? `/delete-account?token=${encodeURIComponent(token)}`
9+
: "/delete-account";
10+
11+
return <WebEmbed path={path} />;
12+
}

app/mobile/app/quick-link.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useLocalSearchParams } from "expo-router";
2+
3+
import WebEmbed from "@/components/WebEmbed";
4+
5+
export default function QuickLinkScreen() {
6+
const { payload, sig } = useLocalSearchParams<{
7+
payload?: string;
8+
sig?: string;
9+
}>();
10+
const path =
11+
payload && sig
12+
? `/quick-link?payload=${encodeURIComponent(payload)}&sig=${encodeURIComponent(sig)}`
13+
: "/quick-link";
14+
15+
return <WebEmbed path={path} />;
16+
}

app/mobile/app/recover-account.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { useLocalSearchParams } from "expo-router";
2+
3+
import WebEmbed from "@/components/WebEmbed";
4+
5+
export default function RecoverAccountScreen() {
6+
const { token } = useLocalSearchParams<{ token?: string }>();
7+
const path = token
8+
? `/recover-account?token=${encodeURIComponent(token)}`
9+
: "/recover-account";
10+
11+
return <WebEmbed path={path} />;
12+
}

app/mobile/app/verify-postal.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { useLocalSearchParams } from "expo-router";
2+
3+
import WebEmbed from "@/components/WebEmbed";
4+
5+
export default function VerifyPostalScreen() {
6+
const { c } = useLocalSearchParams<{ c?: string }>();
7+
const path = c
8+
? `/verify-postal?c=${encodeURIComponent(c)}`
9+
: "/verify-postal";
10+
11+
return <WebEmbed path={path} />;
12+
}

0 commit comments

Comments
 (0)