Skip to content

Commit f414050

Browse files
authored
feat: Reddit-inspired forum polish (#205)
1 parent c2859fd commit f414050

4 files changed

Lines changed: 250 additions & 243 deletions

File tree

apps/web/components/forum/forum-content.tsx

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { formatDistanceToNow } from "date-fns";
1010

1111
import { Button } from "@/components/ui/button";
1212
import { UserAvatar } from "@/components/user-avatar";
13-
import { RichTextViewer } from "@/components/editor/rich-text-viewer";
13+
import { cn } from "@/lib/utils";
1414

1515
interface ForumContentProps {
1616
challengeId: string;
@@ -27,42 +27,42 @@ export function ForumContent({ challengeId }: ForumContentProps) {
2727
<div>
2828
<div className="mb-6 flex items-center justify-between">
2929
<h1 className="text-2xl font-bold">Forum</h1>
30-
<Button asChild>
30+
<Button asChild size="sm">
3131
<Link href={`/challenges/${challengeId}/forum/new`}>
3232
<Plus className="h-4 w-4" />
3333
New Post
3434
</Link>
3535
</Button>
3636
</div>
3737

38-
<div className="space-y-2">
38+
<div className="divide-y divide-zinc-800/70">
3939
{results.map((item) => (
4040
<ForumPostCard
4141
key={item.post._id}
4242
item={item}
4343
challengeId={challengeId}
4444
/>
4545
))}
46+
</div>
4647

47-
{status === "CanLoadMore" && (
48-
<div className="pt-4 text-center">
49-
<Button variant="outline" onClick={() => loadMore(20)}>
50-
Load more
51-
</Button>
52-
</div>
53-
)}
48+
{status === "CanLoadMore" && (
49+
<div className="pt-6 text-center">
50+
<Button variant="outline" size="sm" onClick={() => loadMore(20)}>
51+
Load more
52+
</Button>
53+
</div>
54+
)}
5455

55-
{results.length === 0 && status !== "LoadingFirstPage" && (
56-
<div className="py-12 text-center text-zinc-500">
57-
<MessageSquare className="mx-auto mb-3 h-8 w-8" />
58-
<p>No posts yet. Start the conversation!</p>
59-
</div>
60-
)}
56+
{results.length === 0 && status !== "LoadingFirstPage" && (
57+
<div className="py-16 text-center text-zinc-500">
58+
<MessageSquare className="mx-auto mb-3 h-8 w-8" />
59+
<p className="text-sm">No posts yet. Start the conversation!</p>
60+
</div>
61+
)}
6162

62-
{status === "LoadingFirstPage" && (
63-
<div className="py-12 text-center text-zinc-500">Loading...</div>
64-
)}
65-
</div>
63+
{status === "LoadingFirstPage" && (
64+
<div className="py-16 text-center text-zinc-500 text-sm">Loading...</div>
65+
)}
6666
</div>
6767
);
6868
}
@@ -109,70 +109,70 @@ const ForumPostCard = memo(function ForumPostCard({ item, challengeId }: ForumPo
109109
return (
110110
<Link
111111
href={`/challenges/${challengeId}/forum/${item.post._id}`}
112-
className="flex gap-3 rounded-lg border border-zinc-800 p-4 transition-colors hover:border-zinc-700 hover:bg-zinc-900/50"
112+
className={cn(
113+
"flex gap-3 py-3 transition-colors hover:bg-zinc-900/40",
114+
item.post.isPinned && "bg-amber-500/[0.02]",
115+
)}
113116
>
114117
{/* Upvote column */}
115-
<div className="flex flex-col items-center gap-1">
118+
<div className="flex w-10 shrink-0 flex-col items-center pt-0.5">
116119
<button
117120
onClick={handleUpvote}
118121
disabled={isUpvoting}
119-
className={`rounded p-1 transition-colors ${
122+
className={cn(
123+
"rounded p-1 transition-colors active:scale-95",
120124
item.upvotedByUser
121-
? "text-indigo-400 hover:text-indigo-300"
122-
: "text-zinc-500 hover:text-zinc-300"
123-
}`}
125+
? "text-indigo-400"
126+
: "text-zinc-600 hover:text-zinc-400",
127+
)}
124128
>
125129
<ArrowBigUp
126130
className="h-5 w-5"
127131
fill={item.upvotedByUser ? "currentColor" : "none"}
128132
/>
129133
</button>
130134
<span
131-
className={`text-xs font-medium ${
132-
item.upvotedByUser ? "text-indigo-400" : "text-zinc-500"
133-
}`}
135+
className={cn(
136+
"text-xs font-mono font-medium",
137+
item.upvotedByUser ? "text-indigo-400" : "text-zinc-500",
138+
)}
134139
>
135140
{item.upvoteCount}
136141
</span>
137142
</div>
138143

139-
{/* Content column */}
144+
{/* Content */}
140145
<div className="min-w-0 flex-1">
141-
<div className="flex items-center gap-2">
146+
{/* Title row */}
147+
<div className="flex items-center gap-1.5">
142148
{item.post.isPinned && (
143-
<Pin className="h-3 w-3 flex-shrink-0 text-amber-400" />
149+
<Pin className="h-3 w-3 shrink-0 rotate-45 text-amber-400" />
144150
)}
145-
<h3 className="truncate font-semibold text-white">
151+
<h3 className="truncate text-sm font-semibold text-zinc-100">
146152
{item.post.title}
147153
</h3>
148154
</div>
149155

150-
<div className="mt-1 line-clamp-2 text-sm text-zinc-400 [&_*]:text-zinc-400">
151-
<RichTextViewer content={item.post.content} />
152-
</div>
153-
154-
<div className="mt-2 flex items-center gap-2 text-xs text-zinc-500">
156+
{/* Meta line */}
157+
<div className="mt-1 flex items-center gap-1.5 text-xs text-zinc-500">
155158
{item.user && (
156-
<UserAvatar
157-
user={item.user}
158-
size="sm"
159-
/>
160-
)}
161-
<span className="flex flex-wrap items-center gap-x-2 gap-y-1">
162-
{item.user && (
159+
<>
160+
<UserAvatar user={item.user} size="xs" disableLink />
163161
<span className="font-medium text-zinc-400">
164-
{item.user.name || item.user.username}
162+
{item.user.username}
165163
</span>
166-
)}
167-
<span>
168-
{formatDistanceToNow(new Date(item.post.createdAt), {
169-
addSuffix: true,
170-
})}
171-
</span>
172-
<span className="flex items-center gap-1">
173-
<MessageSquare className="h-3 w-3" />
174-
{item.replyCount}
175-
</span>
164+
<span>·</span>
165+
</>
166+
)}
167+
<span>
168+
{formatDistanceToNow(new Date(item.post.createdAt), {
169+
addSuffix: true,
170+
})}
171+
</span>
172+
<span>·</span>
173+
<span className="flex items-center gap-1">
174+
<MessageSquare className="h-3 w-3" />
175+
{item.replyCount}
176176
</span>
177177
</div>
178178
</div>

0 commit comments

Comments
 (0)