From c859230862c0a7851c2d1e239f527e7c96a65a06 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Mon, 30 Mar 2026 15:19:47 +0200 Subject: [PATCH] feat: add virtual location input #121 --- components/calendar/event-modal.tsx | 36 ++++++++++++++++++++++++++++- locales/de/common.json | 1 + locales/en/common.json | 1 + locales/es/common.json | 1 + locales/fr/common.json | 1 + locales/it/common.json | 1 + locales/ja/common.json | 1 + locales/nl/common.json | 1 + locales/pt/common.json | 1 + locales/ru/common.json | 1 + 10 files changed, 44 insertions(+), 1 deletion(-) diff --git a/components/calendar/event-modal.tsx b/components/calendar/event-modal.tsx index c6178d06..b0901965 100644 --- a/components/calendar/event-modal.tsx +++ b/components/calendar/event-modal.tsx @@ -188,6 +188,9 @@ export function EventModal({ const [location, setLocation] = useState( event?.locations ? Object.values(event.locations)[0]?.name || "" : "" ); + const [virtualLocation, setVirtualLocation] = useState( + event?.virtualLocations ? Object.values(event.virtualLocations)[0]?.uri || "" : "" + ); const [startDate, setStartDate] = useState(formatDateInput(getInitialStart())); const [startTime, setStartTime] = useState(formatTimeInput(getInitialStart())); const [endDate, setEndDate] = useState(formatDateInput(getInitialEnd())); @@ -313,6 +316,20 @@ export function EventModal({ data.locations = null; } + if (virtualLocation.trim()) { + data.virtualLocations = { + vl1: { + "@type": "VirtualLocation", + name: null, + description: null, + uri: virtualLocation.trim(), + features: null, + }, + }; + } else if (event && event.virtualLocations && Object.keys(event.virtualLocations).length > 0) { + data.virtualLocations = null; + } + if (recurrence !== "none") { data.recurrenceRules = [{ "@type": "RecurrenceRule", @@ -372,7 +389,7 @@ export function EventModal({ } finally { setIsSaving(false); } - }, [title, description, location, startDate, startTime, endDate, endTime, allDay, calendarId, recurrence, alert, attendees, sendInvitations, currentUserEmails, existingParticipants, event, onSave, isSaving]); + }, [title, description, location, virtualLocation, startDate, startTime, endDate, endTime, allDay, calendarId, recurrence, alert, attendees, sendInvitations, currentUserEmails, existingParticipants, event, onSave, isSaving]); const handleRsvp = useCallback((status: CalendarParticipant['participationStatus']) => { if (!event || !userParticipantId || !onRsvp) return; @@ -401,6 +418,7 @@ export function EventModal({ privacy: event.privacy, }; if (event.locations) data.locations = structuredClone(event.locations); + if (event.virtualLocations) data.virtualLocations = structuredClone(event.virtualLocations); if (event.recurrenceRules) data.recurrenceRules = structuredClone(event.recurrenceRules); if (event.alerts) data.alerts = structuredClone(event.alerts); if (event.participants) data.participants = structuredClone(event.participants); @@ -778,6 +796,22 @@ export function EventModal({ /> +
+ + setVirtualLocation(e.target.value)} + placeholder="https://meet.example.com/..." + maxLength={2000} + /> +
+