From 57384eb921d22249e4d67aee5c07b1076aaad9f0 Mon Sep 17 00:00:00 2001 From: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Date: Thu, 10 Aug 2023 18:07:09 +0100 Subject: [PATCH] perf: Replace un-needed context fetching (#10657) * Replace ctx.user.credentials to fetch in their own usecases * Remove rawavatar from return * Remove avatar rawAvatar from handler * Fix fallback avatars * fix: profile.slug already includes /team * perf: Deprecate useAvatarQuery hook * Extract to reusable credential func * Fix type errors for credentials * credentialOwner was inferred incorrectly, string non-nullable --------- Co-authored-by: Keith Williams Co-authored-by: Alex van Andel --- apps/web/pages/event-types/index.tsx | 5 +--- .../web/pages/settings/my-account/profile.tsx | 7 +++--- .../settings/layouts/SettingsLayout.tsx | 4 +--- packages/features/shell/Shell.tsx | 4 +--- packages/lib/server/getUsersCredentials.ts | 22 +++++++++++++++++ packages/trpc/react/hooks/useAvatarQuery.ts | 13 ---------- .../server/middlewares/sessionMiddleware.ts | 24 +++---------------- .../routers/loggedInViewer/appById.handler.ts | 3 ++- .../routers/loggedInViewer/avatar.handler.ts | 2 +- .../loggedInViewer/integrations.handler.ts | 14 +++++++++-- .../routers/loggedInViewer/me.handler.ts | 3 ++- .../setDestinationCalendar.handler.ts | 4 +++- ...pdateUserDefaultConferencingApp.handler.ts | 3 ++- .../viewer/bookings/confirm.handler.ts | 15 +++++++++++- .../viewer/bookings/editLocation.handler.ts | 5 +++- .../bookings/requestReschedule.handler.ts | 4 +++- .../viewer/eventTypes/create.handler.ts | 3 ++- packages/ui/components/avatar/Avatar.tsx | 11 ++++++--- 18 files changed, 84 insertions(+), 62 deletions(-) create mode 100644 packages/lib/server/getUsersCredentials.ts delete mode 100644 packages/trpc/react/hooks/useAvatarQuery.ts diff --git a/apps/web/pages/event-types/index.tsx b/apps/web/pages/event-types/index.tsx index 01fee72116..2999667fe4 100644 --- a/apps/web/pages/event-types/index.tsx +++ b/apps/web/pages/event-types/index.tsx @@ -716,10 +716,7 @@ const EventTypeListHeading = ({ diff --git a/apps/web/pages/settings/my-account/profile.tsx b/apps/web/pages/settings/my-account/profile.tsx index 95dfb3249b..294d615c46 100644 --- a/apps/web/pages/settings/my-account/profile.tsx +++ b/apps/web/pages/settings/my-account/profile.tsx @@ -77,10 +77,9 @@ type FormValues = { const ProfileView = () => { const { t } = useLocale(); const utils = trpc.useContext(); - const { data: session, update } = useSession(); + const { data: _session, update } = useSession(); const { data: user, isLoading } = trpc.viewer.me.useQuery(); - const { data: avatar, isLoading: isLoadingAvatar } = trpc.viewer.avatar.useQuery(); const updateProfileMutation = trpc.viewer.updateProfile.useMutation({ onSuccess: async (res) => { showToast(t("settings_updated_successfully"), "success"); @@ -205,14 +204,14 @@ const ProfileView = () => { [ErrorCode.ThirdPartyIdentityProviderEnabled]: t("account_created_with_identity_provider"), }; - if (isLoading || !user || isLoadingAvatar || !avatar) + if (isLoading || !user) return ( ); const defaultValues = { username: user.username || "", - avatar: avatar.avatar || "", + avatar: user.avatar || "", name: user.name || "", email: user.email || "", bio: user.bio || "", diff --git a/packages/features/settings/layouts/SettingsLayout.tsx b/packages/features/settings/layouts/SettingsLayout.tsx index 185bc418e4..57857ee354 100644 --- a/packages/features/settings/layouts/SettingsLayout.tsx +++ b/packages/features/settings/layouts/SettingsLayout.tsx @@ -12,7 +12,6 @@ import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { IdentityProvider, MembershipRole, UserPermissionRole } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc/react"; -import useAvatarQuery from "@calcom/trpc/react/hooks/useAvatarQuery"; import type { VerticalTabItemProps } from "@calcom/ui"; import { Badge, Button, ErrorBoundary, Skeleton, useMeta, VerticalTabItem } from "@calcom/ui"; import { @@ -137,7 +136,6 @@ const organizationRequiredKeys = ["organization"]; const useTabs = () => { const session = useSession(); const { data: user } = trpc.viewer.me.useQuery(); - const { data: avatar } = useAvatarQuery(); const isAdmin = session.data?.user.role === UserPermissionRole.ADMIN; @@ -145,7 +143,7 @@ const useTabs = () => { if (tab.href === "/settings/my-account") { tab.name = user?.name || "my_account"; tab.icon = undefined; - tab.avatar = avatar?.avatar || WEBAPP_URL + "/" + session?.data?.user?.username + "/avatar.png"; + tab.avatar = WEBAPP_URL + "/" + session?.data?.user?.username + "/avatar.png"; } else if ( tab.href === "/settings/security" && user?.identityProvider === IdentityProvider.GOOGLE && diff --git a/packages/features/shell/Shell.tsx b/packages/features/shell/Shell.tsx index d25aeccc37..04a07573cc 100644 --- a/packages/features/shell/Shell.tsx +++ b/packages/features/shell/Shell.tsx @@ -29,7 +29,6 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import { isKeyInObject } from "@calcom/lib/isKeyInObject"; import type { User } from "@calcom/prisma/client"; import { trpc } from "@calcom/trpc/react"; -import useAvatarQuery from "@calcom/trpc/react/hooks/useAvatarQuery"; import useEmailVerifyCheck from "@calcom/trpc/react/hooks/useEmailVerifyCheck"; import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery"; import type { SVGComponent } from "@calcom/types/SVGComponent"; @@ -308,7 +307,6 @@ interface UserDropdownProps { function UserDropdown({ small }: UserDropdownProps) { const { t } = useLocale(); const { data: user } = useMeQuery(); - const { data: avatar } = useAvatarQuery(); const utils = trpc.useContext(); useEffect(() => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -373,7 +371,7 @@ function UserDropdown({ small }: UserDropdownProps) { )}> diff --git a/packages/lib/server/getUsersCredentials.ts b/packages/lib/server/getUsersCredentials.ts new file mode 100644 index 0000000000..5a4323647d --- /dev/null +++ b/packages/lib/server/getUsersCredentials.ts @@ -0,0 +1,22 @@ +import { prisma } from "@calcom/prisma"; + +export async function getUsersCredentials(userId: number) { + const credentials = await prisma.credential.findMany({ + where: { + userId, + }, + select: { + id: true, + type: true, + key: true, + userId: true, + appId: true, + invalid: true, + teamId: true, + }, + orderBy: { + id: "asc", + }, + }); + return credentials; +} diff --git a/packages/trpc/react/hooks/useAvatarQuery.ts b/packages/trpc/react/hooks/useAvatarQuery.ts deleted file mode 100644 index 8cf3a8517a..0000000000 --- a/packages/trpc/react/hooks/useAvatarQuery.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { trpc } from "../trpc"; - -export function useAvatarQuery() { - const avatarQuery = trpc.viewer.avatar.useQuery(undefined, { - retry(failureCount) { - return failureCount > 3; - }, - }); - - return avatarQuery; -} - -export default useAvatarQuery; diff --git a/packages/trpc/server/middlewares/sessionMiddleware.ts b/packages/trpc/server/middlewares/sessionMiddleware.ts index e40a717c85..81055e9d2a 100644 --- a/packages/trpc/server/middlewares/sessionMiddleware.ts +++ b/packages/trpc/server/middlewares/sessionMiddleware.ts @@ -1,7 +1,6 @@ import type { Session } from "next-auth"; import { WEBAPP_URL } from "@calcom/lib/constants"; -import { defaultAvatarSrc } from "@calcom/lib/defaultAvatarImage"; import { MembershipRole } from "@calcom/prisma/enums"; import { teamMetadataSchema, userMetadata } from "@calcom/prisma/zod-utils"; @@ -37,27 +36,12 @@ export async function getUserFromSession(ctx: TRPCContextInner, session: Maybe { const { user } = ctx; const appId = input.appId; - const { credentials } = user; + const credentials = await getUsersCredentials(user.id); const apps = getApps(credentials); const appFromDb = apps.find((app) => app.slug === appId); if (!appFromDb) { diff --git a/packages/trpc/server/routers/loggedInViewer/avatar.handler.ts b/packages/trpc/server/routers/loggedInViewer/avatar.handler.ts index 8a4f0ee74d..9ba4e678f6 100644 --- a/packages/trpc/server/routers/loggedInViewer/avatar.handler.ts +++ b/packages/trpc/server/routers/loggedInViewer/avatar.handler.ts @@ -8,6 +8,6 @@ type AvatarOptions = { export const avatarHandler = async ({ ctx }: AvatarOptions) => { return { - avatar: ctx.user.rawAvatar, + avatar: ctx.user.avatar, }; }; diff --git a/packages/trpc/server/routers/loggedInViewer/integrations.handler.ts b/packages/trpc/server/routers/loggedInViewer/integrations.handler.ts index 53dc562fe1..ddf4f8580a 100644 --- a/packages/trpc/server/routers/loggedInViewer/integrations.handler.ts +++ b/packages/trpc/server/routers/loggedInViewer/integrations.handler.ts @@ -1,5 +1,7 @@ +import type { CredentialOwner } from "@calcom/app-store/types"; import getEnabledApps from "@calcom/lib/apps/getEnabledApps"; import getInstallCountPerApp from "@calcom/lib/apps/getInstallCountPerApp"; +import { getUsersCredentials } from "@calcom/lib/server/getUsersCredentials"; import prisma from "@calcom/prisma"; import { MembershipRole } from "@calcom/prisma/enums"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; @@ -43,7 +45,7 @@ export const integrationsHandler = async ({ ctx, input }: IntegrationsOptions) = teamId, sortByMostPopular, } = input; - let { credentials } = user; + let credentials = await getUsersCredentials(user.id); let userTeams: TeamQuery[] = []; if (includeTeamInstalledApps || teamId) { @@ -138,9 +140,17 @@ export const integrationsHandler = async ({ ctx, input }: IntegrationsOptions) = team.members[0].role === MembershipRole.ADMIN || team.members[0].role === MembershipRole.OWNER, }; }); + // type infer as CredentialOwner + const credentialOwner: CredentialOwner = { + name: user.name, + avatar: user.avatar, + }; + return { ...app, - ...(teams.length && { credentialOwner: { name: user.name, avatar: user.avatar } }), + ...(teams.length && { + credentialOwner, + }), userCredentialIds, invalidCredentialIds, teams, diff --git a/packages/trpc/server/routers/loggedInViewer/me.handler.ts b/packages/trpc/server/routers/loggedInViewer/me.handler.ts index 435a303789..6c82950305 100644 --- a/packages/trpc/server/routers/loggedInViewer/me.handler.ts +++ b/packages/trpc/server/routers/loggedInViewer/me.handler.ts @@ -1,3 +1,4 @@ +import { WEBAPP_URL } from "@calcom/lib/constants"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; type MeOptions = { @@ -23,7 +24,7 @@ export const meHandler = async ({ ctx }: MeOptions) => { locale: user.locale, timeFormat: user.timeFormat, timeZone: user.timeZone, - avatar: user.avatar, + avatar: `${WEBAPP_URL}/${user.username}/avatar.png`, createdDate: user.createdDate, trialEndsAt: user.trialEndsAt, defaultScheduleId: user.defaultScheduleId, diff --git a/packages/trpc/server/routers/loggedInViewer/setDestinationCalendar.handler.ts b/packages/trpc/server/routers/loggedInViewer/setDestinationCalendar.handler.ts index 55343b4f32..10bde57dad 100644 --- a/packages/trpc/server/routers/loggedInViewer/setDestinationCalendar.handler.ts +++ b/packages/trpc/server/routers/loggedInViewer/setDestinationCalendar.handler.ts @@ -1,4 +1,5 @@ import { getCalendarCredentials, getConnectedCalendars } from "@calcom/core/CalendarManager"; +import { getUsersCredentials } from "@calcom/lib/server/getUsersCredentials"; import { prisma } from "@calcom/prisma"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; @@ -16,7 +17,8 @@ type SetDestinationCalendarOptions = { export const setDestinationCalendarHandler = async ({ ctx, input }: SetDestinationCalendarOptions) => { const { user } = ctx; const { integration, externalId, eventTypeId } = input; - const calendarCredentials = getCalendarCredentials(user.credentials); + const credentials = await getUsersCredentials(user.id); + const calendarCredentials = getCalendarCredentials(credentials); const { connectedCalendars } = await getConnectedCalendars(calendarCredentials, user.selectedCalendars); const allCals = connectedCalendars.map((cal) => cal.calendars ?? []).flat(); diff --git a/packages/trpc/server/routers/loggedInViewer/updateUserDefaultConferencingApp.handler.ts b/packages/trpc/server/routers/loggedInViewer/updateUserDefaultConferencingApp.handler.ts index f23d5ef8f1..4fb3827bec 100644 --- a/packages/trpc/server/routers/loggedInViewer/updateUserDefaultConferencingApp.handler.ts +++ b/packages/trpc/server/routers/loggedInViewer/updateUserDefaultConferencingApp.handler.ts @@ -1,6 +1,7 @@ import z from "zod"; import getApps from "@calcom/app-store/utils"; +import { getUsersCredentials } from "@calcom/lib/server/getUsersCredentials"; import { prisma } from "@calcom/prisma"; import { userMetadata } from "@calcom/prisma/zod-utils"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; @@ -21,7 +22,7 @@ export const updateUserDefaultConferencingAppHandler = async ({ input, }: UpdateUserDefaultConferencingAppOptions) => { const currentMetadata = userMetadata.parse(ctx.user.metadata); - const credentials = ctx.user.credentials; + const credentials = await getUsersCredentials(ctx.user.id); const foundApp = getApps(credentials, true).filter((app) => app.slug === input.appSlug)[0]; const appLocation = foundApp?.appData?.location; diff --git a/packages/trpc/server/routers/viewer/bookings/confirm.handler.ts b/packages/trpc/server/routers/viewer/bookings/confirm.handler.ts index 75c139f4c6..8d32d64c15 100644 --- a/packages/trpc/server/routers/viewer/bookings/confirm.handler.ts +++ b/packages/trpc/server/routers/viewer/bookings/confirm.handler.ts @@ -9,6 +9,7 @@ import type { EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload"; import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib"; import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType"; import { getTranslation } from "@calcom/lib/server"; +import { getUsersCredentials } from "@calcom/lib/server/getUsersCredentials"; import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat"; import { prisma } from "@calcom/prisma"; import { BookingStatus, MembershipRole, SchedulingType, WebhookTriggerEvents } from "@calcom/prisma/enums"; @@ -212,7 +213,19 @@ export const confirmHandler = async ({ ctx, input }: ConfirmOptions) => { } if (confirmed) { - await handleConfirmation({ user, evt, recurringEventId, prisma, bookingId, booking }); + const credentials = await getUsersCredentials(user.id); + const userWithCredentials = { + ...user, + credentials, + }; + await handleConfirmation({ + user: userWithCredentials, + evt, + recurringEventId, + prisma, + bookingId, + booking, + }); } else { evt.rejectionReason = rejectionReason; if (recurringEventId) { diff --git a/packages/trpc/server/routers/viewer/bookings/editLocation.handler.ts b/packages/trpc/server/routers/viewer/bookings/editLocation.handler.ts index 669c1e3cd2..19695fd445 100644 --- a/packages/trpc/server/routers/viewer/bookings/editLocation.handler.ts +++ b/packages/trpc/server/routers/viewer/bookings/editLocation.handler.ts @@ -4,6 +4,7 @@ import { sendLocationChangeEmails } from "@calcom/emails"; import { parseRecurringEvent } from "@calcom/lib"; import logger from "@calcom/lib/logger"; import { getTranslation } from "@calcom/lib/server"; +import { getUsersCredentials } from "@calcom/lib/server/getUsersCredentials"; import { prisma } from "@calcom/prisma"; import type { AdditionalInformation, CalendarEvent } from "@calcom/types/Calendar"; import type { CredentialPayload } from "@calcom/types/Credential"; @@ -86,10 +87,12 @@ export const editLocationHandler = async ({ ctx, input }: EditLocationOptions) = seatsShowAttendees: booking.eventType?.seatsShowAttendees, }; + const credentials = await getUsersCredentials(ctx.user.id); + const eventManager = new EventManager({ ...ctx.user, credentials: [ - ...(ctx.user.credentials ? ctx.user.credentials : []), + ...(credentials ? credentials : []), ...(conferenceCredential ? [conferenceCredential] : []), ], }); diff --git a/packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts b/packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts index 65818d0f42..b6cb35d58c 100644 --- a/packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts +++ b/packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts @@ -17,6 +17,7 @@ import sendPayload from "@calcom/features/webhooks/lib/sendPayload"; import { isPrismaObjOrUndefined } from "@calcom/lib"; import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType"; import { getTranslation } from "@calcom/lib/server"; +import { getUsersCredentials } from "@calcom/lib/server/getUsersCredentials"; import { prisma } from "@calcom/prisma"; import type { WebhookTriggerEvents } from "@calcom/prisma/enums"; import { BookingStatus, WorkflowMethods } from "@calcom/prisma/enums"; @@ -189,8 +190,9 @@ export const requestRescheduleHandler = async ({ ctx, input }: RequestReschedule // Handling calendar and videos cancellation // This can set previous time as available, until virtual calendar is done + const credentials = await getUsersCredentials(user.id); const credentialsMap = new Map(); - user.credentials.forEach((credential) => { + credentials.forEach((credential) => { credentialsMap.set(credential.type, credential); }); const bookingRefsFiltered: BookingReference[] = bookingToReschedule.references.filter((ref) => diff --git a/packages/trpc/server/routers/viewer/eventTypes/create.handler.ts b/packages/trpc/server/routers/viewer/eventTypes/create.handler.ts index 7cfe4e1c35..87de226fd9 100644 --- a/packages/trpc/server/routers/viewer/eventTypes/create.handler.ts +++ b/packages/trpc/server/routers/viewer/eventTypes/create.handler.ts @@ -4,6 +4,7 @@ import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; import getAppKeysFromSlug from "@calcom/app-store/_utils/getAppKeysFromSlug"; import { DailyLocationType } from "@calcom/app-store/locations"; import getApps from "@calcom/app-store/utils"; +import { getUsersCredentials } from "@calcom/lib/server/getUsersCredentials"; import type { PrismaClient } from "@calcom/prisma/client"; import { SchedulingType } from "@calcom/prisma/enums"; import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils"; @@ -41,7 +42,7 @@ export const createHandler = async ({ ctx, input }: CreateOptions) => { } if (defaultConferencingData && defaultConferencingData.appSlug !== "daily-video") { - const credentials = ctx.user.credentials; + const credentials = await getUsersCredentials(ctx.user.id); const foundApp = getApps(credentials, true).filter( (app) => app.slug === defaultConferencingData.appSlug )[0]; // There is only one possible install here so index [0] is the one we are looking for ; diff --git a/packages/ui/components/avatar/Avatar.tsx b/packages/ui/components/avatar/Avatar.tsx index 1c6c4c34c5..2aaa376dec 100644 --- a/packages/ui/components/avatar/Avatar.tsx +++ b/packages/ui/components/avatar/Avatar.tsx @@ -52,9 +52,14 @@ export function Avatar(props: AvatarProps) { /> <> - {props.fallback && !gravatarFallbackMd5 && props.fallback} - {gravatarFallbackMd5 && ( - {alt} + {props.fallback ? ( + props.fallback + ) : ( + {alt} )}