feat: copy caption in post actions menu

This commit is contained in:
gosangam
2026-06-08 23:45:01 +05:30
parent 421b41cade
commit cf733062f8
6 changed files with 43 additions and 6 deletions
+38 -1
View File
@@ -21,7 +21,7 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { Ban, Flag, MoreVertical, Trash2 } from "lucide-react";
import { Ban, Copy, Flag, MoreVertical, Trash2 } from "lucide-react";
import { toast } from "sonner";
import {
Drawer,
@@ -41,6 +41,8 @@ import { useAuthGate } from "@/components/auth/auth-gate";
import { useMediaQuery } from "@/components/selection-toolbar/shared";
import { ConfirmModal } from "@/components/ui/confirm-modal";
import { ReportDialog, type ReportOption } from "@/components/social/report-dialog";
import { createShortLink } from "@/lib/share";
import { structuredReferenceForPost } from "@/lib/source-reference";
// Post report categories — mirrors the BFF allow-list (postReportCategories).
const POST_REPORT_OPTIONS: ReportOption[] = [
@@ -147,10 +149,45 @@ export function PostCardMenu({
};
}, [open, isMobile]);
// Copy the post's caption + source citation + a (short) link to the post —
// the same payload the share sheet / footer produce. Available to every
// viewer.
async function copyCaption() {
setOpen(false);
try {
const origin = window.location.origin;
let url = "";
if (authorHandle) {
const longPath = `/u/${encodeURIComponent(authorHandle)}/posts#post-${post.id}`;
const code = await createShortLink(longPath);
url = code
? new URL(`/share/${code}`, origin).toString()
: new URL(longPath, origin).toString();
}
const ref = structuredReferenceForPost(post.source_url, post.source_title);
const lines: string[] = [];
if (post.caption) lines.push(`“${post.caption}”`, "");
lines.push("Source:", ref.title);
if (ref.detail) lines.push(ref.detail);
if (url) lines.push(url);
await navigator.clipboard.writeText(lines.join("\n"));
toast.success("Caption copied");
} catch {
toast.error("Could not copy");
}
}
// Build the items list AFTER all hooks have run so a conditional
// early-return doesn't break the rules-of-hooks contract on
// re-renders where viewer state changes.
const items: MenuItem[] = [];
// Copy caption — first item for everyone (owner and non-owner alike).
items.push({
key: "copy",
label: "Copy caption",
icon: <Copy className="size-4" />,
onClick: copyCaption,
});
if (isOwner) {
if (onDelete) {
items.push({