Files
spsays-training/components/profile/message-button.tsx
T
2026-08-29 03:49:58 +05:30

93 lines
2.8 KiB
TypeScript

"use client";
/**
* The Message button on someone else's profile.
*
* Shown only when the BFF says the viewer may actually start a conversation —
* `can_message` reflects the target's "who can message you" setting and any
* block. Offering a button that fails on tap is worse than not offering one, so
* when messaging is closed this explains why instead.
*
* A blocked profile shows nothing at all: the block itself is already conveyed
* by the rest of the action row, and spelling it out again would be noise.
*/
import { useState } from "react";
import { useRouter } from "next/navigation";
import { Loader2, MessageCircle } from "lucide-react";
import { toast } from "sonner";
import { useAuthGate } from "@/components/auth/auth-gate";
import { chat } from "@/lib/chat";
import type { RelationshipState } from "@/lib/social";
import { cn } from "@/lib/utils";
/** Why messaging is closed, in the target's own terms. */
const REASON: Record<string, string> = {
friends: "only accepts messages from friends",
account_age: "only accepts messages from established accounts",
same_gender: "limits who can message them",
};
export function MessageButton({
handle,
name,
relationship,
}: {
handle: string;
name: string;
relationship: RelationshipState | null;
}) {
const router = useRouter();
const { requireAuth } = useAuthGate();
const [busy, setBusy] = useState(false);
// Anonymous viewers get the button and hit the auth gate on tap — hiding it
// would make the feature invisible to exactly the people who need to sign up.
if (relationship && !relationship.can_message) {
const reason = REASON[relationship.message_restriction ?? ""];
if (!reason) return null; // blocked, or self — say nothing
return (
<p className="text-xs text-muted-foreground">
{name} {reason}.
</p>
);
}
const open = async () => {
if (!(await requireAuth())) return;
setBusy(true);
try {
const conv = await chat.openDM(handle);
router.push(`/chats/${conv.id}`);
} catch (err) {
toast.error(
err instanceof Error && err.message
? err.message
: "Could not open the conversation",
);
setBusy(false);
}
};
return (
<button
type="button"
onClick={open}
disabled={busy}
className={cn(
"inline-flex h-9 items-center justify-center gap-1.5 rounded-lg px-3 shrink-0",
"border border-border text-sm font-medium text-foreground hover:bg-muted",
"transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring",
"disabled:opacity-50",
)}
>
{busy ? (
<Loader2 className="size-4 animate-spin" />
) : (
<MessageCircle className="size-4" />
)}
Message
</button>
);
}