76 lines
2.4 KiB
TypeScript
76 lines
2.4 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* FeedFollowButton — the inline Follow / Follow back affordance shown in a
|
|
* home-feed PostCard header for authors the viewer doesn't already follow.
|
|
*
|
|
* Follows are request-based, so a tap sends a request and the control settles
|
|
* into a non-interactive "Requested" pill (matching the profile action row).
|
|
* Anonymous taps route through the auth gate first.
|
|
*/
|
|
|
|
import { useState } from "react";
|
|
import { Clock, UserPlus } from "lucide-react";
|
|
import { toast } from "sonner";
|
|
import { relationships, SocialError } from "@/lib/social";
|
|
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 [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 || showRequested) return;
|
|
if (!(await requireAuth())) return;
|
|
setBusy(true);
|
|
try {
|
|
await relationships.follow(handle);
|
|
setJustRequested(true);
|
|
} catch (err) {
|
|
toast.error(
|
|
err instanceof SocialError ? err.message : "Could not follow",
|
|
{ duration: 3000 },
|
|
);
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
}
|
|
|
|
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" />
|
|
Requested
|
|
</span>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<button
|
|
type="button"
|
|
onClick={follow}
|
|
disabled={busy}
|
|
className="shrink-0 inline-flex items-center gap-1 rounded-full bg-primary px-3 h-8 text-xs font-medium text-primary-foreground dark:text-white hover:bg-primary/90 transition-colors disabled:opacity-60"
|
|
>
|
|
<UserPlus className="size-3.5" />
|
|
{followsYou ? "Follow back" : "Follow"}
|
|
</button>
|
|
);
|
|
}
|