Add destination calendar name to DestinationCalendarSelector (#6701)

* Add destination calendar name

* Type fix

* Search through calendars only for destination calendar credential

* Refactor get connected calendars

* Clean up

---------

Co-authored-by: zomars <zomars@me.com>
This commit is contained in:
Joe Au-Yeung 2023-02-27 13:47:21 -05:00 committed by zomars
parent 27512b16d2
commit db5b802a15
5 changed files with 47 additions and 44 deletions

View File

@ -9,7 +9,12 @@ import { getUid } from "@calcom/lib/CalEventParser";
import { WEBAPP_URL } from "@calcom/lib/constants";
import logger from "@calcom/lib/logger";
import { performance } from "@calcom/lib/server/perfObserver";
import type { CalendarEvent, EventBusyDate, NewCalendarEventType } from "@calcom/types/Calendar";
import type {
CalendarEvent,
EventBusyDate,
IntegrationCalendar,
NewCalendarEventType,
} from "@calcom/types/Calendar";
import type { CredentialPayload, CredentialWithAppName } from "@calcom/types/Credential";
import type { EventResult } from "@calcom/types/EventManager";
@ -31,12 +36,15 @@ export const getCalendarCredentials = (credentials: Array<CredentialPayload>) =>
export const getConnectedCalendars = async (
calendarCredentials: ReturnType<typeof getCalendarCredentials>,
selectedCalendars: { externalId: string }[]
selectedCalendars: { externalId: string }[],
destinationCalendarExternalId?: string
) => {
let destinationCalendar: IntegrationCalendar | undefined;
const connectedCalendars = await Promise.all(
calendarCredentials.map(async (item) => {
try {
const { calendar, integration, credential } = item;
let primary!: IntegrationCalendar;
// Don't leak credentials to the client
const credentialId = credential.id;
@ -48,16 +56,28 @@ export const getConnectedCalendars = async (
}
const cals = await calendar.listCalendars();
const calendars = _(cals)
.map((cal) => ({
...cal,
readOnly: cal.readOnly || false,
primary: cal.primary || null,
isSelected: selectedCalendars.some((selected) => selected.externalId === cal.externalId),
credentialId,
}))
.map((cal) => {
if (cal.primary) {
primary = { ...cal, credentialId };
}
if (cal.externalId === destinationCalendarExternalId) {
destinationCalendar = cal;
}
return {
...cal,
readOnly: cal.readOnly || false,
primary: cal.primary || null,
isSelected: selectedCalendars.some((selected) => selected.externalId === cal.externalId),
credentialId,
};
})
.sortBy(["primary"])
.value();
const primary = calendars.find((item) => item.primary) ?? calendars.find((cal) => cal !== undefined);
if (primary && destinationCalendar) {
destinationCalendar.primaryEmail = primary.email;
destinationCalendar.integrationTitle = integration.title;
}
if (!primary) {
return {
integration,
@ -95,7 +115,7 @@ export const getConnectedCalendars = async (
})
);
return connectedCalendars;
return { connectedCalendars, destinationCalendar };
};
/**

View File

@ -1,9 +1,10 @@
import classNames from "classnames";
import { useEffect, useState } from "react";
import { components, OptionProps, SingleValueProps } from "react-select";
import type { OptionProps, SingleValueProps } from "react-select";
import { components } from "react-select";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { DestinationCalendar } from "@calcom/prisma/client";
import type { DestinationCalendar } from "@calcom/prisma/client";
import { trpc } from "@calcom/trpc/react";
import { Select } from "@calcom/ui";
@ -117,10 +118,6 @@ const DestinationCalendarSelector = ({
})),
})) ?? [];
// Get primary calendar, which is shown in the placeholder since this is the calendar that will
// be used when no destination calendar is selected.
const primaryCalendar = query.data.destinationCalendarEmail;
const queryDestinationCalendar = query.data.destinationCalendar;
return (
@ -134,7 +131,7 @@ const DestinationCalendarSelector = ({
<span className="min-w-0 overflow-hidden truncate whitespace-nowrap">
{t("default_calendar_selected")}{" "}
{queryDestinationCalendar.name &&
`(${queryDestinationCalendar?.integration} - ${queryDestinationCalendar.name})`}
`| ${queryDestinationCalendar.name} (${queryDestinationCalendar?.integrationTitle} - ${queryDestinationCalendar.primaryEmail})`}
</span>
)
}

View File

@ -1,4 +1,5 @@
import { AppCategories, BookingStatus, DestinationCalendar, IdentityProvider, Prisma } from "@prisma/client";
import type { DestinationCalendar, Prisma } from "@prisma/client";
import { AppCategories, BookingStatus, IdentityProvider } from "@prisma/client";
import _ from "lodash";
import { authenticator } from "otplib";
import z from "zod";
@ -321,10 +322,11 @@ const loggedInViewerRouter = router({
const calendarCredentials = getCalendarCredentials(userCredentials);
// get all the connected integrations' calendars (from third party)
const connectedCalendars = await getConnectedCalendars(calendarCredentials, user.selectedCalendars);
// store email of the destination calendar to display
let destinationCalendarEmail = null;
const { connectedCalendars, destinationCalendar } = await getConnectedCalendars(
calendarCredentials,
user.selectedCalendars,
user.destinationCalendar?.externalId
);
if (connectedCalendars.length === 0) {
/* As there are no connected calendars, delete the destination calendar if it exists */
@ -348,7 +350,6 @@ const loggedInViewerRouter = router({
credentialId,
},
});
destinationCalendarEmail = email ?? user.destinationCalendar?.externalId;
} else {
/* There are connected calendars and a destination calendar */
@ -371,32 +372,14 @@ const loggedInViewerRouter = router({
},
});
}
destinationCalendarEmail = destinationCal?.email ?? user.destinationCalendar?.externalId;
}
let destinationCalendarName = user.destinationCalendar?.externalId || "";
let destinationCalendarIntegration = "";
for (const integration of connectedCalendars) {
if (integration.calendars) {
for (const calendar of integration.calendars) {
if (calendar.externalId === user.destinationCalendar?.externalId) {
destinationCalendarName = calendar.name || calendar.externalId;
destinationCalendarIntegration = integration.integration.title || "";
break;
}
}
}
}
return {
connectedCalendars,
destinationCalendar: {
...(user.destinationCalendar as DestinationCalendar),
name: destinationCalendarName,
integration: destinationCalendarIntegration,
...destinationCalendar,
},
destinationCalendarEmail,
};
}),
setDestinationCalendar: authedProcedure

View File

@ -304,7 +304,7 @@ export const bookingsRouter = router({
const recurringInfo = recurringInfoBasic.map(
(
info: (typeof recurringInfoBasic)[number]
info: typeof recurringInfoBasic[number]
): {
recurringEventId: string | null;
count: number;

View File

@ -181,6 +181,9 @@ export interface IntegrationCalendar extends Ensure<Partial<SelectedCalendar>, "
readOnly?: boolean;
// For displaying the connected email address
email?: string;
primaryEmail?: string;
credentialId?: number;
integrationTitle?: string;
}
export interface Calendar {