620 bug redundant attendees in the invitation list (#629)

Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
Camille Moussu
2026-03-14 06:37:50 +01:00
committed by GitHub
parent ee33da468d
commit 27be9f10ae
3 changed files with 101 additions and 15 deletions
@@ -76,6 +76,80 @@ describe("parseCalendarEvent", () => {
]);
});
it("dedupes attendees addresses correctly", () => {
const rawData = [
["UID", {}, "text", "event-1"],
["DTSTART", {}, "date-time", "2025-07-18T09:00:00Z"],
["DTEND", {}, "date-time", "2025-07-18T10:00:00Z"],
["SUMMARY", {}, "text", "Team Meeting"],
["DESCRIPTION", {}, "text", "Discuss roadmap"],
["LOCATION", {}, "text", "Zoom"],
["ORGANIZER", { cn: "Alice" }, "cal-address", "alice@example.com"],
[
"ATTENDEE",
{ cn: "Bob", partstat: "ACCEPTED" },
"cal-address",
"bob@example.com",
],
[
"ATTENDEE",
{ cn: "Bob", partstat: "ACCEPTED" },
"cal-address",
"bob@example.com",
],
[
"ATTENDEE",
{ cn: "Bob", partstat: "ACCEPTED" },
"cal-address",
"bob@example.com",
],
["X-OPENPAAS-VIDEOCONFERENCE", {}, "text", "https://meet.link"],
["STATUS", {}, "text", "CONFIRMED"],
["SEQUENCE", {}, "integer", "2"],
["TRANSP", {}, "text", "OPAQUE"],
["CLASS", {}, "text", "PUBLIC"],
["DTSTAMP", {}, "date-time", "2025-07-18T08:00:00Z"],
] as VObjectProperty[];
const result = parseCalendarEvent(
rawData,
baseColor,
calendar,
"/calendars/test.ics"
);
expect(result.uid).toBe("event-1");
expect(result.title).toBe("Team Meeting");
expect(result.description).toBe("Discuss roadmap");
expect(result.location).toBe("Zoom");
expect(result.start).toBe("2025-07-18T09:00:00Z");
expect(result.end).toBe("2025-07-18T10:00:00Z");
expect(result.stamp).toBe("2025-07-18T08:00:00Z");
expect(result.sequence).toBe(2);
expect(result.color).toBe(baseColor);
expect(result.status).toBe("CONFIRMED");
expect(result.transp).toBe("OPAQUE");
expect(result.class).toBe("PUBLIC");
expect(result.x_openpass_videoconference).toBe("https://meet.link");
expect(result.timezone).not.toBeDefined();
expect(result.organizer).toEqual({
cn: "Alice",
cal_address: "alice@example.com",
});
expect(result.attendee).toEqual([
{
cn: "Bob",
cal_address: "bob@example.com",
partstat: "ACCEPTED",
cutype: "INDIVIDUAL",
role: "REQ-PARTICIPANT",
rsvp: "FALSE",
},
]);
});
it("parses a full event correctly", () => {
const rawData = [
["UID", {}, "text", "event-1"],
+10 -5
View File
@@ -297,11 +297,16 @@ export function PeopleSearch({
return;
}
setInputError(null);
const mapped = value.map((v: string | User) =>
typeof v === "string"
? { email: v.trim(), displayName: v.trim() }
: v
);
const mapped = value
.map((v: string | User) =>
typeof v === "string"
? { email: v.trim(), displayName: v.trim() }
: v
)
.filter(
(user, index, self) =>
self.findIndex((u) => u.email === user.email) === index
);
onChange(event, mapped);
}}
slotProps={{
+17 -10
View File
@@ -110,16 +110,23 @@ export function parseCalendarEvent(
}
case "attendee": {
const paramsObj = params as Record<string, string>;
(event.attendee as userAttendee[]).push(
createAttendee({
cn: paramsObj?.cn,
cal_address: String(value).replace(/^mailto:/i, ""),
partstat: paramsObj?.partstat as userAttendee["partstat"],
rsvp: paramsObj?.rsvp as userAttendee["rsvp"],
role: paramsObj?.role as userAttendee["role"],
cutype: paramsObj?.cutype as userAttendee["cutype"],
})
);
if (
!event.attendee?.find(
(attendee) =>
attendee.cal_address === String(value).replace(/^mailto:/i, "")
)
) {
(event.attendee as userAttendee[]).push(
createAttendee({
cn: paramsObj?.cn,
cal_address: String(value).replace(/^mailto:/i, ""),
partstat: paramsObj?.partstat as userAttendee["partstat"],
rsvp: paramsObj?.rsvp as userAttendee["rsvp"],
role: paramsObj?.role as userAttendee["role"],
cutype: paramsObj?.cutype as userAttendee["cutype"],
})
);
}
break;
}
case "dtstamp":