From 7046f6f80adbd94473db04efc0198099b675c488 Mon Sep 17 00:00:00 2001 From: Achraf <54310457+sekaiking@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:08:54 +0100 Subject: [PATCH] feat: Fix Proton Calendar with ICS (#15454) * feat: fix proton calendar with ics * ics-feed: add note & clarify checkbox * fix review --------- Co-authored-by: Keith Williams Co-authored-by: Amit Sharma <74371312+Amit91848@users.noreply.github.com> --- apps/web/public/static/locales/en/common.json | 2 ++ .../app-store/ics-feedcalendar/api/add.ts | 4 +-- .../ics-feedcalendar/lib/CalendarService.ts | 15 ++++++++++- .../ics-feedcalendar/pages/setup/index.tsx | 25 ++++++++++++++++--- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index 14fa65d5e0..13c86760c4 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -2494,7 +2494,9 @@ "USER_PENDING_MEMBER_OF_THE_ORG": "User is a pending member of the organization", "USER_ALREADY_INVITED_OR_MEMBER": "User is already invited or a member", "USER_MEMBER_OF_OTHER_ORGANIZATION": "User is member of an organization that this team is not a part of.", + "skip_writing_to_calendar": "Do not write to the ICS feed", "rescheduling_not_possible": "Rescheduling is not possible as the event has expired", "event_expired": "This event is expired", + "skip_writing_to_calendar_note": "If your ICS link is read-only (e.g., Proton Calendar), check the box above to avoid errors. You'll also need to manually update your calendar for changes.", "ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑" } diff --git a/packages/app-store/ics-feedcalendar/api/add.ts b/packages/app-store/ics-feedcalendar/api/add.ts index 1ae3d89049..626a838206 100644 --- a/packages/app-store/ics-feedcalendar/api/add.ts +++ b/packages/app-store/ics-feedcalendar/api/add.ts @@ -10,7 +10,7 @@ import { CalendarService } from "../lib"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method === "POST") { - const { urls } = req.body; + const { urls, skipWriting } = req.body; // Get user const user = await prisma.user.findFirstOrThrow({ where: { @@ -24,7 +24,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const data = { type: appConfig.type, - key: symmetricEncrypt(JSON.stringify({ urls }), process.env.CALENDSO_ENCRYPTION_KEY || ""), + key: symmetricEncrypt(JSON.stringify({ urls, skipWriting }), process.env.CALENDSO_ENCRYPTION_KEY || ""), userId: user.id, teamId: null, appId: appConfig.slug, diff --git a/packages/app-store/ics-feedcalendar/lib/CalendarService.ts b/packages/app-store/ics-feedcalendar/lib/CalendarService.ts index ca9821107f..19e425e3e2 100644 --- a/packages/app-store/ics-feedcalendar/lib/CalendarService.ts +++ b/packages/app-store/ics-feedcalendar/lib/CalendarService.ts @@ -40,18 +40,28 @@ const CALENDSO_ENCRYPTION_KEY = process.env.CALENDSO_ENCRYPTION_KEY || ""; export default class ICSFeedCalendarService implements Calendar { private urls: string[] = []; + private skipWriting = false; protected integrationName = "ics-feed_calendar"; constructor(credential: CredentialPayload) { - const { urls } = JSON.parse(symmetricDecrypt(credential.key as string, CALENDSO_ENCRYPTION_KEY)); + const { urls, skipWriting } = JSON.parse( + symmetricDecrypt(credential.key as string, CALENDSO_ENCRYPTION_KEY) + ); this.urls = urls; + this.skipWriting = skipWriting; } createEvent(_event: CalendarEvent, _credentialId: number): Promise { + if (this.skipWriting) { + return Promise.reject(new Error("Event creation is disabled for this calendar.")); + } throw new Error("createEvent called on read-only ICS feed"); } deleteEvent(_uid: string, _event: CalendarEvent, _externalCalendarId?: string): Promise { + if (this.skipWriting) { + return Promise.reject(new Error("Event creation is disabled for this calendar.")); + } throw new Error("deleteEvent called on read-only ICS feed"); } @@ -60,6 +70,9 @@ export default class ICSFeedCalendarService implements Calendar { _event: CalendarEvent, _externalCalendarId?: string ): Promise { + if (this.skipWriting) { + return Promise.reject(new Error("Event creation is disabled for this calendar.")); + } throw new Error("updateEvent called on read-only ICS feed"); } diff --git a/packages/app-store/ics-feedcalendar/pages/setup/index.tsx b/packages/app-store/ics-feedcalendar/pages/setup/index.tsx index 3c0e2307be..17c9897ca2 100644 --- a/packages/app-store/ics-feedcalendar/pages/setup/index.tsx +++ b/packages/app-store/ics-feedcalendar/pages/setup/index.tsx @@ -4,7 +4,7 @@ import { useForm } from "react-hook-form"; import { Toaster } from "react-hot-toast"; import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { Alert, Button, Form, TextField } from "@calcom/ui"; +import { Alert, Button, Form, TextField, CheckboxField } from "@calcom/ui"; import { Icon } from "@calcom/ui"; export default function ICSFeedSetup() { @@ -17,6 +17,7 @@ export default function ICSFeedSetup() { const [urls, setUrls] = useState([""]); const [errorMessage, setErrorMessage] = useState(""); const [errorActionUrl, setErrorActionUrl] = useState(""); + const [skipWriting, setSkipWriting] = useState(false); // track if user opts out of writing to any calendar return (
@@ -40,7 +41,7 @@ export default function ICSFeedSetup() { setErrorMessage(""); const res = await fetch("/api/integrations/ics-feedcalendar/add", { method: "POST", - body: JSON.stringify({ urls }), + body: JSON.stringify({ urls, skipWriting }), headers: { "Content-Type": "application/json", }, @@ -88,9 +89,27 @@ export default function ICSFeedSetup() { onClick={() => { setUrls((urls) => urls.concat("")); }}> - {t("add")} + {t("add")} +
+ setSkipWriting(e.target.checked)} + className="mr-2" + description={t("skip_writing_to_calendar")} + /> +
+ + + {errorMessage && (