perf(frontend): eliminate author profile waterfall — use thought.author directly

This commit is contained in:
2026-05-15 19:47:06 +02:00
parent 9ecbde019d
commit dadfe04934
10 changed files with 8581 additions and 114 deletions

View File

@@ -91,11 +91,6 @@ export function RemoteUserProfile({
const isOwnProfile = me?.username === actor.handle;
const authorDetails = new Map<string, { avatarUrl?: string | null }>();
initialPosts.forEach((t) => {
authorDetails.set(t.author.username, { avatarUrl: actor.avatarUrl });
});
return (
<div>
<div
@@ -220,7 +215,6 @@ export function RemoteUserProfile({
{initialPosts.length > 0 ? (
<ThoughtList
thoughts={initialPosts}
authorDetails={authorDetails}
currentUser={me}
/>
) : (

View File

@@ -7,11 +7,11 @@ import {
CardHeader,
} from "@/components/ui/card";
import { UserAvatar } from "./user-avatar";
import { deleteThought, Me, Thought } from "@/lib/api";
import { Me, Thought } from "@/lib/api";
import { deleteThought } from "@/app/actions/thoughts";
import { format, formatDistanceToNow } from "date-fns";
import { useAuth } from "@/hooks/use-auth";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { toast } from "sonner";
import {
DropdownMenu,
@@ -42,25 +42,19 @@ import { cn } from "@/lib/utils";
interface ThoughtCardProps {
thought: Thought;
author: {
username: string;
displayName?: string | null;
avatarUrl?: string | null;
};
currentUser: Me | null;
isReply?: boolean;
}
export function ThoughtCard({
thought,
author,
currentUser,
isReply = false,
}: ThoughtCardProps) {
const { author } = thought;
const [isAlertOpen, setIsAlertOpen] = useState(false);
const [isReplyOpen, setIsReplyOpen] = useState(false);
const { token } = useAuth();
const router = useRouter();
const timeAgo = formatDistanceToNow(new Date(thought.createdAt), {
addSuffix: true,
});
@@ -68,11 +62,9 @@ export function ThoughtCard({
const isAuthor = currentUser?.username === thought.author.username;
const handleDelete = async () => {
if (!token) return;
try {
await deleteThought(thought.id, token);
await deleteThought(thought.id);
toast.success("Thought deleted successfully.");
router.refresh();
} catch (error) {
console.error("Failed to delete thought:", error);
toast.error("Failed to delete thought.");

View File

@@ -4,15 +4,10 @@ import { Card, CardContent } from "./ui/card";
interface ThoughtListProps {
thoughts: Thought[];
authorDetails: Map<string, { avatarUrl?: string | null }>;
currentUser: Me | null;
}
export function ThoughtList({
thoughts,
authorDetails,
currentUser,
}: ThoughtListProps) {
export function ThoughtList({ thoughts, currentUser }: ThoughtListProps) {
if (thoughts.length === 0) {
return (
<p className="text-center text-muted-foreground pt-8">
@@ -25,21 +20,13 @@ export function ThoughtList({
<Card>
<CardContent className="divide-y p-0">
<div className="space-y-6 p-4">
{thoughts.map((thought) => {
const author = {
username: thought.author.username,
displayName: thought.author.displayName,
...authorDetails.get(thought.author.username),
};
return (
<ThoughtCard
key={thought.id}
thought={thought}
author={author}
currentUser={currentUser}
/>
);
})}
{thoughts.map((thought) => (
<ThoughtCard
key={thought.id}
thought={thought}
currentUser={currentUser}
/>
))}
</div>
</CardContent>
</Card>

View File

@@ -3,29 +3,19 @@ import { ThoughtCard } from "./thought-card";
interface ThoughtThreadProps {
thought: ThoughtThreadType;
authorDetails: Map<string, { avatarUrl?: string | null }>;
currentUser: Me | null;
isReply?: boolean;
}
export function ThoughtThread({
thought,
authorDetails,
currentUser,
isReply = false,
}: ThoughtThreadProps) {
const author = {
username: thought.author.username,
displayName: thought.author.displayName,
avatarUrl: thought.author.avatarUrl, // API-provided avatar (from DB COALESCE)
...authorDetails.get(thought.author.username), // override for local users
};
return (
<div id={`thought-thread-${thought.id}`} className="flex flex-col gap-0">
<ThoughtCard
thought={thought}
author={author}
currentUser={currentUser}
isReply={isReply}
/>
@@ -39,7 +29,6 @@ export function ThoughtThread({
<ThoughtThread
key={reply.id}
thought={reply}
authorDetails={authorDetails}
currentUser={currentUser}
isReply={true}
/>