diff --git a/packages/features/ee/event-tracking/lib/posthog/userPostHog.tsx b/packages/features/ee/event-tracking/lib/posthog/userPostHog.tsx index 2c66d58266..4722c790dc 100644 --- a/packages/features/ee/event-tracking/lib/posthog/userPostHog.tsx +++ b/packages/features/ee/event-tracking/lib/posthog/userPostHog.tsx @@ -28,11 +28,18 @@ const usePostHogHook: () => PostHogHook = isPostHogEnabled const usePostHog = () => { const posthog = usePostHogHook(); const { data } = trpc.viewer.me.useQuery(); + const { data: statsData } = trpc.viewer.myStats.useQuery(undefined, { + trpc: { + context: { + skipBatch: true, + }, + }, + }); const { hasPaidPlan } = useHasPaidPlan(); const { hasTeamPlan } = useHasTeamPlan(); const identify = async () => { - if (!data) return; + if (!data || !statsData) return; posthog.identify(String(data.id), { distinctId: String(data.id), @@ -49,13 +56,13 @@ const usePostHog = () => { has_team_plan: hasTeamPlan, metadata: data?.metadata, completed_onboarding: data?.completedOnboarding, - sum_of_bookings: data?.sumOfBookings, - sum_of_calendars: data?.sumOfCalendars, - sum_of_teams: data?.sumOfTeams, + sum_of_bookings: statsData?.sumOfBookings, + sum_of_calendars: statsData?.sumOfCalendars, + sum_of_teams: statsData?.sumOfTeams, has_orgs_plan: !!data?.organizationId, organization: data?.organization?.slug, - sum_of_event_types: data?.sumOfEventTypes, - sum_of_team_event_types: data?.sumOfTeamEventTypes, + sum_of_event_types: statsData?.sumOfEventTypes, + sum_of_team_event_types: statsData?.sumOfTeamEventTypes, is_premium: data?.isPremium, }); }; diff --git a/packages/features/ee/support/lib/intercom/useIntercom.ts b/packages/features/ee/support/lib/intercom/useIntercom.ts index d639137feb..070dd5b12b 100644 --- a/packages/features/ee/support/lib/intercom/useIntercom.ts +++ b/packages/features/ee/support/lib/intercom/useIntercom.ts @@ -26,11 +26,18 @@ const useIntercomHook = isInterComEnabled export const useIntercom = () => { const hookData = useIntercomHook(); const { data } = trpc.viewer.me.useQuery(); + const { data: statsData } = trpc.viewer.myStats.useQuery(undefined, { + trpc: { + context: { + skipBatch: true, + }, + }, + }); const { hasPaidPlan } = useHasPaidPlan(); const { hasTeamPlan } = useHasTeamPlan(); const boot = async () => { - if (!data) return; + if (!data || !statsData) return; let userHash; const req = await fetch(`/api/intercom-hash`); @@ -61,13 +68,13 @@ export const useIntercom = () => { metadata: data?.metadata, completed_onboarding: data.completedOnboarding, is_logged_in: !!data, - sum_of_bookings: data?.sumOfBookings, - sum_of_calendars: data?.sumOfCalendars, - sum_of_teams: data?.sumOfTeams, + sum_of_bookings: statsData?.sumOfBookings, + sum_of_calendars: statsData?.sumOfCalendars, + sum_of_teams: statsData?.sumOfTeams, has_orgs_plan: !!data?.organizationId, organization: data?.organization?.slug, - sum_of_event_types: data?.sumOfEventTypes, - sum_of_team_event_types: data?.sumOfTeamEventTypes, + sum_of_event_types: statsData?.sumOfEventTypes, + sum_of_team_event_types: statsData?.sumOfTeamEventTypes, is_premium: data?.isPremium, }, }); @@ -104,13 +111,13 @@ export const useIntercom = () => { metadata: data?.metadata, completed_onboarding: data?.completedOnboarding, is_logged_in: !!data, - sum_of_bookings: data?.sumOfBookings, - sum_of_calendars: data?.sumOfCalendars, - sum_of_teams: data?.sumOfTeams, + sum_of_bookings: statsData?.sumOfBookings, + sum_of_calendars: statsData?.sumOfCalendars, + sum_of_teams: statsData?.sumOfTeams, has_orgs_plan: !!data?.organizationId, organization: data?.organization?.slug, - sum_of_event_types: data?.sumOfEventTypes, - sum_of_team_event_types: data?.sumOfTeamEventTypes, + sum_of_event_types: statsData?.sumOfEventTypes, + sum_of_team_event_types: statsData?.sumOfTeamEventTypes, is_premium: data?.isPremium, }, }); @@ -122,13 +129,22 @@ export const useIntercom = () => { export const useBootIntercom = () => { const { boot } = useIntercom(); const { data: user } = trpc.viewer.me.useQuery(); + const { data: statsData } = trpc.viewer.myStats.useQuery(undefined, { + trpc: { + context: { + skipBatch: true, + }, + }, + }); useEffect(() => { // not using useMediaQuery as it toggles between true and false const showIntercom = localStorage.getItem("showIntercom"); - if (!isInterComEnabled || showIntercom === "false" || window.innerWidth <= 768 || !user) return; + if (!isInterComEnabled || showIntercom === "false" || window.innerWidth <= 768 || !user || !statsData) + return; + boot(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [user]); + }, [user, statsData]); }; export default useIntercom; diff --git a/packages/trpc/server/routers/loggedInViewer/_router.tsx b/packages/trpc/server/routers/loggedInViewer/_router.tsx index 7be92b93eb..7b70a4a918 100644 --- a/packages/trpc/server/routers/loggedInViewer/_router.tsx +++ b/packages/trpc/server/routers/loggedInViewer/_router.tsx @@ -16,6 +16,7 @@ import { ZLocationOptionsInputSchema } from "./locationOptions.schema"; import { ZNoShowInputSchema } from "./markNoShow.schema"; import { ZOutOfOfficeInputSchema, ZOutOfOfficeDelete } from "./outOfOffice.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"; @@ -32,6 +33,7 @@ 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; @@ -72,6 +74,7 @@ const UNSTABLE_HANDLER_CACHE: AppsRouterHandlerCache = {}; export const loggedInViewerRouter = router({ me, + myStats, platformMe, deleteMe: authedProcedure.input(ZDeleteMeInputSchema).mutation(async ({ ctx, input }) => { diff --git a/packages/trpc/server/routers/loggedInViewer/me.handler.ts b/packages/trpc/server/routers/loggedInViewer/me.handler.ts index ce0dea1eae..db1cfbf2fe 100644 --- a/packages/trpc/server/routers/loggedInViewer/me.handler.ts +++ b/packages/trpc/server/routers/loggedInViewer/me.handler.ts @@ -72,36 +72,6 @@ export const meHandler = async ({ ctx, input }: MeOptions) => { identityProviderEmail = account?.providerEmail || ""; } - const additionalUserInfo = await prisma.user.findFirst({ - where: { - id: user.id, - }, - select: { - bookings: { - select: { id: true }, - }, - selectedCalendars: true, - teams: { - select: { - team: { - select: { - id: true, - eventTypes: true, - }, - }, - }, - }, - eventTypes: { - select: { id: true }, - }, - }, - }); - let sumOfTeamEventTypes = 0; - for (const team of additionalUserInfo?.teams || []) { - for (const _eventType of team.team.eventTypes) { - sumOfTeamEventTypes++; - } - } const userMetadataPrased = userMetadata.parse(user.metadata); // Destructuring here only makes it more illegible @@ -159,12 +129,7 @@ export const meHandler = async ({ ctx, input }: MeOptions) => { receiveMonthlyDigestEmail: user.receiveMonthlyDigestEmail, ...profileData, secondaryEmails, - sumOfBookings: additionalUserInfo?.bookings.length, - sumOfCalendars: additionalUserInfo?.selectedCalendars.length, - sumOfTeams: additionalUserInfo?.teams.length, - sumOfEventTypes: additionalUserInfo?.eventTypes.length, isPremium: userMetadataPrased?.isPremium, - sumOfTeamEventTypes, ...(passwordAdded ? { passwordAdded } : {}), }; }; diff --git a/packages/trpc/server/routers/loggedInViewer/myStats.handler.ts b/packages/trpc/server/routers/loggedInViewer/myStats.handler.ts new file mode 100644 index 0000000000..8ce0d19185 --- /dev/null +++ b/packages/trpc/server/routers/loggedInViewer/myStats.handler.ts @@ -0,0 +1,57 @@ +import type { Session } from "next-auth"; + +import prisma from "@calcom/prisma"; +import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; + +type MyStatsOptions = { + ctx: { + user: NonNullable; + session: Session; + }; +}; + +export const myStatsHandler = async ({ ctx }: MyStatsOptions) => { + const { user: sessionUser } = ctx; + + const additionalUserInfo = await prisma.user.findFirst({ + where: { + id: sessionUser.id, + }, + select: { + _count: { + select: { + bookings: true, + selectedCalendars: true, + teams: true, + eventTypes: true, + }, + }, + teams: { + select: { + team: { + select: { + eventTypes: { + select: { + id: true, + }, + }, + }, + }, + }, + }, + }, + }); + const sumOfTeamEventTypes = additionalUserInfo?.teams.reduce( + (sum, team) => sum + team.team.eventTypes.length, + 0 + ); + + return { + id: sessionUser.id, + sumOfBookings: additionalUserInfo?._count.bookings, + sumOfCalendars: additionalUserInfo?._count.selectedCalendars, + sumOfTeams: additionalUserInfo?._count.teams, + sumOfEventTypes: additionalUserInfo?._count.eventTypes, + sumOfTeamEventTypes, + }; +}; diff --git a/packages/trpc/server/routers/loggedInViewer/myStats.schema.ts b/packages/trpc/server/routers/loggedInViewer/myStats.schema.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/packages/trpc/server/routers/loggedInViewer/myStats.schema.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/trpc/server/routers/loggedInViewer/procedures/myStats.ts b/packages/trpc/server/routers/loggedInViewer/procedures/myStats.ts new file mode 100644 index 0000000000..4202796bc6 --- /dev/null +++ b/packages/trpc/server/routers/loggedInViewer/procedures/myStats.ts @@ -0,0 +1,7 @@ +import authedProcedure from "../../../procedures/authedProcedure"; + +export const myStats = authedProcedure.query(async ({ ctx, input }) => { + const handler = (await import("../myStats.handler")).myStatsHandler; + + return handler({ ctx }); +});