Calendar subscription naming (#235)

Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
Camille Moussu
2025-10-24 12:47:18 +02:00
committed by GitHub
parent 34363562cc
commit 8813bff48c
6 changed files with 106 additions and 11 deletions
@@ -1,6 +1,7 @@
// __test__/features/calendars/calendarApi.test.ts
import {
addSharedCalendar,
getCalendar,
getCalendars,
postCalendar,
@@ -112,4 +113,43 @@ describe("Calendar API", () => {
},
});
});
it("When adding a sharedCal with #default as a name a new name is sent to the back", async () => {
const mockApiPost = jest.spyOn(api, "post");
const calData = {
cal: {
id: "cal123",
"dav:name": "#default",
"apple:color": "#FF5733",
"caldav:description": "Default calendar",
acl: [],
invite: [],
_links: {
self: {
href: "/calendars/owner123/cal123.json",
},
},
},
owner: {
displayName: "John Doe",
email: "john.doe@example.com",
openpaasId: "owner123",
},
color: "#FF5733",
};
await addSharedCalendar("currentUserId", "newCalId123", calData);
expect(mockApiPost).toHaveBeenCalledWith(
"dav/calendars/currentUserId.json",
expect.objectContaining({
body: expect.stringContaining('"dav:name":"John Doe\'s calendar"'),
})
);
const callBody = JSON.parse(String(mockApiPost.mock.calls[0][1]?.body));
expect(callBody["dav:name"]).toBe("John Doe's calendar");
expect(callBody["dav:name"]).not.toBe("#default");
});
});
+4 -1
View File
@@ -28,7 +28,10 @@
padding-left 5px
height 100%
flex-direction column
overflow auto
overflow-y: hidden
.sidebar:hover
overflow-y: auto
.declined-event
opacity 0.7
+5 -1
View File
@@ -61,7 +61,11 @@ function CalendarItem({
}}
/>
<Box>
<Typography variant="body1">{cal.cal["dav:name"]}</Typography>
<Typography variant="body1">
{cal.cal["dav:name"] === "#default"
? cal.owner.displayName + "'s calendar"
: cal.cal["dav:name"]}
</Typography>
<Typography variant="body2" color="textSecondary">
{cal.owner.email}
</Typography>
+38 -6
View File
@@ -12,7 +12,7 @@ import IconButton from "@mui/material/IconButton";
import Checkbox from "@mui/material/Checkbox";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import CalendarSearch from "./CalendarSearch";
import { Divider, Menu, MenuItem } from "@mui/material";
import { Divider, ListItem, Menu, MenuItem } from "@mui/material";
import { removeCalendarAsync } from "../../features/Calendars/CalendarSlice";
import { DeleteCalendarDialog } from "./DeleteCalendarDialog";
@@ -41,13 +41,24 @@ function CalendarAccordion({
if (calendars.length === 0 && !defaultExpanded) return null;
return (
<Accordion defaultExpanded={defaultExpanded} expanded={expended}>
<Accordion
defaultExpanded={defaultExpanded}
expanded={expended}
style={{ width: "100%", padding: 0, margin: 0, boxShadow: "none" }}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls={`${title}-content`}
id={`${title}-header`}
className="calendarListHeader"
onClick={() => setExpended(!expended)}
sx={{
"& .MuiAccordionSummary-content": {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
},
}}
>
<Typography component="h3">{title}</Typography>
{showAddButton && (
@@ -62,7 +73,7 @@ function CalendarAccordion({
</IconButton>
)}
</AccordionSummary>
<AccordionDetails>
<AccordionDetails style={{ textAlign: "left", padding: 0 }}>
{calendars.map((id) => (
<CalendarSelector
key={id}
@@ -214,7 +225,24 @@ function CalendarSelector({
};
return (
<>
<div>
<ListItem
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
transition: "background-color 0.2s ease",
padding: "8px 24px 8px 16px",
"& .MoreBtn": {
opacity: 0,
},
"&:hover": {
backgroundColor: "#F3F3F6",
"& .MoreBtn": {
opacity: 1,
},
},
}}
>
<label>
<Checkbox
sx={{
@@ -227,10 +255,14 @@ function CalendarSelector({
/>
{calendars[id].name}
</label>
<IconButton onClick={handleClick}>
<IconButton
className="MoreBtn"
onClick={handleClick}
style={{ alignSelf: "end" }}
>
<MoreVertIcon />
</IconButton>
</div>
</ListItem>
<Menu id={id} anchorEl={anchorEl} open={open} onClose={handleClose}>
<MenuItem
onClick={() => {
+4
View File
@@ -64,6 +64,10 @@ export async function addSharedCalendar(
body: JSON.stringify({
id: calId,
...cal.cal,
"dav:name":
cal.cal["dav:name"] === "#default"
? cal.owner.displayName + "'s calendar"
: cal.cal["dav:name"],
"calendarserver:source": {
acl: cal.cal.acl,
calendarHomeId: cal.cal.id,
+15 -3
View File
@@ -39,7 +39,6 @@ export const getCalendarsListAsync = createAsyncThunk<
const rawCalendars = calendars._embedded["dav:calendar"];
for (const cal of rawCalendars) {
const name = cal["dav:name"];
const description = cal["caldav:description"];
let delegated = false;
let source = cal["calendarserver:source"]
@@ -51,8 +50,16 @@ export const getCalendarsListAsync = createAsyncThunk<
delegated = true;
}
const id = source.replace("/calendars/", "").replace(".json", "");
const ownerId = id.split("/")[0];
const visibility = getCalendarVisibility(cal["acl"]);
const ownerData: any = await getUserDetails(id.split("/")[0]);
const ownerData: any = await getUserDetails(ownerId);
const name =
ownerId !== user.id && cal["dav:name"] === "#default"
? `${ownerData.firstname ? `${ownerData.firstname} ` : ""}${
ownerData.lastname
}` + "'s calendar"
: cal["dav:name"];
const color = {
light: cal["apple:color"] ?? "#006BD8",
dark: cal["X-TWAKE-Dark-theme-color"] ?? "#FFF",
@@ -370,7 +377,12 @@ export const addSharedCalendarAsync = createAsyncThunk<
},
link: `/calendars/${userId}/${calId}.json`,
desc: cal.cal["caldav:description"],
name: cal.cal["dav:name"],
name:
ownerData.id !== userId && cal.cal["dav:name"] === "#default"
? `${ownerData.firstname ? `${ownerData.firstname} ` : ""}${
ownerData.lastname
}` + "'s calendar"
: cal.cal["dav:name"],
owner: `${ownerData.firstname ? `${ownerData.firstname} ` : ""}${
ownerData.lastname
}`,