feat: copy caption in post actions menu
This commit is contained in:
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user