/** * Renders the action of the signed-in user's own profile header, which invites * someone. * * @module */ import { useState } from "react"; import { t } from "@lingui/core/macro"; import { Trans } from "@lingui/react/macro"; import { API_URL } from "../../config/api.ts"; import { useAuth } from "../../hooks/auth/useAuth.ts"; import { useHeightTransition } from "../../hooks/shared/useHeightTransition.ts"; import { ErrorCard } from "../shared/ErrorCard.tsx"; /** * Renders the "Invite someone" button, which opens a panel with an invite link. * Each opening of the panel creates a new single-use link, for `/register`. */ export function ProfileOwnActions(): React.JSX.Element { const { authFetch } = useAuth(); const [inviteUrl, setInviteUrl] = useState(null); const [pending, setPending] = useState(false); const [copied, setCopied] = useState(false); const [error, setError] = useState(null); const ref = useHeightTransition( (inviteUrl ?? "") + (error ?? ""), ); async function toggle() { setError(null); setCopied(false); if (inviteUrl) { setInviteUrl(null); return; } setPending(true); try { const res = await authFetch(`${API_URL}/api/invites`, { method: "POST" }); const body = await res.json(); if (body.success) { setInviteUrl( `${globalThis.location.origin}/register?token=${body.data.token}`, ); } else { setError(t`Failed to generate invite`); } } catch { setError(t`Failed to generate invite`); } finally { setPending(false); } } async function copy() { if (!inviteUrl) return; try { await navigator.clipboard.writeText(inviteUrl); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch { // The clipboard is denied outside a secure context. The link can still // be selected by hand. } } return (
{inviteUrl && (
e.currentTarget.select()} /> Single use, valid for 7 days.
)} {error && (
)}
); }