fix: feed card follow ui

This commit is contained in:
gosangam
2026-06-18 00:21:06 +05:30
parent 071eb4d58a
commit 84cb273941
4 changed files with 59 additions and 19 deletions
+12 -4
View File
@@ -18,22 +18,30 @@ import { useAuthGate } from "@/components/auth/auth-gate";
export function FeedFollowButton({
handle,
followsYou,
requested = false,
}: {
handle: string;
/** The author already follows the viewer → label becomes "Follow back". */
followsYou: boolean;
/** Server-truth: the viewer already has a pending follow request to this
* author (e.g. sent in a prior session or on the profile page). */
requested?: boolean;
}) {
const { requireAuth } = useAuthGate();
const [requested, setRequested] = useState(false);
const [justRequested, setJustRequested] = useState(false);
const [busy, setBusy] = useState(false);
// Show "Requested" when the server already knows about a pending request or
// the viewer just sent one in this session.
const showRequested = requested || justRequested;
async function follow() {
if (busy || requested) return;
if (busy || showRequested) return;
if (!(await requireAuth())) return;
setBusy(true);
try {
await relationships.follow(handle);
setRequested(true);
setJustRequested(true);
} catch (err) {
toast.error(
err instanceof SocialError ? err.message : "Could not follow",
@@ -44,7 +52,7 @@ export function FeedFollowButton({
}
}
if (requested) {
if (showRequested) {
return (
<span className="shrink-0 inline-flex items-center gap-1 rounded-full border border-border px-3 h-8 text-xs font-medium text-muted-foreground">
<Clock className="size-3.5" />
+19 -5
View File
@@ -71,11 +71,25 @@ export function PostCard({
show (delete for owners, block/report for everyone else), so
a view with nothing to expose simply hides the dot. */}
<header className="flex items-center gap-3 mb-4">
<UserAvatar
src={post.author_avatar_url}
name={authorName}
className="size-9"
/>
{authorHandle ? (
<Link
href={`/u/${authorHandle}`}
aria-label={`View ${authorName}'s profile`}
className="shrink-0"
>
<UserAvatar
src={post.author_avatar_url}
name={authorName}
className="size-9"
/>
</Link>
) : (
<UserAvatar
src={post.author_avatar_url}
name={authorName}
className="size-9"
/>
)}
<div className="min-w-0 flex-1">
{authorHandle ? (
<Link
+25 -10
View File
@@ -33,6 +33,7 @@ import { AtSign, ImageIcon, Repeat2 } from "lucide-react";
import { cn } from "@/lib/utils";
import type { Post, PostImage } from "@/lib/social";
import { displaySourceLabel } from "@/lib/source-reference";
import { timeAgo } from "@/lib/time";
import { useMediaQuery } from "@/components/selection-toolbar/shared";
import { UserAvatar } from "@/components/ui/user-avatar";
import { useAuth } from "@/components/auth/auth-provider";
@@ -125,11 +126,25 @@ export function PostScrollCard({
on the right (Delete for author, Block/Report for everyone
else; PostCardMenu hides itself when there are no items). */}
<header className="flex items-center gap-3 px-4 py-3">
<UserAvatar
src={post.author_avatar_url}
name={post.author_name ?? "Unknown"}
className="size-9"
/>
{post.author_handle ? (
<Link
href={`/u/${post.author_handle}`}
aria-label={`View ${post.author_name || post.author_handle}'s profile`}
className="shrink-0"
>
<UserAvatar
src={post.author_avatar_url}
name={post.author_name ?? "Unknown"}
className="size-9"
/>
</Link>
) : (
<UserAvatar
src={post.author_avatar_url}
name={post.author_name ?? "Unknown"}
className="size-9"
/>
)}
<div className="min-w-0 flex-1">
<Link
href={
@@ -139,16 +154,16 @@ export function PostScrollCard({
>
{post.author_name || "Unknown"}
</Link>
{post.author_handle ? (
<span className="block text-xs text-muted-foreground truncate">
@{post.author_handle}
</span>
) : null}
<span className="block text-xs text-muted-foreground truncate">
{post.author_handle ? <>@{post.author_handle} · </> : null}
{timeAgo(post.created_at)}
</span>
</div>
{enableFollow && post.author_handle && !post.viewer_follows_author ? (
<FeedFollowButton
handle={post.author_handle}
followsYou={!!post.author_follows_viewer}
requested={!!post.viewer_follow_requested}
/>
) : null}
<PostCardMenu
+3
View File
@@ -94,6 +94,9 @@ export type Post = {
viewer_follows_author?: boolean;
/** Home-feed only: does the author follow the viewer (drives "Follow back")? */
author_follows_viewer?: boolean;
/** Home-feed only: viewer has a pending (unaccepted) follow request to the
* author — drives the "Requested" state on the feed follow button. */
viewer_follow_requested?: boolean;
/** Owner-only: post is hidden from timelines + public collections. Still
* shown to the owner on their own profile and in private collections. */
is_hidden?: boolean;