refactor recurring event modification (#173)

This commit is contained in:
Camille Moussu
2025-10-07 18:16:00 +02:00
committed by GitHub
parent 3f8f3eff23
commit c86db8fa49
15 changed files with 1924 additions and 176 deletions
+116 -51
View File
@@ -3,14 +3,6 @@ import CloseIcon from "@mui/icons-material/Close";
import DeleteIcon from "@mui/icons-material/Delete";
import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
import VideocamIcon from "@mui/icons-material/Videocam";
import {
deleteEventAsync,
moveEventAsync,
putEventAsync,
removeEvent,
} from "../Calendars/CalendarSlice";
import { useAppDispatch, useAppSelector } from "../../app/hooks";
import AttendeeSelector from "../../components/Attendees/AttendeeSearch";
import {
Box,
Button,
@@ -33,24 +25,42 @@ import {
Typography,
} from "@mui/material";
import { useEffect, useState } from "react";
import { useAppDispatch, useAppSelector } from "../../app/hooks";
import AttendeeSelector from "../../components/Attendees/AttendeeSearch";
import {
handleDelete,
handleRSVP,
} from "../../components/Event/eventHandlers/eventHandlers";
import RepeatEvent from "../../components/Event/EventRepeat";
import { InfoRow } from "../../components/Event/InfoRow";
import { userAttendee } from "../User/userDataTypes";
import { refreshCalendars } from "../../components/Event/utils/eventUtils";
import { renderAttendeeBadge } from "../../components/Event/utils/eventUtils";
import { getCalendarRange } from "../../utils/dateUtils";
import {
moveEventAsync,
putEventAsync,
removeEvent,
updateEventInstanceAsync,
updateSeriesAsync,
} from "../Calendars/CalendarSlice";
import { Calendars } from "../Calendars/CalendarTypes";
import { userAttendee } from "../User/userDataTypes";
import { getEvent } from "./EventApi";
import { formatLocalDateTime } from "./EventModal";
import { CalendarEvent, RepetitionObject } from "./EventsTypes";
import { renderAttendeeBadge } from "../../components/Event/utils/eventUtils";
export default function EventDisplayModal({
eventId,
calId,
open,
onClose,
typeOfAction,
}: {
eventId: string;
calId: string;
open: boolean;
onClose: (event: {}, reason: "backdropClick" | "escapeKeyDown") => void;
typeOfAction?: "solo" | "all";
}) {
const dispatch = useAppDispatch();
const calendar = useAppSelector((state) => state.calendars.list[calId]);
@@ -87,7 +97,7 @@ export default function EventDisplayModal({
const [alarm, setAlarm] = useState(event?.alarm?.trigger ?? "");
const [busy, setBusy] = useState(event?.transp ?? "OPAQUE");
const [eventClass, setEventClass] = useState(event?.class ?? "PUBLIC");
const [timezone] = useState(event?.timezone ?? "UTC");
const [timezone, setTimezone] = useState(event?.timezone ?? "UTC");
const [newCalId, setNewCalId] = useState(event?.calId);
const [calendarid, setCalendarid] = useState(
calId.split("/")[0] === user.userData?.openpaasId
@@ -119,19 +129,29 @@ export default function EventDisplayModal({
}
setRepetition(event?.repetition ?? ({} as RepetitionObject));
}, [open, eventId, dispatch, onClose, event]);
useEffect(() => {
const fetchMasterEvent = async () => {
const masterEvent = await getEvent(event);
setTitle(masterEvent.title ?? "");
setDescription(masterEvent.description ?? "");
setLocation(masterEvent.location ?? "");
setStart(formatLocalDateTime(new Date(masterEvent?.start ?? Date.now())));
setEnd(formatLocalDateTime(new Date(masterEvent?.end ?? Date.now())));
setAllDay(masterEvent.allday ?? false);
setRepetition(masterEvent?.repetition ?? ({} as RepetitionObject));
setAlarm(masterEvent?.alarm?.trigger ?? "");
setBusy(masterEvent?.transp ?? "OPAQUE");
setEventClass(masterEvent?.class ?? "PUBLIC");
setTimezone(masterEvent.timezone ?? "UTC");
};
if (typeOfAction === "all") {
fetchMasterEvent();
}
}, [typeOfAction, event]);
if (!event || !calendar) return null;
function handleRSVP(rsvp: string) {
const newEvent = {
...event,
attendee: event.attendee?.map((a) =>
a.cal_address === user.userData.email ? { ...a, partstat: rsvp } : a
),
};
dispatch(putEventAsync({ cal: calendar, newEvent }));
}
const isRecurring = event.uid?.includes("/");
const handleSave = async () => {
const newEventUID = crypto.randomUUID();
@@ -157,28 +177,35 @@ export default function EventDisplayModal({
};
const [baseId, recurrenceId] = event.uid.split("/");
if (recurrenceId) {
Object.keys(userPersonnalCalendars[calendarid].events).forEach(
(element) => {
if (element.split("/")[0] === baseId) {
dispatch(removeEvent({ calendarUid: calId, eventUid: element }));
}
}
const calendarRange = getCalendarRange(new Date(start));
if (typeOfAction === "solo") {
dispatch(
updateEventInstanceAsync({
cal: userPersonnalCalendars[calendarid],
event: { ...newEvent, recurrenceId: recurrenceId },
})
);
} else if (typeOfAction === "all") {
dispatch(
updateSeriesAsync({
cal: userPersonnalCalendars[calendarid],
event: { ...newEvent, recurrenceId: recurrenceId },
})
);
await refreshCalendars(dispatch, calendars, calendarRange);
} else {
dispatch(
putEventAsync({ cal: userPersonnalCalendars[calendarid], newEvent })
);
}
await dispatch(
putEventAsync({
cal: userPersonnalCalendars[calendarid],
newEvent,
})
);
if (newCalId !== calId) {
dispatch(
moveEventAsync({
cal: userPersonnalCalendars[calendarid],
newEvent,
newURL: `/calendars/${newCalId}/${event.uid}.ics`,
newURL: `/calendars/${newCalId}/${baseId}.ics`,
})
);
dispatch(removeEvent({ calendarUid: calId, eventUid: event.uid }));
@@ -268,7 +295,17 @@ export default function EventDisplayModal({
? "success"
: "primary"
}
onClick={() => handleRSVP("ACCEPTED")}
onClick={() =>
handleRSVP(
dispatch,
calendar,
user,
event,
"ACCEPTED",
undefined,
isRecurring ? typeOfAction : undefined
)
}
>
Accept
</Button>
@@ -278,7 +315,17 @@ export default function EventDisplayModal({
? "warning"
: "primary"
}
onClick={() => handleRSVP("TENTATIVE")}
onClick={() =>
handleRSVP(
dispatch,
calendar,
user,
event,
"TENTATIVE",
undefined,
isRecurring ? typeOfAction : undefined
)
}
>
Maybe
</Button>
@@ -288,7 +335,17 @@ export default function EventDisplayModal({
? "error"
: "primary"
}
onClick={() => handleRSVP("DECLINED")}
onClick={() =>
handleRSVP(
dispatch,
calendar,
user,
event,
"DECLINED",
undefined,
isRecurring ? typeOfAction : undefined
)
}
>
Decline
</Button>
@@ -463,12 +520,14 @@ export default function EventDisplayModal({
{/* Extended options */}
{showMore && (
<>
<RepeatEvent
repetition={repetition}
eventStart={event.start}
setRepetition={setRepetition}
isOwn={isOwn}
/>
{isOwn && (
<RepeatEvent
repetition={repetition}
eventStart={event.start}
setRepetition={setRepetition}
isOwn={isOwn && typeOfAction !== "solo"}
/>
)}
<FormControl fullWidth margin="dense" size="small">
<InputLabel id="notification">Notification</InputLabel>
<Select
@@ -546,12 +605,18 @@ export default function EventDisplayModal({
{isOwn && (
<IconButton
size="small"
onClick={() => {
onClose({}, "backdropClick");
dispatch(
deleteEventAsync({ calId, eventId, eventURL: event.URL })
);
}}
onClick={() =>
handleDelete(
isRecurring,
typeOfAction,
onClose,
dispatch,
calendar,
event,
calId,
eventId
)
}
>
<DeleteIcon fontSize="small" />
</IconButton>