feat: share collection URL

This commit is contained in:
gosangam
2026-06-14 01:29:08 +05:30
parent b04ed1d3aa
commit 3accc753d0
5 changed files with 414 additions and 85 deletions
@@ -21,15 +21,18 @@ import { useEffect, useRef, useState } from "react";
import Image from "next/image";
import Link from "next/link";
import {
Check,
ChevronLeft,
ChevronRight,
Globe,
Link2,
Loader2,
Lock,
Pencil,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { collections, type Collection, type Post } from "@/lib/social";
import { useCopyFeedback } from "@/components/selection-toolbar/shared";
import { PostGridCard } from "../post/post-grid-card";
import { quoteCardBgStyle } from "../post/post-card-shared";
@@ -201,6 +204,7 @@ function CoverSlide({
href: string;
onEdit?: () => void;
}) {
const { copied, copy } = useCopyFeedback();
// The slide wrapper hosts the Link AND any owner-action chip as
// siblings. Nesting a button inside the Link is an a11y anti-pattern
// (interactive nested in interactive), so we keep them peers and let
@@ -285,6 +289,23 @@ function CoverSlide({
</div>
</Link>
{/* Copy public link — a peer of the Link (no nested interactives). The
public route /c/<id> carries OG metadata so the pasted link unfurls
with a generated card. */}
<button
type="button"
onClick={() => copy(`${window.location.origin}/c/${c.id}`)}
aria-label={copied ? "Link copied" : "Copy collection link"}
title={copied ? "Link copied" : "Copy collection link"}
className={cn(
"absolute top-3 left-3 z-10 inline-flex size-8 items-center justify-center",
"rounded-full bg-black/50 backdrop-blur-sm hover:bg-black/70",
"text-white transition-colors",
)}
>
{copied ? <Check className="size-4" /> : <Link2 className="size-4" />}
</button>
{onEdit ? (
<button
type="button"