Files
thoughts/thoughts-frontend/app/page.tsx

160 lines
5.1 KiB
TypeScript

import type { Metadata } from "next";
import { cookies } from "next/headers";
import {
getFeed,
getFriends,
getMe,
getTopFriends,
Me,
} from "@/lib/api";
import { ThoughtForm } from "@/components/thought-form";
import { EmptyState } from "@/components/empty-state";
import { Button } from "@/components/ui/button";
import Link from "next/link";
import { PopularTags } from "@/components/popular-tags";
import { ThoughtThread } from "@/components/thought-thread";
import { buildThoughtThreads } from "@/lib/utils";
import { TopFriends } from "@/components/top-friends";
import { UsersCount } from "@/components/users-count";
import { PaginationNav } from "@/components/pagination-nav";
import { redirect } from "next/navigation";
export const metadata: Metadata = {
title: "Home",
description: "Your home timeline — thoughts from people you follow",
};
export default async function Home({
searchParams,
}: {
searchParams: Promise<{ page?: string }>;
}) {
const token = (await cookies()).get("auth_token")?.value ?? null;
const resolvedSearchParams = await searchParams;
if (token) {
return <FeedPage token={token} searchParams={resolvedSearchParams} />;
} else {
return <LandingPage />;
}
}
async function FeedPage({
token,
searchParams,
}: {
token: string;
searchParams: { page?: string };
}) {
const page = parseInt(searchParams.page ?? "1", 10);
const [feedData, me] = await Promise.all([
getFeed(token, page).catch(() => null),
getMe(token).catch(() => null) as Promise<Me | null>,
]);
if (!feedData || !me) {
redirect("/login");
}
const { items: allThoughts, totalPages } = feedData!;
const thoughtThreads = buildThoughtThreads(allThoughts);
const friends = (await getFriends(token)).users.map((user) => user.username);
const topFriendsData = me
? await getTopFriends(me.username, token).catch(() => ({ topFriends: [] }))
: { topFriends: [] };
const shouldDisplayTopFriends = topFriendsData.topFriends.length > 0;
return (
<div className="container mx-auto max-w-6xl p-4 sm:p-6">
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
<aside className="hidden lg:block lg:col-span-1">
<div className="sticky top-20 space-y-6 glass-effect glossy-effect bottom rounded-md p-4">
<h2 className="text-lg font-semibold">Filters & Sorting</h2>
<p className="text-sm text-muted-foreground">Coming soon...</p>
</div>
</aside>
<main className="col-span-1 lg:col-span-2 space-y-6">
<header className="mb-6">
<h1 className="text-3xl font-bold text-shadow-sm">Your Feed</h1>
</header>
<ThoughtForm />
<div className="block lg:hidden space-y-6">
<PopularTags />
{shouldDisplayTopFriends && (
<TopFriends mode="top-friends" usernames={topFriendsData.topFriends} />
)}
{!shouldDisplayTopFriends && token && friends.length > 0 && (
<TopFriends mode="friends" usernames={friends || []} />
)}
<UsersCount />
</div>
<div className="space-y-6">
{thoughtThreads.map((thought) => (
<ThoughtThread
key={thought.id}
thought={thought}
currentUser={me}
/>
))}
{thoughtThreads.length === 0 && (
<EmptyState message="Your feed is empty. Follow some users to see their thoughts!" />
)}
</div>
<PaginationNav
page={page}
totalPages={totalPages}
buildHref={(p) => `/?page=${p}`}
/>
</main>
<aside className="hidden lg:block lg:col-span-1">
<div className="sticky top-20 space-y-6">
<PopularTags />
{shouldDisplayTopFriends && (
<TopFriends mode="top-friends" usernames={topFriendsData.topFriends} />
)}
{!shouldDisplayTopFriends && token && friends.length > 0 && (
<TopFriends mode="friends" usernames={friends || []} />
)}
<UsersCount />
</div>
</aside>
</div>
</div>
);
}
function LandingPage() {
return (
<>
<div className="font-sans min-h-screen text-gray-800 flex items-center justify-center">
<div className="container mx-auto max-w-2xl p-4 sm:p-6 text-center glass-effect glossy-effect bottom rounded-md shadow-fa-lg">
<h1
className="text-5xl font-bold"
style={{ textShadow: "2px 2px 4px rgba(0,0,0,0.1)" }}
>
Welcome to Thoughts
</h1>
<p className="text-muted-foreground mt-2">
Throwback to the golden age of microblogging.
</p>
<div className="mt-8 flex justify-center gap-4">
<Button asChild>
<Link href="/login">Login</Link>
</Button>
<Button variant="secondary" asChild>
<Link href="/register">Register</Link>
</Button>
</div>
</div>
</div>
</>
);
}