mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-02 08:15:55 +00:00
Invitations - Teams above all - leave virtualLocations empty: LOCATION reads "Microsoft Teams Meeting" and the join URL sits in the description between help and dial-in links, so the popover showed no meeting link and "Copy meeting link" was not offered. Find the join URL of known conferencing services (Teams, Zoom, Meet, Webex, Jitsi, Whereby, GoTo) in the location or description, unwrapping Outlook Safe Links. Nothing is written to the event. The location was plain text unless it was a URL. It is now always a link: a URL opens as is, a location that only names the service joins the meeting, anything else opens a Google Maps search (the Maps app on a phone). A copy button sits next to it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
112 lines
2.8 KiB
TypeScript
112 lines
2.8 KiB
TypeScript
"use client";
|
|
|
|
import { useTranslations } from "next-intl";
|
|
import {
|
|
ContextMenu,
|
|
ContextMenuItem,
|
|
ContextMenuSeparator,
|
|
} from "@/components/ui/context-menu";
|
|
import {
|
|
Pencil,
|
|
Copy,
|
|
Download,
|
|
ClipboardCopy,
|
|
Link as LinkIcon,
|
|
CalendarArrowUp as CalendarLinkIcon,
|
|
Trash2,
|
|
} from "@/components/icons";
|
|
import type { CalendarEvent } from "@/lib/jmap/types";
|
|
import { buildCalendarPath } from "@/lib/deep-links";
|
|
import { useCopyLink } from "@/hooks/use-copy-link";
|
|
import { findMeetingLink } from "@/lib/event-links";
|
|
|
|
interface Position {
|
|
x: number;
|
|
y: number;
|
|
}
|
|
|
|
interface EventContextMenuProps {
|
|
event: CalendarEvent;
|
|
position: Position;
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
menuRef: React.RefObject<HTMLDivElement | null>;
|
|
onEdit: () => void;
|
|
onDuplicate: () => void;
|
|
onExportICS: () => void;
|
|
onCopyTitle: () => void;
|
|
onCopyMeetingLink?: () => void;
|
|
onDelete: () => void;
|
|
}
|
|
|
|
export function EventContextMenu({
|
|
event,
|
|
position,
|
|
isOpen,
|
|
onClose,
|
|
menuRef,
|
|
onEdit,
|
|
onDuplicate,
|
|
onExportICS,
|
|
onCopyTitle,
|
|
onCopyMeetingLink,
|
|
onDelete,
|
|
}: EventContextMenuProps) {
|
|
const t = useTranslations("calendar");
|
|
const tDeepLink = useTranslations("deep_link");
|
|
const copyLink = useCopyLink();
|
|
|
|
const handle = (fn: () => void) => () => {
|
|
fn();
|
|
onClose();
|
|
};
|
|
|
|
const hasMeetingLink = !!findMeetingLink(event);
|
|
|
|
return (
|
|
<ContextMenu ref={menuRef} isOpen={isOpen} position={position} onClose={onClose}>
|
|
<ContextMenuItem icon={Pencil} label={t("events.edit")} onClick={handle(onEdit)} />
|
|
<ContextMenuItem icon={Copy} label={t("events.duplicate")} onClick={handle(onDuplicate)} />
|
|
<ContextMenuSeparator />
|
|
<ContextMenuItem
|
|
icon={Download}
|
|
label={t("events.export_ics")}
|
|
onClick={handle(onExportICS)}
|
|
/>
|
|
<ContextMenuItem
|
|
icon={ClipboardCopy}
|
|
label={t("events.copy_title")}
|
|
onClick={handle(onCopyTitle)}
|
|
/>
|
|
{hasMeetingLink && onCopyMeetingLink && (
|
|
<ContextMenuItem
|
|
icon={LinkIcon}
|
|
label={t("events.copy_link")}
|
|
onClick={handle(onCopyMeetingLink)}
|
|
/>
|
|
)}
|
|
{/* Permalink into Bulwark (#733) - distinct from the meeting URL above,
|
|
which points at whatever conferencing tool the organiser used. */}
|
|
<ContextMenuItem
|
|
icon={CalendarLinkIcon}
|
|
label={tDeepLink("copy_event")}
|
|
onClick={handle(() => {
|
|
void copyLink(buildCalendarPath({
|
|
view: "month",
|
|
date: null,
|
|
eventId: event.id,
|
|
accountId: event.accountId,
|
|
}));
|
|
})}
|
|
/>
|
|
<ContextMenuSeparator />
|
|
<ContextMenuItem
|
|
icon={Trash2}
|
|
label={t("events.delete")}
|
|
onClick={handle(onDelete)}
|
|
destructive
|
|
/>
|
|
</ContextMenu>
|
|
);
|
|
}
|