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 <keithwillcode@gmail.com> Co-authored-by: Alex van Andel <me@alexvanandel.com>
This commit is contained in:
parent
a153f9627d
commit
57384eb921
|
@ -716,10 +716,7 @@ const EventTypeListHeading = ({
|
|||
<Avatar
|
||||
alt={profile?.name || ""}
|
||||
href={teamId ? `/settings/teams/${teamId}/profile` : "/settings/my-account/profile"}
|
||||
imageSrc={
|
||||
`${orgBranding?.fullDomain ?? WEBAPP_URL}/${teamId ? "team/" : ""}${profile.slug}/avatar.png` ||
|
||||
undefined
|
||||
}
|
||||
imageSrc={`${orgBranding?.fullDomain ?? WEBAPP_URL}/${profile.slug}/avatar.png` || undefined}
|
||||
size="md"
|
||||
className="mt-1 inline-flex justify-center"
|
||||
/>
|
||||
|
|
|
@ -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 (
|
||||
<SkeletonLoader title={t("profile")} description={t("profile_description", { appName: APP_NAME })} />
|
||||
);
|
||||
|
||||
const defaultValues = {
|
||||
username: user.username || "",
|
||||
avatar: avatar.avatar || "",
|
||||
avatar: user.avatar || "",
|
||||
name: user.name || "",
|
||||
email: user.email || "",
|
||||
bio: user.bio || "",
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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) {
|
|||
)}>
|
||||
<Avatar
|
||||
size={small ? "xs" : "xsm"}
|
||||
imageSrc={avatar?.avatar || WEBAPP_URL + "/" + user.username + "/avatar.png"}
|
||||
imageSrc={WEBAPP_URL + "/" + user.username + "/avatar.png"}
|
||||
alt={user.username || "Nameless User"}
|
||||
className="overflow-hidden"
|
||||
/>
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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<S
|
|||
theme: true,
|
||||
createdDate: true,
|
||||
hideBranding: true,
|
||||
avatar: true,
|
||||
twoFactorEnabled: true,
|
||||
disableImpersonation: true,
|
||||
identityProvider: true,
|
||||
brandColor: true,
|
||||
darkBrandColor: true,
|
||||
away: true,
|
||||
credentials: {
|
||||
select: {
|
||||
id: true,
|
||||
type: true,
|
||||
key: true,
|
||||
userId: true,
|
||||
appId: true,
|
||||
invalid: true,
|
||||
teamId: true,
|
||||
},
|
||||
orderBy: {
|
||||
id: "asc",
|
||||
},
|
||||
},
|
||||
selectedCalendars: {
|
||||
select: {
|
||||
externalId: true,
|
||||
|
@ -102,11 +86,8 @@ export async function getUserFromSession(ctx: TRPCContextInner, session: Maybe<S
|
|||
|
||||
const userMetaData = userMetadata.parse(user.metadata || {});
|
||||
const orgMetadata = teamMetadataSchema.parse(user.organization?.metadata || {});
|
||||
const rawAvatar = user.avatar;
|
||||
// This helps to prevent reaching the 4MB payload limit by avoiding base64 and instead passing the avatar url
|
||||
user.avatar = rawAvatar
|
||||
? `${WEBAPP_URL}/${user.username}/avatar.png?orgId=${user.organizationId}`
|
||||
: defaultAvatarSrc({ email });
|
||||
|
||||
const locale = user?.locale || ctx.locale;
|
||||
|
||||
const isOrgAdmin = !!user.organization?.members.length;
|
||||
|
@ -116,13 +97,14 @@ export async function getUserFromSession(ctx: TRPCContextInner, session: Maybe<S
|
|||
}
|
||||
return {
|
||||
...user,
|
||||
avatar:
|
||||
`${WEBAPP_URL}/${user.username}/avatar.png?` + user.organizationId && `orgId=${user.organizationId}`,
|
||||
organization: {
|
||||
...user.organization,
|
||||
isOrgAdmin,
|
||||
metadata: orgMetadata,
|
||||
},
|
||||
id,
|
||||
rawAvatar,
|
||||
email,
|
||||
username,
|
||||
locale,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import getApps from "@calcom/app-store/utils";
|
||||
import { getUsersCredentials } from "@calcom/lib/server/getUsersCredentials";
|
||||
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
|
||||
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
@ -15,7 +16,7 @@ type AppByIdOptions = {
|
|||
export const appByIdHandler = async ({ ctx, input }: AppByIdOptions) => {
|
||||
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) {
|
||||
|
|
|
@ -8,6 +8,6 @@ type AvatarOptions = {
|
|||
|
||||
export const avatarHandler = async ({ ctx }: AvatarOptions) => {
|
||||
return {
|
||||
avatar: ctx.user.rawAvatar,
|
||||
avatar: ctx.user.avatar,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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] : []),
|
||||
],
|
||||
});
|
||||
|
|
|
@ -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) =>
|
||||
|
|
|
@ -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 ;
|
||||
|
|
|
@ -52,9 +52,14 @@ export function Avatar(props: AvatarProps) {
|
|||
/>
|
||||
<AvatarPrimitive.Fallback delayMs={600} asChild={props.asChild} className="flex items-center">
|
||||
<>
|
||||
{props.fallback && !gravatarFallbackMd5 && props.fallback}
|
||||
{gravatarFallbackMd5 && (
|
||||
<img src={defaultAvatarSrc({ md5: gravatarFallbackMd5 })} alt={alt} className={rootClass} />
|
||||
{props.fallback ? (
|
||||
props.fallback
|
||||
) : (
|
||||
<img
|
||||
src={defaultAvatarSrc({ md5: gravatarFallbackMd5 ?? "fallback" })}
|
||||
alt={alt}
|
||||
className={rootClass}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</AvatarPrimitive.Fallback>
|
||||
|
|
Loading…
Reference in New Issue
Block a user