From 0b1947dd4cd6950b4e75bfa7634af9542a0a9f53 Mon Sep 17 00:00:00 2001 From: mohanadft Date: Mon, 31 Aug 2026 16:16:12 +0300 Subject: [PATCH] feat(events): add shareable per-event links Adds a "copy link" button to the event modal on both /events and /events-new that encodes the event's id as ?event=, and reopens that event's modal on load when the param is present, so a specific event can be shared directly instead of only the whole page or category. --- src/components/Events.tsx | 46 ++++++++++++++++++++++++-- src/components/events/EventModal.tsx | 33 ++++++++++++++++-- src/components/events/EventsNew.tsx | 18 ++++++++-- src/components/events/eventsShared.tsx | 22 ++++++++++++ src/utils/copyAnchorLink.ts | 17 ++++++++++ src/utils/eventSections.ts | 7 ++++ 6 files changed, 137 insertions(+), 6 deletions(-) diff --git a/src/components/Events.tsx b/src/components/Events.tsx index b47043bf..4ca0057c 100644 --- a/src/components/Events.tsx +++ b/src/components/Events.tsx @@ -1,14 +1,15 @@ -import { useContext, useEffect, useState } from "react"; +import { useContext, useEffect, useRef, useState } from "react"; import { Dialog, DialogContent, DialogActions } from "@mui/material"; import { hasMeaningfulDescription, primaryEventLink, type EventItem } from "../store/eventsClient"; import { displayTitle, getUpcomingEvents, groupIntoSections, + isEventPast, type EventSection, type UpcomingEvent, } from "../utils/eventSections"; -import { copyAnchorLink } from "../utils/copyAnchorLink"; +import { copyAnchorLink, copyEventLink } from "../utils/copyAnchorLink"; import { formatSpeakerList, getDescriptionExcerpt, @@ -22,6 +23,7 @@ import { ChevronDown, CloseIcon, EventModalContext, + ShareIcon, useCarouselScroll, useEventDate, type SelectedEvent, @@ -171,6 +173,14 @@ function EventDetailsDialogBody({ } = useEventDate(event); const { link: infoLink, label: infoLabel } = primaryEventLink(event, isPast); const title = displayTitle(event); + const [copied, setCopied] = useState(false); + + const handleCopyLink = async () => { + const ok = await copyEventLink(event.id); + if (!ok) return; + setCopied(true); + window.setTimeout(() => setCopied(false), COPIED_FEEDBACK_MS); + }; // MUI Dialog is kept for its focus trap, escape handling, and scroll lock; // everything inside the paper is plain markup. @@ -194,6 +204,24 @@ function EventDetailsDialogBody({ alt={title} className="max-h-[70vh] w-auto max-w-full object-contain" /> + + + {copied && ( + + Copied! + + )} + + {copied && ( + + Copied! + + )} +