import React, { useState, useRef, useEffect } from "react"; import { Copy, ExternalLink, Check, Share2, MoreHorizontal, } from "lucide-react"; import { AturiIcon, BlueskyIcon, BlackskyIcon, WitchskyIcon, CatskyIcon, DeerIcon, } from "../common/Icons"; const SembleLogo = () => ( Semble ); const BLUESKY_COLOR = "#1185fe"; interface ShareMenuProps { uri: string; text?: string; customUrl?: string; handle?: string; type?: string; url?: string; } export default function ShareMenu({ uri, text, customUrl, handle, type, url, }: ShareMenuProps) { const [isOpen, setIsOpen] = useState(false); const [copied, setCopied] = useState(null); const menuRef = useRef(null); const buttonRef = useRef(null); const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0, alignRight: false, }); const getShareUrl = () => { if (customUrl) return customUrl; if (!uri) return ""; const uriParts = uri.split("/"); const rkey = uriParts[uriParts.length - 1]; const did = uriParts[2]; if (uri.includes("network.cosmik.card")) return `${window.location.origin}/at/${did}/${rkey}`; if (handle && type) return `${window.location.origin}/${handle}/${type.toLowerCase()}/${rkey}`; return `${window.location.origin}/at/${did}/${rkey}`; }; const shareUrl = getShareUrl(); const isSemble = uri && uri.includes("network.cosmik"); const sembleUrl = (() => { if (!isSemble) return ""; const parts = (uri || "").split("/"); const rkey = parts[parts.length - 1]; const userHandle = handle || (parts.length > 2 ? parts[2] : ""); if (uri.includes("network.cosmik.collection")) return `https://semble.so/profile/${userHandle}/collections/${rkey}`; if (uri.includes("network.cosmik.card") && url) return `https://semble.so/url?id=${encodeURIComponent(url)}`; return `https://semble.so/profile/${userHandle}`; })(); const handleCopy = async (textToCopy: string, key: string) => { try { await navigator.clipboard.writeText(textToCopy); setCopied(key); setTimeout(() => { setCopied(null); setIsOpen(false); }, 1000); } catch { prompt("Copy this link:", textToCopy); } }; const handleShareToFork = (domain: string) => { const composeText = text ? `${text.substring(0, 200)}...\n\n${shareUrl}` : shareUrl; const composeUrl = `https://${domain}/intent/compose?text=${encodeURIComponent(composeText)}`; window.open(composeUrl, "_blank"); setIsOpen(false); }; useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if ( menuRef.current && !menuRef.current.contains(e.target as Node) && !buttonRef.current?.contains(e.target as Node) ) { setIsOpen(false); } }; if (isOpen) { document.addEventListener("mousedown", handleClickOutside); window.addEventListener("scroll", () => setIsOpen(false), true); window.addEventListener("resize", () => setIsOpen(false)); } return () => { document.removeEventListener("mousedown", handleClickOutside); window.removeEventListener("scroll", () => setIsOpen(false), true); window.removeEventListener("resize", () => setIsOpen(false)); }; }, [isOpen]); const calculatePosition = () => { if (!buttonRef.current) return; const rect = buttonRef.current.getBoundingClientRect(); const menuWidth = 240; let top = rect.bottom + 8; let left = rect.left; let alignRight = false; if (left + menuWidth > window.innerWidth - 16) { left = rect.right - menuWidth; alignRight = true; } if (top + 300 > window.innerHeight) { top = rect.top - 8; } setMenuPosition({ top, left, alignRight }); }; const toggleMenu = () => { if (!isOpen) calculatePosition(); setIsOpen(!isOpen); }; const renderMenuItem = ( label: string, icon: React.ReactNode, onClick: () => void, isCopied: boolean = false, highlight: boolean = false, ) => ( ); const shareForks = [ { name: "Bluesky", domain: "bsky.app", icon: , }, { name: "Witchsky", domain: "witchsky.app", icon: , }, { name: "Blacksky", domain: "blacksky.community", icon: , }, { name: "Catsky", domain: "catsky.social", icon: }, { name: "Deer", domain: "deer.social", icon: }, ]; return (
{isOpen && (
{isSemble ? ( <>
Semble Integration
{renderMenuItem( "Open on Semble", , () => window.open(sembleUrl, "_blank"), false, true, )} {renderMenuItem( "Copy Semble Link", , () => handleCopy(sembleUrl, "semble"), copied === "semble", )}
) : null} {renderMenuItem( "Copy Link", , () => handleCopy(shareUrl, "link"), copied === "link", )}
Share via App
{shareForks.map((fork) => ( ))}
{renderMenuItem( "Copy Universal Link", , () => handleCopy(uri.replace("at://", "https://aturi.to/"), "aturi"), copied === "aturi", )} {navigator.share && renderMenuItem( "More Options...", , () => { navigator .share({ title: "Margin", text, url: shareUrl }) .catch(() => {}); setIsOpen(false); }, )}
)}
); }