feat: load more pagination for user profile thoughts
This commit is contained in:
@@ -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">
|
||||||
|
|||||||
72
thoughts-frontend/components/user-thoughts-list.tsx
Normal file
72
thoughts-frontend/components/user-thoughts-list.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user