perf: Slim down loggedInViewer tRPC router (#20111)

* perf: Slim down loggedInViewer tRPC router

* Fixed trpc client calls for new routes

* Moved bookingUnconfirmedCount

* Moved getUserTopBanners to me router

* Moved shouldVerifyEmail to me router

* Moved i18n from public to its own router

* fix ssrInit

* fix ssrInit usage

* fix type check

* better naming

* fix

* fix

* Fix types

* Removed used of importHandler

---------

Co-authored-by: hbjORbj <sldisek783@gmail.com>
This commit is contained in:
Keith Williams
2025-03-16 18:22:48 -07:00
committed by GitHub
co-authored by hbjORbj
parent 424d94f16d
commit 20d6eb5200
81 changed files with 162 additions and 188 deletions
@@ -174,7 +174,7 @@ const organizationAdminKeys = [
const useTabs = ({ isDelegationCredentialEnabled }: { isDelegationCredentialEnabled: boolean }) => {
const session = useSession();
const { data: user } = trpc.viewer.me.useQuery({ includePasswordAdded: true });
const { data: user } = trpc.viewer.me.get.useQuery({ includePasswordAdded: true });
const orgBranding = useOrgBranding();
const isAdmin = session.data?.user.role === UserPermissionRole.ADMIN;
const isOrgAdminOrOwner =
+1 -1
View File
@@ -2,7 +2,7 @@ import { CALCOM_VERSION } from "@calcom/lib/constants";
import { trpc } from "@calcom/trpc/react";
export function useViewerI18n(locale: string) {
return trpc.viewer.public.i18n.useQuery(
return trpc.viewer.i18n.get.useQuery(
{ locale, CalComVersion: CALCOM_VERSION },
{
/**
@@ -15,7 +15,7 @@ type FormData = {
};
const UserProfile = () => {
const [user] = trpc.viewer.me.useSuspenseQuery();
const [user] = trpc.viewer.me.get.useSuspenseQuery();
const { t } = useLocale();
const avatarRef = useRef<HTMLInputElement>(null);
const { setValue, handleSubmit, getValues } = useForm<FormData>({
@@ -56,7 +56,7 @@ const UserProfile = () => {
console.error(error);
}
await utils.viewer.me.refetch();
await utils.viewer.me.get.refetch();
const redirectUrl = localStorage.getItem("onBoardingRedirect");
localStorage.removeItem("onBoardingRedirect");
redirectUrl ? router.push(redirectUrl) : router.push("/");
@@ -21,7 +21,7 @@ interface IUserSettingsProps {
const UserSettings = (props: IUserSettingsProps) => {
const { nextStep } = props;
const [user] = trpc.viewer.me.useSuspenseQuery();
const [user] = trpc.viewer.me.get.useSuspenseQuery();
const { t } = useLocale();
const { setTimezone: setSelectedTimeZone, timezone: selectedTimeZone } = useTimePreferences();
const telemetry = useTelemetry();
@@ -1,7 +1,7 @@
import { trpc } from "@calcom/trpc/react";
export function usePlatformMe() {
const meQuery = trpc.viewer.platformMe.useQuery(undefined, {
const meQuery = trpc.viewer.me.platformMe.useQuery(undefined, {
retry(failureCount) {
return failureCount > 3;
},
@@ -64,7 +64,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
onErrorMutation,
readonly: disabled,
} = props;
const [user] = trpc.viewer.me.useSuspenseQuery();
const [user] = trpc.viewer.me.get.useSuspenseQuery();
const [usernameIsAvailable, setUsernameIsAvailable] = useState(false);
const [markAsError, setMarkAsError] = useState(false);
const recentAttemptPaymentStatus = searchParams?.get("recentAttemptPaymentStatus");
@@ -47,7 +47,7 @@ export const UsernameAvailabilityField = ({
onErrorMutation,
}: UsernameAvailabilityFieldProps) => {
const searchParams = useSearchParams();
const [user] = trpc.viewer.me.useSuspenseQuery();
const [user] = trpc.viewer.me.get.useSuspenseQuery();
const [currentUsernameState, setCurrentUsernameState] = useState(user.username || "");
const { username: usernameFromQuery, setQuery: setUsernameFromQuery } = useRouterQuery("username");
const { username: currentUsername, setQuery: setCurrentUsername } =
@@ -18,7 +18,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
const ssr = await ssrInit(context);
await ssr.viewer.me.prefetch();
await ssr.viewer.me.get.prefetch();
const user = await UserRepository.findUserTeams({
id: session.user.id,
+1 -1
View File
@@ -1,7 +1,7 @@
import { trpc } from "@calcom/trpc/react";
export function useMeQuery() {
const meQuery = trpc.viewer.me.useQuery(undefined, {
const meQuery = trpc.viewer.me.get.useQuery(undefined, {
retry(failureCount) {
return failureCount > 3;
},
@@ -27,7 +27,7 @@ export function AvailabilityList({ schedules }: RouterOutputs["viewer"]["availab
const [bulkUpdateModal, setBulkUpdateModal] = useState(false);
const utils = trpc.useUtils();
const meQuery = trpc.viewer.me.useQuery();
const meQuery = trpc.viewer.me.get.useQuery();
const router = useRouter();
@@ -51,7 +51,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
let userTimeFormat: number | null = null;
let requiresLoginToUpdate = false;
if (session) {
const user = await ssr.viewer.me.fetch();
const user = await ssr.viewer.me.get.fetch();
tz = user.timeZone;
userTimeFormat = user.timeFormat;
}
@@ -89,7 +89,7 @@ const OnboardingPage = (props: PageProps) => {
const params = useParamsWithFallback();
const router = useRouter();
const [user] = trpc.viewer.me.useSuspenseQuery();
const [user] = trpc.viewer.me.get.useSuspenseQuery();
const { t } = useLocale();
const result = stepRouteSchema.safeParse({
@@ -85,7 +85,7 @@ const AppearanceView = ({
user,
hasPaidPlan,
}: {
user: RouterOutputs["viewer"]["me"];
user: RouterOutputs["viewer"]["me"]["get"];
hasPaidPlan: boolean;
}) => {
const { t } = useLocale();
@@ -436,7 +436,7 @@ const AppearanceView = ({
};
const AppearancePage = () => {
const { data: user, isPending } = trpc.viewer.me.useQuery();
const { data: user, isPending } = trpc.viewer.me.get.useQuery();
const { isPending: isTeamPlanStatusLoading, hasPaidPlan } = useHasPaidPlan();
if (isPending || isTeamPlanStatusLoading || !user) return <SkeletonLoader />;
@@ -66,7 +66,7 @@ const SkeletonLoader = () => {
interface GeneralViewProps {
localeProp: string;
user: RouterOutputs["viewer"]["me"];
user: RouterOutputs["viewer"]["me"]["get"];
travelSchedules: RouterOutputs["viewer"]["getTravelSchedules"];
revalidatePage: GeneralQueryViewProps["revalidatePage"];
}
@@ -78,7 +78,7 @@ type GeneralQueryViewProps = {
const GeneralQueryView = ({ revalidatePage }: GeneralQueryViewProps) => {
const { t } = useLocale();
const { data: user, isPending } = trpc.viewer.me.useQuery();
const { data: user, isPending } = trpc.viewer.me.get.useQuery();
const { data: travelSchedules, isPending: isPendingTravelSchedules } =
trpc.viewer.getTravelSchedules.useQuery();
@@ -95,13 +95,13 @@ const ProfileView = () => {
const { t } = useLocale();
const utils = trpc.useUtils();
const { update } = useSession();
const { data: user, isPending } = trpc.viewer.me.useQuery({ includePasswordAdded: true });
const { data: user, isPending } = trpc.viewer.me.get.useQuery({ includePasswordAdded: true });
const updateProfileMutation = trpc.viewer.updateProfile.useMutation({
onSuccess: async (res) => {
await update(res);
utils.viewer.me.invalidate();
utils.viewer.shouldVerifyEmail.invalidate();
utils.viewer.me.shouldVerifyEmail.invalidate();
if (res.hasEmailBeenChanged && res.sendEmailVerification) {
showToast(t("change_of_email_toast", { email: tempFormValues?.email }), "success");
@@ -187,14 +187,14 @@ const ProfileView = () => {
setHasDeleteErrors(true);
setDeleteErrorMessage(errorMessages[error.message]);
};
const deleteMeMutation = trpc.viewer.deleteMe.useMutation({
const deleteMeMutation = trpc.viewer.me.deleteMe.useMutation({
onSuccess: onDeleteMeSuccessMutation,
onError: onDeleteMeErrorMutation,
async onSettled() {
await utils.viewer.me.invalidate();
},
});
const deleteMeWithoutPasswordMutation = trpc.viewer.deleteMeWithoutPassword.useMutation({
const deleteMeWithoutPasswordMutation = trpc.viewer.me.deleteMeWithoutPassword.useMutation({
onSuccess: onDeleteMeSuccessMutation,
onError: onDeleteMeErrorMutation,
async onSettled() {
@@ -511,8 +511,8 @@ const ProfileForm = ({
extraField?: React.ReactNode;
isPending: boolean;
isFallbackImg: boolean;
user: RouterOutputs["viewer"]["me"];
userOrganization: RouterOutputs["viewer"]["me"]["organization"];
user: RouterOutputs["viewer"]["me"]["get"];
userOrganization: RouterOutputs["viewer"]["me"]["get"]["organization"];
isCALIdentityProvider: boolean;
}) => {
const { t } = useLocale();
@@ -17,7 +17,7 @@ const SkeletonLoader = () => {
);
};
const ProfileImpersonationView = ({ user }: { user: RouterOutputs["viewer"]["me"] }) => {
const ProfileImpersonationView = ({ user }: { user: RouterOutputs["viewer"]["me"]["get"] }) => {
const { t } = useLocale();
const utils = trpc.useUtils();
const [disableImpersonation, setDisableImpersonation] = useState<boolean | undefined>(
@@ -32,8 +32,8 @@ const ProfileImpersonationView = ({ user }: { user: RouterOutputs["viewer"]["me"
utils.viewer.me.invalidate();
},
onMutate: async ({ disableImpersonation }) => {
await utils.viewer.me.cancel();
const previousValue = utils.viewer.me.getData();
await utils.viewer.me.get.cancel();
const previousValue = utils.viewer.me.get.getData();
setDisableImpersonation(disableImpersonation);
@@ -41,7 +41,7 @@ const ProfileImpersonationView = ({ user }: { user: RouterOutputs["viewer"]["me"
},
onError: (error, variables, context) => {
if (context?.previousValue) {
utils.viewer.me.setData(undefined, context.previousValue);
utils.viewer.me.get.setData(undefined, context.previousValue);
setDisableImpersonation(context.previousValue?.disableImpersonation);
}
showToast(`${t("error")}, ${error.message}`, "error");
@@ -68,7 +68,7 @@ const ProfileImpersonationView = ({ user }: { user: RouterOutputs["viewer"]["me"
};
const ProfileImpersonationViewWrapper = () => {
const { data: user, isPending } = trpc.viewer.me.useQuery();
const { data: user, isPending } = trpc.viewer.me.get.useQuery();
if (isPending || !user) return <SkeletonLoader />;
return <ProfileImpersonationView user={user} />;
@@ -33,7 +33,7 @@ type ChangePasswordSessionFormValues = {
};
interface PasswordViewProps {
user: RouterOutputs["viewer"]["me"];
user: RouterOutputs["viewer"]["me"]["get"];
}
const SkeletonLoader = () => {
@@ -72,12 +72,12 @@ const PasswordView = ({ user }: PasswordViewProps) => {
utils.viewer.me.invalidate();
},
onMutate: async () => {
await utils.viewer.me.cancel();
const previousValue = await utils.viewer.me.getData();
await utils.viewer.me.get.cancel();
const previousValue = await utils.viewer.me.get.getData();
const previousMetadata = userMetadataSchema.safeParse(previousValue?.metadata);
if (previousValue && sessionTimeout && previousMetadata.success) {
utils.viewer.me.setData(undefined, {
utils.viewer.me.get.setData(undefined, {
...previousValue,
metadata: { ...previousMetadata?.data, sessionTimeout: sessionTimeout },
});
@@ -86,7 +86,7 @@ const PasswordView = ({ user }: PasswordViewProps) => {
},
onError: (error, _, context) => {
if (context?.previousValue) {
utils.viewer.me.setData(undefined, context.previousValue);
utils.viewer.me.get.setData(undefined, context.previousValue);
}
showToast(`${t("session_timeout_change_error")}, ${error.message}`, "error");
},
@@ -305,7 +305,7 @@ const PasswordView = ({ user }: PasswordViewProps) => {
};
const PasswordViewWrapper = () => {
const { data: user, isPending } = trpc.viewer.me.useQuery({ includePasswordAdded: true });
const { data: user, isPending } = trpc.viewer.me.get.useQuery({ includePasswordAdded: true });
const { t } = useLocale();
if (isPending || !user) return <SkeletonLoader />;
@@ -26,7 +26,7 @@ const TwoFactorAuthView = () => {
const utils = trpc.useUtils();
const { t } = useLocale();
const { data: user, isPending } = trpc.viewer.me.useQuery({ includePasswordAdded: true });
const { data: user, isPending } = trpc.viewer.me.get.useQuery({ includePasswordAdded: true });
const [enableModalOpen, setEnableModalOpen] = useState<boolean>(false);
const [disableModalOpen, setDisableModalOpen] = useState<boolean>(false);
+1 -1
View File
@@ -8,7 +8,7 @@ import { Button } from "@calcom/ui";
export const TeamsCTA = () => {
const { t } = useLocale();
const [user] = trpc.viewer.me.useSuspenseQuery();
const [user] = trpc.viewer.me.get.useSuspenseQuery();
return !user.organizationId || user.organization.isOrgAdmin ? (
<Button
data-testid="new-team-btn"
+1 -1
View File
@@ -10,7 +10,7 @@ import { Button, EmptyScreen } from "@calcom/ui";
import { showToast } from "@calcom/ui";
export type OrgUpgradeBannerProps = {
data: RouterOutputs["viewer"]["getUserTopBanners"]["orgUpgradeBanner"];
data: RouterOutputs["viewer"]["me"]["getUserTopBanners"]["orgUpgradeBanner"];
};
export default function UpgradePage() {
+4
View File
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { i18nRouter } from "@calcom/trpc/server/routers/viewer/i18n/_router";
export default createNextApiHandler(i18nRouter, true, "i18n");
+4
View File
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { meRouter } from "@calcom/trpc/server/routers/viewer/me/_router";
export default createNextApiHandler(meRouter);
@@ -14,9 +14,11 @@ test.describe("Forgot password", async () => {
// Got to reset password flow
await page.goto("/auth/forgot-password");
await page.waitForSelector("text=Forgot Password?");
await page.fill('input[name="email"]', `${user.username}@example.com`);
await page.press('input[name="email"]', "Enter");
await page.waitForLoadState("networkidle");
// wait for confirm page.
await page.waitForSelector("text=Reset link sent");
+2
View File
@@ -1043,6 +1043,8 @@ export async function login(
//login
await page.goto("/");
await page.waitForSelector("text=Welcome back");
await emailLocator.fill(user.email ?? `${user.username}@example.com`);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await passwordLocator.fill(user.password ?? user.username!);
+8
View File
@@ -19,6 +19,7 @@ test.describe("unauthorized user sees correct translations (de)", async () => {
await page.locator("html[dir=ltr]").waitFor({ state: "attached" });
{
await page.waitForSelector("text=Willkommen zurück");
const locator = page.getByText("Willkommen zurück", { exact: true });
expect(await locator.count()).toEqual(1);
}
@@ -43,6 +44,7 @@ test.describe("unauthorized user sees correct translations (ar)", async () => {
await page.locator("html[dir=rtl]").waitFor({ state: "attached" });
{
await page.waitForSelector("text=أهلاً بك من جديد");
const locator = page.getByText("أهلاً بك من جديد", { exact: true });
expect(await locator.count()).toEqual(1);
}
@@ -67,6 +69,7 @@ test.describe("unauthorized user sees correct translations (zh)", async () => {
await page.locator("html[dir=ltr]").waitFor({ state: "attached" });
{
await page.waitForSelector("text=欢迎回来");
const locator = page.getByText("欢迎回来", { exact: true });
expect(await locator.count()).toEqual(1);
}
@@ -91,6 +94,7 @@ test.describe("unauthorized user sees correct translations (zh-CN)", async () =>
await page.locator("html[dir=ltr]").waitFor({ state: "attached" });
{
await page.waitForSelector("text=欢迎回来");
const locator = page.getByText("欢迎回来", { exact: true });
expect(await locator.count()).toEqual(1);
}
@@ -115,6 +119,7 @@ test.describe("unauthorized user sees correct translations (zh-TW)", async () =>
await page.locator("html[dir=ltr]").waitFor({ state: "attached" });
{
await page.waitForSelector("text=歡迎回來");
const locator = page.getByText("歡迎回來", { exact: true });
expect(await locator.count()).toEqual(1);
}
@@ -139,6 +144,7 @@ test.describe("unauthorized user sees correct translations (pt)", async () => {
await page.locator("html[dir=ltr]").waitFor({ state: "attached" });
{
await page.waitForSelector("text=Olá novamente");
const locator = page.getByText("Olá novamente", { exact: true });
expect(await locator.count()).toEqual(1);
}
@@ -163,6 +169,7 @@ test.describe("unauthorized user sees correct translations (pt-br)", async () =>
await page.locator("html[dir=ltr]").waitFor({ state: "attached" });
{
await page.waitForSelector("text=Bem-vindo(a) novamente");
const locator = page.getByText("Bem-vindo(a) novamente", { exact: true });
expect(await locator.count()).toEqual(1);
}
@@ -188,6 +195,7 @@ test.describe("unauthorized user sees correct translations (es-419)", async () =
await page.locator("html[dir=ltr]").waitFor({ state: "attached" });
{
await page.waitForSelector("text=Bienvenido de nuevo");
const locator = page.getByText("Bienvenido de nuevo", { exact: true });
expect(await locator.count()).toEqual(1);
}
+4 -8
View File
@@ -7,25 +7,21 @@ import { getLocale } from "@calcom/features/auth/lib/getLocale";
import { map } from "@calcom/features/flags/server/procedures/map";
import { CALCOM_VERSION } from "@calcom/lib/constants";
import { createContext } from "@calcom/trpc/server/createContext";
import { me } from "@calcom/trpc/server/routers/loggedInViewer/procedures/me";
import { teamsAndUserProfilesQuery } from "@calcom/trpc/server/routers/loggedInViewer/procedures/teamsAndUserProfilesQuery";
import { event } from "@calcom/trpc/server/routers/publicViewer/procedures/event";
import { session } from "@calcom/trpc/server/routers/publicViewer/procedures/session";
import { get } from "@calcom/trpc/server/routers/viewer/eventTypes/procedures/get";
import { meRouter } from "@calcom/trpc/server/routers/viewer/me/_router";
import { hasTeamPlan } from "@calcom/trpc/server/routers/viewer/teams/procedures/hasTeamPlan";
import { router, mergeRouters } from "@calcom/trpc/server/trpc";
import { mergeRouters, router } from "@calcom/trpc/server/trpc";
import { createServerSideHelpers } from "@trpc/react-query/server";
const loggedInRouter = router({
me,
});
// Temporary workaround for OOM issue, import only procedures that are called on the server side
const routerSlice = router({
viewer: mergeRouters(
loggedInRouter,
router({
me: meRouter,
features: router({
map,
}),
@@ -82,7 +78,7 @@ export async function ssrInit(context: GetServerSidePropsContext, options?: { no
// Provides a better UX to the users who have already upgraded.
ssr.viewer.teams.hasTeamPlan.prefetch(),
ssr.viewer.public.session.prefetch(),
ssr.viewer.me.prefetch(),
ssr.viewer.me.get.prefetch(),
]);
return ssr;
+1 -1
View File
@@ -59,7 +59,7 @@ export const InstallAppButton = (
disableInstall?: boolean;
} & InstallAppButtonProps
) => {
const { isPending: isUserLoading, data: user } = trpc.viewer.me.useQuery();
const { isPending: isUserLoading, data: user } = trpc.viewer.me.get.useQuery();
const router = useRouter();
const proProtectionElementRef = useRef<HTMLDivElement | null>(null);
const { isPending: isTeamPlanStatusLoading, hasTeamPlan } = useHasTeamPlan();
@@ -6,7 +6,7 @@ import { Badge } from "@calcom/ui";
export default function UnconfirmedBookingBadge() {
const { t } = useLocale();
const { data: unconfirmedBookingCount } = trpc.viewer.bookingUnconfirmedCount.useQuery();
const { data: unconfirmedBookingCount } = trpc.viewer.me.bookingUnconfirmedCount.useQuery();
if (!unconfirmedBookingCount) return null;
return (
<Link href="/bookings/unconfirmed">
@@ -7,7 +7,7 @@ import { trpc } from "@calcom/trpc/react";
import { showToast, TopBanner } from "@calcom/ui";
export type OrgUpgradeBannerProps = {
data: RouterOutputs["viewer"]["getUserTopBanners"]["orgUpgradeBanner"];
data: RouterOutputs["viewer"]["me"]["getUserTopBanners"]["orgUpgradeBanner"];
};
export function OrgUpgradeBanner({ data }: OrgUpgradeBannerProps) {
@@ -60,7 +60,7 @@ const OrgGeneralView = () => {
isPending,
error,
} = trpc.viewer.organizations.listCurrent.useQuery(undefined, {});
const { data: user } = trpc.viewer.me.useQuery();
const { data: user } = trpc.viewer.me.get.useQuery();
useEffect(
function refactorMeWithoutEffect() {
@@ -20,7 +20,7 @@ export const isFreshChatEnabled =
nonEmptySchema.safeParse(host).success && nonEmptySchema.safeParse(token).success;
export default function FreshChatScript() {
const { data } = trpc.viewer.me.useQuery();
const { data } = trpc.viewer.me.get.useQuery();
return (
<Script
id="fresh-chat-sdk"
@@ -25,8 +25,8 @@ const useIntercomHook = isInterComEnabled
export const useIntercom = () => {
const hookData = useIntercomHook();
const { data } = trpc.viewer.me.useQuery();
const { data: statsData } = trpc.viewer.myStats.useQuery(undefined, {
const { data } = trpc.viewer.me.get.useQuery();
const { data: statsData } = trpc.viewer.me.myStats.useQuery(undefined, {
trpc: {
context: {
skipBatch: true,
@@ -128,8 +128,8 @@ export const useIntercom = () => {
export const useBootIntercom = () => {
const { boot } = useIntercom();
const { data: user } = trpc.viewer.me.useQuery();
const { data: statsData } = trpc.viewer.myStats.useQuery(undefined, {
const { data: user } = trpc.viewer.me.get.useQuery();
const { data: statsData } = trpc.viewer.me.myStats.useQuery(undefined, {
trpc: {
context: {
skipBatch: true,
@@ -24,7 +24,7 @@ export default function TeamList(props: Props) {
const utils = trpc.useUtils();
const { t } = useLocale();
const { data: user } = trpc.viewer.me.useQuery();
const { data: user } = trpc.viewer.me.get.useQuery();
const [hideDropdown, setHideDropdown] = useState(false);
@@ -46,7 +46,7 @@ export default function TeamListItem(props: Props) {
const searchParams = useCompatSearchParams();
const { t } = useLocale();
const utils = trpc.useUtils();
const user = trpc.viewer.me.useQuery().data;
const user = trpc.viewer.me.get.useQuery().data;
const team = props.team;
const showDialog = searchParams?.get("inviteModal") === "true";
@@ -30,7 +30,7 @@ export function TeamsListing() {
}
);
const { data: user } = trpc.viewer.me.useQuery();
const { data: user } = trpc.viewer.me.get.useQuery();
const { mutate: inviteMemberByToken } = trpc.viewer.teams.inviteMemberByToken.useMutation({
onSuccess: (teamName) => {
@@ -6,7 +6,7 @@ import type { RouterOutputs } from "@calcom/trpc/react";
import { showToast, TopBanner } from "@calcom/ui";
export type TeamsUpgradeBannerProps = {
data: RouterOutputs["viewer"]["getUserTopBanners"]["teamUpgradeBanner"];
data: RouterOutputs["viewer"]["me"]["getUserTopBanners"]["teamUpgradeBanner"];
};
export function TeamsUpgradeBanner({ data }: TeamsUpgradeBannerProps) {
@@ -18,7 +18,7 @@ import { AddActionDialog } from "./AddActionDialog";
import { DeleteDialog } from "./DeleteDialog";
import WorkflowStepContainer from "./WorkflowStepContainer";
type User = RouterOutputs["viewer"]["me"];
type User = RouterOutputs["viewer"]["me"]["get"];
interface Props {
form: UseFormReturn<FormValues>;
@@ -58,7 +58,7 @@ import { whatsappReminderTemplate } from "../lib/reminders/templates/whatsapp";
import type { FormValues } from "../pages/workflow";
import { TimeTimeUnitInput } from "./TimeTimeUnitInput";
type User = RouterOutputs["viewer"]["me"];
type User = RouterOutputs["viewer"]["me"]["get"];
type WorkflowStepProps = {
step?: WorkflowStep;
+1 -1
View File
@@ -698,7 +698,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
{ id: parsedEventId },
{ enabled: !Number.isNaN(parsedEventId) && embedType === "email", refetchOnWindowFocus: false }
);
const { data: userSettings } = trpc.viewer.me.useQuery();
const { data: userSettings } = trpc.viewer.me.get.useQuery();
const teamSlug = !!eventTypeData?.team ? eventTypeData.team.slug : null;
+1 -1
View File
@@ -8,7 +8,7 @@ import { useEmbedTypes } from "./lib/hooks";
export const EventTypeEmbedDialog = () => {
const types = useEmbedTypes();
const { data: user } = trpc.viewer.me.useQuery();
const { data: user } = trpc.viewer.me.get.useQuery();
return (
<EmbedDialog
+1 -1
View File
@@ -8,7 +8,7 @@ import { useEmbedTypes } from "./lib/hooks";
export const RoutingFormEmbedDialog = () => {
const types = useEmbedTypes();
const { data: user } = trpc.viewer.me.useQuery();
const { data: user } = trpc.viewer.me.get.useQuery();
const routingFormTypes = types.filter((type) => type.type !== "email");
return (
<EmbedDialog
@@ -101,7 +101,7 @@ type BookingField = z.infer<typeof fieldSchema>;
export type EventAdvancedBaseProps = Pick<EventTypeSetupProps, "eventType" | "team"> & {
user?: Partial<
Pick<RouterOutputs["viewer"]["me"], "email" | "secondaryEmails" | "theme" | "defaultBookerLayouts">
Pick<RouterOutputs["viewer"]["me"]["get"], "email" | "secondaryEmails" | "theme" | "defaultBookerLayouts">
>;
isUserLoading?: boolean;
showToast: (message: string, variant: "success" | "warning" | "error") => void;
@@ -70,7 +70,7 @@ type EventTypeScheduleDetailsProps = {
schedule: Array<Pick<ScheduleQueryData["schedule"][number], "days" | "startTime" | "endTime">>;
};
isSchedulePending?: boolean;
user?: Pick<RouterOutputs["viewer"]["me"], "timeFormat" | "weekStart">;
user?: Pick<RouterOutputs["viewer"]["me"]["get"], "timeFormat" | "weekStart">;
editAvailabilityRedirectUrl?: string;
customClassNames?: AvailabilityTableCustomClassNames;
};
@@ -34,7 +34,7 @@ type BookerLayoutSelectorProps = {
isLoading?: boolean;
isDisabled?: boolean;
isOuterBorder?: boolean;
user?: Partial<Pick<RouterOutputs["viewer"]["me"], "defaultBookerLayouts">>;
user?: Partial<Pick<RouterOutputs["viewer"]["me"]["get"], "defaultBookerLayouts">>;
isUserLoading?: boolean;
};
@@ -104,7 +104,7 @@ type BookerLayoutFieldsProps = {
showUserSettings: boolean;
isDark?: boolean;
isOuterBorder?: boolean;
user?: Partial<Pick<RouterOutputs["viewer"]["me"], "defaultBookerLayouts">>;
user?: Partial<Pick<RouterOutputs["viewer"]["me"]["get"], "defaultBookerLayouts">>;
isUserLoading?: boolean;
};
@@ -69,7 +69,7 @@ const TimezoneChangeDialogContent = () => {
};
export function useOpenTimezoneDialog() {
const { data: user } = trpc.viewer.me.useQuery();
const { data: user } = trpc.viewer.me.get.useQuery();
const [showDialog, setShowDialog] = useState(false);
const { data: userSession, status } = useSession();
@@ -7,7 +7,7 @@ import { trpc } from "@calcom/trpc/react";
import { type AllBannerProps } from "./LayoutBanner";
const _useBanners = () => {
const { data: getUserTopBanners, isPending } = trpc.viewer.getUserTopBanners.useQuery();
const { data: getUserTopBanners, isPending } = trpc.viewer.me.getUserTopBanners.useQuery();
const { data: userSession } = useSession();
if (isPending || !userSession) return null;
@@ -18,7 +18,7 @@ import classNames from "@calcom/ui/classNames";
export function ProfileDropdown() {
const { update, data: sessionData } = useSession();
const { data } = trpc.viewer.me.useQuery();
const { data } = trpc.viewer.me.get.useQuery();
const [menuOpen, setMenuOpen] = useState(false);
const refreshData = useRefreshData();
@@ -5,7 +5,7 @@ import { type RouterOutputs } from "@calcom/trpc";
import { TopBanner } from "@calcom/ui";
export type CalendarCredentialBannerProps = {
data: RouterOutputs["viewer"]["getUserTopBanners"]["calendarCredentialBanner"];
data: RouterOutputs["viewer"]["me"]["getUserTopBanners"]["calendarCredentialBanner"];
};
function CalendarCredentialBanner({ data }: CalendarCredentialBannerProps) {
@@ -5,7 +5,7 @@ import { type RouterOutputs } from "@calcom/trpc";
import { TopBanner } from "@calcom/ui";
export type InvalidAppCredentialBannersProps = {
data: RouterOutputs["viewer"]["getUserTopBanners"]["invalidAppCredentialBanners"];
data: RouterOutputs["viewer"]["me"]["getUserTopBanners"]["invalidAppCredentialBanners"];
};
export function InvalidAppCredentialBanners({ data }: InvalidAppCredentialBannersProps) {
+1 -1
View File
@@ -8,7 +8,7 @@ export function useHasPaidPlan() {
const { data: hasTeamPlan, isPending: isPendingTeamQuery } = trpc.viewer.teams.hasTeamPlan.useQuery();
const { data: user, isPending: isPendingUserQuery } = trpc.viewer.me.useQuery();
const { data: user, isPending: isPendingUserQuery } = trpc.viewer.me.get.useQuery();
const isPending = isPendingTeamQuery || isPendingUserQuery;
@@ -11,7 +11,7 @@ import { trpc } from "@calcom/trpc/react";
export type EventAvailabilityTabWebWrapperProps = {
eventType: EventTypeSetup;
isTeamEvent: boolean;
user?: RouterOutputs["viewer"]["me"];
user?: RouterOutputs["viewer"]["me"]["get"];
teamMembers: TeamMembers;
};
@@ -1,7 +1,7 @@
import { trpc } from "../trpc";
export function useEmailVerifyCheck() {
const emailCheck = trpc.viewer.shouldVerifyEmail.useQuery(undefined, {
const emailCheck = trpc.viewer.me.shouldVerifyEmail.useQuery(undefined, {
retry(failureCount) {
return failureCount > 3;
},
+1 -1
View File
@@ -1,7 +1,7 @@
import { trpc } from "../trpc";
export function useMeQuery() {
const meQuery = trpc.viewer.me.useQuery(undefined, {
const meQuery = trpc.viewer.me.get.useQuery(undefined, {
retry(failureCount) {
return failureCount > 3;
},
+2
View File
@@ -14,7 +14,9 @@ export const ENDPOINTS = [
"eventTypes",
"features",
"highPerf",
"i18n",
"insights",
"me",
"payments",
"public",
"timezones",
+1 -1
View File
@@ -67,7 +67,7 @@ export function createNextApiHandler(router: AnyRouter, isPublic = false, namesp
// i18n and cityTimezones are now being accessed using the CalComVersion, which updates on every release,
// letting the clients get the new versions when the version number changes.
i18n: SETTING_FOR_CACHED_BY_VERSION,
"i18n.get": SETTING_FOR_CACHED_BY_VERSION,
cityTimezones: SETTING_FOR_CACHED_BY_VERSION,
// FIXME: Using `max-age=1, stale-while-revalidate=60` fails some booking tests.
@@ -7,7 +7,6 @@ import { ZAppCredentialsByTypeInputSchema } from "./appCredentialsByType.schema"
import { ZConnectAndJoinInputSchema } from "./connectAndJoin.schema";
import { ZConnectedCalendarsInputSchema } from "./connectedCalendars.schema";
import { ZDeleteCredentialInputSchema } from "./deleteCredential.schema";
import { ZDeleteMeInputSchema } from "./deleteMe.schema";
import { ZEventTypeOrderInputSchema } from "./eventTypeOrder.schema";
import { ZGetCalVideoRecordingsInputSchema } from "./getCalVideoRecordings.schema";
import { ZGetDownloadLinkOfCalVideoRecordingsInputSchema } from "./getDownloadLinkOfCalVideoRecordings.schema";
@@ -17,9 +16,6 @@ import { ZNoShowInputSchema } from "./markNoShow.schema";
import { ZOutOfOfficeInputSchema } from "./outOfOfficeCreateOrUpdate.schema";
import { ZOutOfOfficeEntriesListSchema } from "./outOfOfficeEntriesList.schema";
import { ZOutOfOfficeDelete } from "./outOfOfficeEntryDelete.schema";
import { me } from "./procedures/me";
import { myStats } from "./procedures/myStats";
import { platformMe } from "./procedures/platformMe";
import { teamsAndUserProfilesQuery } from "./procedures/teamsAndUserProfilesQuery";
import { ZRemoveNotificationsSubscriptionInputSchema } from "./removeNotificationsSubscription.schema";
import { ZRoutingFormOrderInputSchema } from "./routingFormOrder.schema";
@@ -34,12 +30,6 @@ const NAMESPACE = "loggedInViewer";
const namespaced = (s: string) => `${NAMESPACE}.${s}`;
type AppsRouterHandlerCache = {
me?: typeof import("./me.handler").meHandler;
myStats?: typeof import("./myStats.handler").myStatsHandler;
platformMe?: typeof import("./platformMe.handler").platformMeHandler;
shouldVerifyEmail?: typeof import("./shouldVerifyEmail.handler").shouldVerifyEmailHandler;
deleteMe?: typeof import("./deleteMe.handler").deleteMeHandler;
deleteMeWithoutPassword?: typeof import("./deleteMeWithoutPassword.handler").deleteMeWithoutPasswordHandler;
connectedCalendars?: typeof import("./connectedCalendars.handler").connectedCalendarsHandler;
setDestinationCalendar?: typeof import("./setDestinationCalendar.handler").setDestinationCalendarHandler;
integrations?: typeof import("./integrations.handler").integrationsHandler;
@@ -53,13 +43,11 @@ type AppsRouterHandlerCache = {
submitFeedback?: typeof import("./submitFeedback.handler").submitFeedbackHandler;
locationOptions?: typeof import("./locationOptions.handler").locationOptionsHandler;
deleteCredential?: typeof import("./deleteCredential.handler").deleteCredentialHandler;
bookingUnconfirmedCount?: typeof import("./bookingUnconfirmedCount.handler").bookingUnconfirmedCountHandler;
getCalVideoRecordings?: typeof import("./getCalVideoRecordings.handler").getCalVideoRecordingsHandler;
getDownloadLinkOfCalVideoRecordings?: typeof import("./getDownloadLinkOfCalVideoRecordings.handler").getDownloadLinkOfCalVideoRecordingsHandler;
getUsersDefaultConferencingApp?: typeof import("./getUsersDefaultConferencingApp.handler").getUsersDefaultConferencingAppHandler;
updateUserDefaultConferencingApp?: typeof import("./updateUserDefaultConferencingApp.handler").updateUserDefaultConferencingAppHandler;
teamsAndUserProfilesQuery?: typeof import("./teamsAndUserProfilesQuery.handler").teamsAndUserProfilesQuery;
getUserTopBanners?: typeof import("./getUserTopBanners.handler").getUserTopBannersHandler;
connectAndJoin?: typeof import("./connectAndJoin.handler").Handler;
outOfOfficeCreateOrUpdate?: typeof import("./outOfOfficeCreateOrUpdate.handler").outOfOfficeCreateOrUpdate;
outOfOfficeEntriesList?: typeof import("./outOfOfficeEntriesList.handler").outOfOfficeEntriesList;
@@ -75,38 +63,6 @@ type AppsRouterHandlerCache = {
const UNSTABLE_HANDLER_CACHE: AppsRouterHandlerCache = {};
export const loggedInViewerRouter = router({
me,
myStats,
platformMe,
deleteMe: authedProcedure.input(ZDeleteMeInputSchema).mutation(async ({ ctx, input }) => {
if (!UNSTABLE_HANDLER_CACHE.deleteMe) {
UNSTABLE_HANDLER_CACHE.deleteMe = (await import("./deleteMe.handler")).deleteMeHandler;
}
// Unreachable code but required for type safety
if (!UNSTABLE_HANDLER_CACHE.deleteMe) {
throw new Error("Failed to load handler");
}
return UNSTABLE_HANDLER_CACHE.deleteMe({ ctx, input });
}),
deleteMeWithoutPassword: authedProcedure.mutation(async ({ ctx }) => {
if (!UNSTABLE_HANDLER_CACHE.deleteMeWithoutPassword) {
UNSTABLE_HANDLER_CACHE.deleteMeWithoutPassword = (
await import("./deleteMeWithoutPassword.handler")
).deleteMeWithoutPasswordHandler;
}
// Unreachable code but required for type safety
if (!UNSTABLE_HANDLER_CACHE.deleteMeWithoutPassword) {
throw new Error("Failed to load handler");
}
return UNSTABLE_HANDLER_CACHE.deleteMeWithoutPassword({ ctx });
}),
connectedCalendars: authedProcedure.input(ZConnectedCalendarsInputSchema).query(async ({ ctx, input }) => {
if (!UNSTABLE_HANDLER_CACHE.connectedCalendars) {
UNSTABLE_HANDLER_CACHE.connectedCalendars = (
@@ -307,21 +263,6 @@ export const loggedInViewerRouter = router({
return UNSTABLE_HANDLER_CACHE.deleteCredential({ ctx, input });
}),
bookingUnconfirmedCount: authedProcedure.query(async ({ ctx }) => {
if (!UNSTABLE_HANDLER_CACHE.bookingUnconfirmedCount) {
UNSTABLE_HANDLER_CACHE.bookingUnconfirmedCount = (
await import("./bookingUnconfirmedCount.handler")
).bookingUnconfirmedCountHandler;
}
// Unreachable code but required for type safety
if (!UNSTABLE_HANDLER_CACHE.bookingUnconfirmedCount) {
throw new Error("Failed to load handler");
}
return UNSTABLE_HANDLER_CACHE.bookingUnconfirmedCount({ ctx });
}),
getCalVideoRecordings: authedProcedure
.input(ZGetCalVideoRecordingsInputSchema)
.query(async ({ ctx, input }) => {
@@ -339,21 +280,6 @@ export const loggedInViewerRouter = router({
return UNSTABLE_HANDLER_CACHE.getCalVideoRecordings({ ctx, input });
}),
getUserTopBanners: authedProcedure.query(async ({ ctx }) => {
if (!UNSTABLE_HANDLER_CACHE.getUserTopBanners) {
UNSTABLE_HANDLER_CACHE.getUserTopBanners = (
await import("./getUserTopBanners.handler")
).getUserTopBannersHandler;
}
// Unreachable code but required for type safety
if (!UNSTABLE_HANDLER_CACHE.getUserTopBanners) {
throw new Error("Failed to load handler");
}
return UNSTABLE_HANDLER_CACHE.getUserTopBanners({ ctx });
}),
getDownloadLinkOfCalVideoRecordings: authedProcedure
.input(ZGetDownloadLinkOfCalVideoRecordingsInputSchema)
.query(async ({ ctx, input }) => {
@@ -402,20 +328,6 @@ export const loggedInViewerRouter = router({
return UNSTABLE_HANDLER_CACHE.updateUserDefaultConferencingApp({ ctx, input });
}),
shouldVerifyEmail: authedProcedure.query(async ({ ctx }) => {
if (!UNSTABLE_HANDLER_CACHE.shouldVerifyEmail) {
UNSTABLE_HANDLER_CACHE.shouldVerifyEmail = (
await import("./shouldVerifyEmail.handler")
).shouldVerifyEmailHandler;
}
// Unreachable code but required for type safety
if (!UNSTABLE_HANDLER_CACHE.shouldVerifyEmail) {
throw new Error("Failed to load handler");
}
return UNSTABLE_HANDLER_CACHE.shouldVerifyEmail({ ctx });
}),
teamsAndUserProfilesQuery,
connectAndJoin: authedProcedure.input(ZConnectAndJoinInputSchema).mutation(async ({ ctx, input }) => {
if (!UNSTABLE_HANDLER_CACHE.connectAndJoin) {
@@ -1,8 +0,0 @@
import authedProcedure from "../../../procedures/authedProcedure";
import { ZMeInputSchema } from "../me.schema";
export const me = authedProcedure.input(ZMeInputSchema).query(async ({ ctx, input }) => {
const handler = (await import("../me.handler")).meHandler;
return handler({ ctx, input });
});
@@ -2,7 +2,6 @@ import publicProcedure from "../../procedures/publicProcedure";
import { importHandler, router } from "../../trpc";
import { slotsRouter } from "../viewer/slots/_router";
import { ZUserEmailVerificationRequiredSchema } from "./checkIfUserEmailVerificationRequired.schema";
import { i18nInputSchema } from "./i18n.schema";
import { ZMarkHostAsNoShowInputSchema } from "./markHostAsNoShow.schema";
import { event } from "./procedures/event";
import { session } from "./procedures/session";
@@ -17,10 +16,6 @@ const namespaced = (s: string) => `${NAMESPACE}.${s}`;
// things that unauthenticated users can query about themselves
export const publicViewerRouter = router({
session,
i18n: publicProcedure.input(i18nInputSchema).query(async (opts) => {
const handler = await importHandler(namespaced("i18n"), () => import("./i18n.handler"));
return handler(opts);
}),
countryCode: publicProcedure.query(async (opts) => {
const handler = await importHandler(namespaced("countryCode"), () => import("./countryCode.handler"));
return handler(opts);
@@ -21,6 +21,8 @@ import { dsyncRouter } from "./dsync/_router";
import { eventTypesRouter } from "./eventTypes/_router";
import { googleWorkspaceRouter } from "./googleWorkspace/_router";
import { highPerfRouter } from "./highPerf/_router";
import { i18nRouter } from "./i18n/_router";
import { meRouter } from "./me/_router";
import { oAuthRouter } from "./oAuth/_router";
import { viewerOrganizationsRouter } from "./organizations/_router";
import { paymentsRouter } from "./payments/_router";
@@ -35,7 +37,7 @@ export const viewerRouter = mergeRouters(
loggedInViewerRouter,
router({
loggedInViewerRouter,
me: meRouter,
public: publicViewerRouter,
auth: authRouter,
deploymentSetup: deploymentSetupRouter,
@@ -52,6 +54,7 @@ export const viewerRouter = mergeRouters(
workflows: workflowsRouter,
saml: ssoRouter,
dsync: dsyncRouter,
i18n: i18nRouter,
insights: insightsRouter,
payments: paymentsRouter,
// NOTE: Add all app related routes in the bottom till the problem described in @calcom/app-store/trpc-routers.ts is solved.
@@ -0,0 +1,10 @@
import publicProcedure from "../../../procedures/publicProcedure";
import { router } from "../../../trpc";
import { i18nInputSchema } from "./i18n.schema";
export const i18nRouter = router({
get: publicProcedure.input(i18nInputSchema).query(async (opts) => {
const handler = (await import("./i18n.handler")).i18nHandler;
return handler(opts);
}),
});
@@ -0,0 +1,36 @@
import authedProcedure from "../../../procedures/authedProcedure";
import { router } from "../../../trpc";
import { ZDeleteMeInputSchema } from "./deleteMe.schema";
import { get } from "./procedures/get";
export const meRouter = router({
bookingUnconfirmedCount: authedProcedure.query(async ({ ctx }) => {
const handler = (await import("./bookingUnconfirmedCount.handler")).bookingUnconfirmedCountHandler;
return handler({ ctx });
}),
deleteMe: authedProcedure.input(ZDeleteMeInputSchema).mutation(async ({ ctx, input }) => {
const handler = (await import("./deleteMe.handler")).deleteMeHandler;
return handler({ ctx, input });
}),
deleteMeWithoutPassword: authedProcedure.mutation(async ({ ctx }) => {
const handler = (await import("./deleteMeWithoutPassword.handler")).deleteMeWithoutPasswordHandler;
return handler({ ctx });
}),
get,
getUserTopBanners: authedProcedure.query(async ({ ctx }) => {
const handler = (await import("./getUserTopBanners.handler")).getUserTopBannersHandler;
return handler({ ctx });
}),
myStats: authedProcedure.query(async ({ ctx }) => {
const handler = (await import("./myStats.handler")).myStatsHandler;
return handler({ ctx });
}),
platformMe: authedProcedure.query(async ({ ctx }) => {
const handler = (await import("./platformMe.handler")).platformMeHandler;
return handler({ ctx });
}),
shouldVerifyEmail: authedProcedure.query(async ({ ctx }) => {
const handler = (await import("./shouldVerifyEmail.handler")).shouldVerifyEmailHandler;
return handler({ ctx });
}),
});
@@ -8,17 +8,17 @@ import { IdentityProvider, MembershipRole } from "@calcom/prisma/enums";
import { userMetadata } from "@calcom/prisma/zod-utils";
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
import type { TMeInputSchema } from "./me.schema";
import type { TGetInputSchema } from "./get.schema";
type MeOptions = {
ctx: {
user: NonNullable<TrpcSessionUser>;
session: Session;
};
input: TMeInputSchema;
input: TGetInputSchema;
};
export const meHandler = async ({ ctx, input }: MeOptions) => {
export const getHandler = async ({ ctx, input }: MeOptions) => {
const crypto = await import("crypto");
const { user: sessionUser, session } = ctx;
@@ -1,9 +1,9 @@
import { z } from "zod";
export const ZMeInputSchema = z
export const ZGetInputSchema = z
.object({
includePasswordAdded: z.boolean().optional(),
})
.optional();
export type TMeInputSchema = z.infer<typeof ZMeInputSchema>;
export type TGetInputSchema = z.infer<typeof ZGetInputSchema>;
@@ -4,8 +4,8 @@ import { prisma } from "@calcom/prisma";
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
import { checkIfOrgNeedsUpgradeHandler } from "../viewer/organizations/checkIfOrgNeedsUpgrade.handler";
import { getUpgradeableHandler } from "../viewer/teams/getUpgradeable.handler";
import { checkIfOrgNeedsUpgradeHandler } from "../organizations/checkIfOrgNeedsUpgrade.handler";
import { getUpgradeableHandler } from "../teams/getUpgradeable.handler";
import { checkInvalidAppCredentials } from "./checkForInvalidAppCredentials";
import { shouldVerifyEmailHandler } from "./shouldVerifyEmail.handler";
@@ -0,0 +1,8 @@
import authedProcedure from "../../../../procedures/authedProcedure";
import { ZGetInputSchema } from "../get.schema";
export const get = authedProcedure.input(ZGetInputSchema).query(async ({ ctx, input }) => {
const handler = (await import("../get.handler")).getHandler;
return handler({ ctx, input });
});
@@ -1,4 +1,4 @@
import authedProcedure from "../../../procedures/authedProcedure";
import authedProcedure from "../../../../procedures/authedProcedure";
export const myStats = authedProcedure.query(async ({ ctx, input }) => {
const handler = (await import("../myStats.handler")).myStatsHandler;
@@ -1,4 +1,4 @@
import authedProcedure from "../../../procedures/authedProcedure";
import authedProcedure from "../../../../procedures/authedProcedure";
export const platformMe = authedProcedure.query(async ({ ctx }) => {
const handler = (await import("../platformMe.handler")).platformMeHandler;