feat: load more pagination for user profile thoughts

This commit is contained in:
2026-05-16 15:21:18 +02:00
parent 4a84c595d5
commit 39f7d39232
3 changed files with 86 additions and 17 deletions

View File

@@ -53,8 +53,7 @@ import { FollowButton } from "@/components/follow-button";
import { TopFriends } from "@/components/top-friends"; import { TopFriends } from "@/components/top-friends";
import { Suspense } from "react"; import { Suspense } from "react";
import { ProfileSkeleton } from "@/components/loading-skeleton"; import { ProfileSkeleton } from "@/components/loading-skeleton";
import { buildThoughtThreads } from "@/lib/utils"; import { UserThoughtsList } from "@/components/user-thoughts-list";
import { ThoughtThread } from "@/components/thought-thread";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import Link from "next/link"; import Link from "next/link";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
@@ -95,9 +94,11 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
const user = userResult.value; const user = userResult.value;
const me = meResult.status === "fulfilled" ? (meResult.value as Me) : null; const me = meResult.status === "fulfilled" ? (meResult.value as Me) : null;
const thoughts = const thoughtsData = thoughtsResult.status === "fulfilled" ? thoughtsResult.value : null;
thoughtsResult.status === "fulfilled" ? thoughtsResult.value.items : []; const thoughts = thoughtsData?.items ?? [];
const thoughtThreads = buildThoughtThreads(thoughts); const totalPages = thoughtsData
? Math.ceil(thoughtsData.total / thoughtsData.per_page)
: 1;
const localFollowersCount = const localFollowersCount =
followersResult.status === "fulfilled" followersResult.status === "fulfilled"
@@ -262,16 +263,12 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
)} )}
</TabsList> </TabsList>
<TabsContent value="thoughts" className="space-y-4"> <TabsContent value="thoughts" className="space-y-4">
{thoughtThreads.map((thought) => ( <UserThoughtsList
<ThoughtThread username={username}
key={thought.id} initialThoughts={thoughts}
thought={thought} totalPages={totalPages}
currentUser={me} me={me}
/> />
))}
{thoughtThreads.length === 0 && (
<EmptyState emoji="💭" title="Nothing here yet" message="This user hasn't posted any public thoughts yet." />
)}
</TabsContent> </TabsContent>
{isOwnProfile && ( {isOwnProfile && (
<TabsContent value="federation"> <TabsContent value="federation">

View File

@@ -0,0 +1,72 @@
"use client";
import { useState } from "react";
import { getUserThoughts, Me, Thought } from "@/lib/api";
import { ThoughtThread } from "@/components/thought-thread";
import { Button } from "@/components/ui/button";
import { EmptyState } from "@/components/empty-state";
import { buildThoughtThreads } from "@/lib/utils";
import { toast } from "sonner";
import { useAuth } from "@/hooks/use-auth";
interface UserThoughtsListProps {
username: string;
initialThoughts: Thought[];
totalPages: number;
me: Me | null;
}
export function UserThoughtsList({
username,
initialThoughts,
totalPages,
me,
}: UserThoughtsListProps) {
const [thoughts, setThoughts] = useState<Thought[]>(initialThoughts);
const [page, setPage] = useState(1);
const [loadingMore, setLoadingMore] = useState(false);
const { token } = useAuth();
const thoughtThreads = buildThoughtThreads(thoughts);
const loadMore = async () => {
setLoadingMore(true);
try {
const result = await getUserThoughts(username, token, page + 1);
setThoughts((prev) => [...prev, ...result.items]);
setPage((p) => p + 1);
} catch {
toast.error("Failed to load more thoughts.");
} finally {
setLoadingMore(false);
}
};
if (thoughtThreads.length === 0) {
return (
<EmptyState
emoji="💭"
title="Nothing here yet"
message="This user hasn't posted any public thoughts yet."
/>
);
}
return (
<div className="space-y-4">
{thoughtThreads.map((thought) => (
<ThoughtThread key={thought.id} thought={thought} currentUser={me} />
))}
{page < totalPages && (
<Button
onClick={loadMore}
disabled={loadingMore}
variant="outline"
className="w-full rounded-full"
>
{loadingMore ? "Loading…" : "Load more"}
</Button>
)}
</div>
);
}

View File

@@ -343,9 +343,9 @@ export const getFeed = (token: string, page: number = 1, pageSize: number = 20)
token token
); );
export const getUserThoughts = (username: string, token: string | null) => export const getUserThoughts = (username: string, token: string | null, page = 1) =>
apiFetch( apiFetch(
`/users/${username}/thoughts`, `/users/${username}/thoughts?page=${page}`,
{ next: { tags: [`profile:${username}`] } }, { next: { tags: [`profile:${username}`] } },
z.object({ items: z.array(ThoughtSchema), total: z.number(), page: z.number(), per_page: z.number() }), z.object({ items: z.array(ThoughtSchema), total: z.number(), page: z.number(), per_page: z.number() }),
token token