tangled
alpha
login
or
join now
leaflet.pub
/
leaflet
289
fork
atom
a tool for shared writing and social publishing
289
fork
atom
overview
issues
28
pulls
pipelines
persist nav state on reader/home
awarm.space
1 month ago
cbd83033
63a9157d
+85
-14
6 changed files
expand all
collapse all
unified
split
app
(home-pages)
layout.tsx
page.tsx
reader
layout.tsx
api
update-nav-state
route.ts
route.ts
components
NavStateTracker.tsx
+3
app/(home-pages)/layout.tsx
···
1
1
import { getIdentityData } from "actions/getIdentityData";
2
2
import { EntitySetProvider } from "components/EntitySetProvider";
3
3
+
import { NavStateTracker } from "components/NavStateTracker";
3
4
import {
4
5
ThemeProvider,
5
6
ThemeBackgroundProvider,
···
13
14
if (!identityData?.home_leaflet)
14
15
return (
15
16
<>
17
17
+
<NavStateTracker />
16
18
<ThemeProvider entityID={""}>{props.children}</ThemeProvider>
17
19
</>
18
20
);
···
34
36
>
35
37
<ThemeProvider entityID={root_entity}>
36
38
<ThemeBackgroundProvider entityID={root_entity}>
39
39
+
<NavStateTracker />
37
40
{props.children}
38
41
</ThemeBackgroundProvider>
39
42
</ThemeProvider>
+31
app/(home-pages)/page.tsx
···
1
1
+
import { cookies } from "next/headers";
2
2
+
import ReaderLayout from "./reader/layout";
3
3
+
import ReaderPage from "./reader/page";
4
4
+
import HomePage from "./home/page";
5
5
+
6
6
+
export default async function RootPage() {
7
7
+
const cookieStore = await cookies();
8
8
+
const hasAuth =
9
9
+
cookieStore.has("auth_token") ||
10
10
+
cookieStore.has("external_auth_token");
11
11
+
12
12
+
if (!hasAuth) {
13
13
+
return (
14
14
+
<ReaderLayout>
15
15
+
<ReaderPage />
16
16
+
</ReaderLayout>
17
17
+
);
18
18
+
}
19
19
+
20
20
+
const navState = cookieStore.get("nav-state")?.value;
21
21
+
22
22
+
if (navState === "reader") {
23
23
+
return (
24
24
+
<ReaderLayout>
25
25
+
<ReaderPage />
26
26
+
</ReaderLayout>
27
27
+
);
28
28
+
}
29
29
+
30
30
+
return <HomePage />;
31
31
+
}
+8
-3
app/(home-pages)/reader/layout.tsx
···
27
27
const tabs = allTabs.filter((tab) => !tab.requiresAuth || isLoggedIn);
28
28
29
29
const isActive = (href: string) => {
30
30
-
if (href === "/reader") return pathname === "/reader";
31
31
-
if (href === "/reader/hot" && !isLoggedIn && pathname === "/reader")
30
30
+
if (href === "/reader")
31
31
+
return pathname === "/reader" || pathname === "/";
32
32
+
if (
33
33
+
href === "/reader/hot" &&
34
34
+
!isLoggedIn &&
35
35
+
(pathname === "/reader" || pathname === "/")
36
36
+
)
32
37
return true;
33
38
return pathname.startsWith(href);
34
39
};
···
62
67
))}
63
68
</div>
64
69
<div className="sm:block grow">
65
65
-
{pathname === "/reader" && (
70
70
+
{(pathname === "/reader" || pathname === "/") && (
66
71
<div className="place-self-end text text-tertiary text-sm">
67
72
Publications
68
73
</div>
+16
app/api/update-nav-state/route.ts
···
1
1
+
import { NextRequest, NextResponse } from "next/server";
2
2
+
3
3
+
export async function POST(request: NextRequest) {
4
4
+
const { state } = (await request.json()) as { state: string };
5
5
+
if (state !== "home" && state !== "reader") {
6
6
+
return NextResponse.json({ error: "Invalid state" }, { status: 400 });
7
7
+
}
8
8
+
9
9
+
const response = NextResponse.json({ ok: true });
10
10
+
response.cookies.set("nav-state", state, {
11
11
+
path: "/",
12
12
+
sameSite: "lax",
13
13
+
maxAge: 60 * 60 * 24 * 365,
14
14
+
});
15
15
+
return response;
16
16
+
}
-11
app/route.ts
···
1
1
-
import { createNewLeaflet } from "actions/createNewLeaflet";
2
2
-
import { cookies } from "next/headers";
3
3
-
import { redirect } from "next/navigation";
4
4
-
5
5
-
export const preferredRegion = ["sfo1"];
6
6
-
export const dynamic = "force-dynamic";
7
7
-
export const fetchCache = "force-no-store";
8
8
-
9
9
-
export async function GET() {
10
10
-
redirect("/home");
11
11
-
}
+27
components/NavStateTracker.tsx
···
1
1
+
"use client";
2
2
+
3
3
+
import { usePathname } from "next/navigation";
4
4
+
import { useEffect, useRef } from "react";
5
5
+
6
6
+
export function NavStateTracker() {
7
7
+
const pathname = usePathname();
8
8
+
const lastState = useRef<string | null>(null);
9
9
+
10
10
+
useEffect(() => {
11
11
+
let state: string | null = null;
12
12
+
if (pathname === "/home") state = "home";
13
13
+
else if (pathname === "/reader" || pathname.startsWith("/reader/"))
14
14
+
state = "reader";
15
15
+
16
16
+
if (state && state !== lastState.current) {
17
17
+
lastState.current = state;
18
18
+
fetch("/api/update-nav-state", {
19
19
+
method: "POST",
20
20
+
headers: { "Content-Type": "application/json" },
21
21
+
body: JSON.stringify({ state }),
22
22
+
});
23
23
+
}
24
24
+
}, [pathname]);
25
25
+
26
26
+
return null;
27
27
+
}