AT-based link agregator. Mirror of https://github.com/likeandscribe/frontpage

Refactor pages and layouts to use PageProps/LayoutProps (#314)

* Refactor pages and layouts to use PageProps/LayoutProps

* Add typegen

* Typo

* Use RouteContext

* Move typegen into separate task and mark as dependency in turbo

authored by tom.sherman.is and committed by

GitHub 62f74172 b07fcdaa

+49 -61
+3 -6
packages/frontpage/app/(app)/post/[postAuthor]/[postRkey]/[commentAuthor]/[commentRkey]/_lib/page-data.tsx
··· 7 7 import { getPost } from "@/lib/data/db/post"; 8 8 import { notFound } from "next/navigation"; 9 9 10 - export type CommentPageParams = { 11 - commentRkey: string; 12 - postRkey: string; 13 - postAuthor: string; 14 - commentAuthor: string; 15 - }; 10 + export type CommentPageParams = Awaited< 11 + PageProps<"/post/[postAuthor]/[postRkey]/[commentAuthor]/[commentRkey]">["params"] 12 + >; 16 13 17 14 export async function getCommentPageData(params: CommentPageParams) { 18 15 const [postAuthorDid, commentAuthorDid] = await Promise.all([
+4 -2
packages/frontpage/app/(app)/post/[postAuthor]/[postRkey]/[commentAuthor]/[commentRkey]/og-image/route.tsx
··· 8 8 OgWrapper, 9 9 frontpageOgImageResponse, 10 10 } from "@/lib/og"; 11 - import { type CommentPageParams, getCommentPageData } from "../_lib/page-data"; 11 + import { getCommentPageData } from "../_lib/page-data"; 12 12 import { getBlueskyProfile } from "@/lib/data/user"; 13 13 import { shouldHideComment } from "@/lib/data/db/comment"; 14 14 import { notFound } from "next/navigation"; ··· 19 19 20 20 export async function GET( 21 21 _req: Request, 22 - { params }: { params: Promise<CommentPageParams> }, 22 + { 23 + params, 24 + }: RouteContext<"/post/[postAuthor]/[postRkey]/[commentAuthor]/[commentRkey]/og-image">, 23 25 ) { 24 26 const { comment } = await getCommentPageData(await params); 25 27 if ((await shouldHideComment(comment)) || comment.status !== "live") {
+6 -6
packages/frontpage/app/(app)/post/[postAuthor]/[postRkey]/[commentAuthor]/[commentRkey]/page.tsx
··· 17 17 return `/post/${params.postAuthor}/${params.postRkey}/${params.commentAuthor}/${params.commentRkey}`; 18 18 } 19 19 20 - export async function generateMetadata(props: { 21 - params: Promise<CommentPageParams>; 22 - }): Promise<Metadata> { 20 + export async function generateMetadata( 21 + props: PageProps<"/post/[postAuthor]/[postRkey]/[commentAuthor]/[commentRkey]">, 22 + ): Promise<Metadata> { 23 23 const params = await props.params; 24 24 const { comment, post } = await getCommentPageData(params); 25 25 ··· 56 56 }; 57 57 } 58 58 59 - export default async function CommentPage(props: { 60 - params: Promise<CommentPageParams>; 61 - }) { 59 + export default async function CommentPage( 60 + props: PageProps<"/post/[postAuthor]/[postRkey]/[commentAuthor]/[commentRkey]">, 61 + ) { 62 62 const params = await props.params; 63 63 const { comment, post } = await getCommentPageData(params); 64 64
+3 -4
packages/frontpage/app/(app)/post/[postAuthor]/[postRkey]/_lib/page-data.tsx
··· 3 3 import { getPost } from "@/lib/data/db/post"; 4 4 import { notFound } from "next/navigation"; 5 5 6 - export type PostPageParams = { 7 - postAuthor: string; 8 - postRkey: string; 9 - }; 6 + export type PostPageParams = Awaited< 7 + PageProps<"/post/[postAuthor]/[postRkey]">["params"] 8 + >; 10 9 11 10 export async function getPostPageData(params: PostPageParams) { 12 11 const authorDid = await getDidFromHandleOrDid(params.postAuthor);
+3 -9
packages/frontpage/app/(app)/post/[postAuthor]/[postRkey]/layout.tsx
··· 8 8 import { NewComment } from "./_lib/comment-client"; 9 9 import { SuperHackyScrollToTop } from "./scroller"; 10 10 11 - type Params = { 12 - postRkey: string; 13 - postAuthor: string; 14 - }; 15 - 16 - export default async function PostLayout(props: { 17 - children: React.ReactNode; 18 - params: Promise<Params>; 19 - }) { 11 + export default async function PostLayout( 12 + props: LayoutProps<"/post/[postAuthor]/[postRkey]">, 13 + ) { 20 14 const params = await props.params; 21 15 22 16 const { children } = props;
+1 -6
packages/frontpage/app/(app)/post/[postAuthor]/[postRkey]/og-image/route.tsx
··· 12 12 } from "@/lib/og"; 13 13 import { getPostPageData } from "../_lib/page-data"; 14 14 15 - type Params = { 16 - postRkey: string; 17 - postAuthor: string; 18 - }; 19 - 20 15 export const dynamic = "force-static"; 21 16 export const revalidate = 3600; // 1 hour 22 17 23 18 export async function GET( 24 19 _req: Request, 25 - { params }: { params: Promise<Params> }, 20 + { params }: RouteContext<"/post/[postAuthor]/[postRkey]/og-image">, 26 21 ) { 27 22 const { post } = await getPostPageData(await params); 28 23 const profile = await getBlueskyProfile(post.authorDid);
+6 -6
packages/frontpage/app/(app)/post/[postAuthor]/[postRkey]/page.tsx
··· 10 10 return `/post/${params.postAuthor}/${params.postRkey}`; 11 11 } 12 12 13 - export async function generateMetadata(props: { 14 - params: Promise<PostPageParams>; 15 - }): Promise<Metadata> { 13 + export async function generateMetadata( 14 + props: PageProps<"/post/[postAuthor]/[postRkey]">, 15 + ): Promise<Metadata> { 16 16 const params = await props.params; 17 17 const { post } = await getPostPageData(params); 18 18 ··· 41 41 }; 42 42 } 43 43 44 - export default async function PostPage(props: { 45 - params: Promise<PostPageParams>; 46 - }) { 44 + export default async function PostPage( 45 + props: PageProps<"/post/[postAuthor]/[postRkey]">, 46 + ) { 47 47 const params = await props.params; 48 48 const { post, authorDid } = await getPostPageData(params); 49 49 const comments = await getCommentsForPost(post.id);
+11 -7
packages/frontpage/app/(app)/post/new/page.tsx
··· 6 6 robots: "noindex, nofollow", 7 7 }; 8 8 9 - export default async function NewPost(props: { 10 - searchParams: Promise<Record<string, string>>; 11 - }) { 9 + export default async function NewPost(props: PageProps<"/post/new">) { 12 10 const searchParams = await props.searchParams; 11 + const defaultTitle = 12 + typeof searchParams.title === "string" 13 + ? searchParams.title 14 + : searchParams.title?.[0]; 15 + 16 + const defaultUrl = 17 + typeof searchParams.url === "string" 18 + ? searchParams.url 19 + : searchParams.url?.[0]; 13 20 return ( 14 21 <main className="flex flex-col gap-3"> 15 22 <h2 className="text-3xl font-bold tracking-tight text-gray-900 dark:text-gray-100"> 16 23 New post 17 24 </h2> 18 - <NewPostForm 19 - defaultTitle={searchParams.title} 20 - defaultUrl={searchParams.url} 21 - /> 25 + <NewPostForm defaultTitle={defaultTitle} defaultUrl={defaultUrl} /> 22 26 </main> 23 27 ); 24 28 }
+4 -8
packages/frontpage/app/(app)/profile/[user]/page.tsx
··· 24 24 import { type Metadata } from "next"; 25 25 import { LinkAlternateAtUri } from "@/lib/components/link-alternate-at"; 26 26 27 - type Params = { 28 - user: string; 29 - }; 30 - 31 - export async function generateMetadata(props: { 32 - params: Promise<Params>; 33 - }): Promise<Metadata> { 27 + export async function generateMetadata( 28 + props: PageProps<"/profile/[user]">, 29 + ): Promise<Metadata> { 34 30 const params = await props.params; 35 31 const did = await getDidFromHandleOrDid(params.user); 36 32 if (!did) { ··· 57 53 }; 58 54 } 59 55 60 - export default async function Profile(props: { params: Promise<Params> }) { 56 + export default async function Profile(props: PageProps<"/profile/[user]">) { 61 57 await connection(); 62 58 const params = await props.params; 63 59 const did = await getDidFromHandleOrDid(params.user);
+1 -5
packages/frontpage/app/(auth)/login/page.tsx
··· 4 4 import { Alert, AlertDescription, AlertTitle } from "@/lib/components/ui/alert"; 5 5 import { CrossCircledIcon } from "@radix-ui/react-icons"; 6 6 7 - export default async function LoginPage({ 8 - searchParams, 9 - }: { 10 - searchParams: Promise<{ error?: string }>; 11 - }) { 7 + export default async function LoginPage({ searchParams }: PageProps<"/login">) { 12 8 const user = await getUser(); 13 9 14 10 if (user !== null) {
+1
packages/frontpage/package.json
··· 8 8 "build-1pw": "NODE_OPTIONS=--use-openssl-ca op run --env-file=\"./.env.1pw\" -- next build", 9 9 "build": "NODE_OPTIONS=--use-openssl-ca next build --turbopack", 10 10 "start": "NODE_OPTIONS=--use-openssl-ca next start", 11 + "typegen": "next typegen", 11 12 "lint": "eslint .", 12 13 "db:generate": "NODE_OPTIONS=--use-openssl-ca drizzle-kit generate", 13 14 "db:migrate": "NODE_OPTIONS=--use-openssl-ca drizzle-kit migrate",
+6 -2
turbo.json
··· 10 10 "dependsOn": ["^build"] 11 11 }, 12 12 "type-check": { 13 - "dependsOn": ["^build"] 13 + "dependsOn": ["^build", "typegen"] 14 14 }, 15 - "lint": { 15 + "typegen": { 16 16 "dependsOn": ["^build"], 17 + "outputs": [".next/types/**"] 18 + }, 19 + "lint": { 20 + "dependsOn": ["^build", "typegen"], 17 21 "outputs": ["eslint_report.json"] 18 22 }, 19 23 "test": {