diff --git a/apps/web/components/eventtype/EventTypeSingleLayout.tsx b/apps/web/components/eventtype/EventTypeSingleLayout.tsx index 3218b90418..9523c34fb9 100644 --- a/apps/web/components/eventtype/EventTypeSingleLayout.tsx +++ b/apps/web/components/eventtype/EventTypeSingleLayout.tsx @@ -7,7 +7,7 @@ import { useMemo, useState, Suspense } from "react"; import type { UseFormReturn } from "react-hook-form"; import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager"; -import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks"; +import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider"; import Shell from "@calcom/features/shell/Shell"; import { classNames } from "@calcom/lib"; import { CAL_URL } from "@calcom/lib/constants"; @@ -234,7 +234,7 @@ function EventTypeSingleLayout({ formMethods, ]); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); const isOrgEvent = orgBranding?.fullDomain; const permalink = `${orgBranding?.fullDomain ?? CAL_URL}/${ team ? `${!isOrgEvent ? "team/" : ""}${team.slug}` : eventType.users[0].username diff --git a/apps/web/lib/app-providers.tsx b/apps/web/lib/app-providers.tsx index ac9f70e9dc..d9d5683ff9 100644 --- a/apps/web/lib/app-providers.tsx +++ b/apps/web/lib/app-providers.tsx @@ -1,5 +1,6 @@ import { TooltipProvider } from "@radix-ui/react-tooltip"; import { SessionProvider } from "next-auth/react"; +import { useSession } from "next-auth/react"; import { EventCollectionProvider } from "next-collect/client"; import type { SSRConfig } from "next-i18next"; import { appWithTranslation } from "next-i18next"; @@ -10,7 +11,6 @@ import { useRouter } from "next/router"; import type { ComponentProps, PropsWithChildren, ReactNode } from "react"; import { OrgBrandingProvider } from "@calcom/features/ee/organizations/context/provider"; -import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks"; import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic"; import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic"; import { FeatureProvider } from "@calcom/features/flags/context/provider"; @@ -218,9 +218,26 @@ function FeatureFlagsProvider({ children }: { children: React.ReactNode }) { return {children}; } +function useOrgBrandingValues() { + const session = useSession(); + + const res = trpc.viewer.organizations.getBrand.useQuery(undefined, { + // Only fetch if we have a session to avoid flooding logs with errors + enabled: session.status === "authenticated", + }); + + if (res.status === "loading") { + return undefined; + } + + if (res.status === "error") return null; + + return res.data; +} + function OrgBrandProvider({ children }: { children: React.ReactNode }) { const orgBrand = useOrgBrandingValues(); - return {children}; + return {children}; } const AppProviders = (props: AppPropsWithChildren) => { diff --git a/apps/web/pages/event-types/index.tsx b/apps/web/pages/event-types/index.tsx index bbc06b878c..72e5e2c53e 100644 --- a/apps/web/pages/event-types/index.tsx +++ b/apps/web/pages/event-types/index.tsx @@ -7,7 +7,7 @@ import type { FC } from "react"; import { useEffect, useState, memo } from "react"; import { z } from "zod"; -import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks"; +import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider"; import useIntercom from "@calcom/features/ee/support/lib/intercom/useIntercom"; import { EventTypeDescriptionLazy as EventTypeDescription } from "@calcom/features/eventtypes/components"; import CreateEventTypeDialog from "@calcom/features/eventtypes/components/CreateEventTypeDialog"; @@ -102,7 +102,7 @@ const querySchema = z.object({ const MobileTeamsTab: FC = (props) => { const { eventTypeGroups } = props; - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); const tabs = eventTypeGroups.map((item) => ({ name: item.profile.name ?? "", href: item.teamId ? `/event-types?teamId=${item.teamId}` : "/event-types", @@ -200,7 +200,7 @@ const MemoizedItem = memo(Item); export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeListProps): JSX.Element => { const { t } = useLocale(); const router = useRouter(); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); const [parent] = useAutoAnimate(); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [deleteDialogTypeId, setDeleteDialogTypeId] = useState(0); @@ -700,7 +700,7 @@ const EventTypeListHeading = ({ }: EventTypeListHeadingProps): JSX.Element => { const { t } = useLocale(); const router = useRouter(); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); const publishTeamMutation = trpc.viewer.teams.publish.useMutation({ onSuccess(data) { @@ -809,7 +809,7 @@ const Actions = () => { const SetupProfileBanner = ({ closeAction }: { closeAction: () => void }) => { const { t } = useLocale(); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); return ( { const router = useRouter(); const filters = getTeamsFiltersFromQuery(router.query); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); // TODO: Maybe useSuspenseQuery to focus on success case only? Remember that it would crash the page when there is an error in query. Also, it won't support skeleton const res = trpc.viewer.eventTypes.getByViewer.useQuery(filters && { filters }); @@ -906,7 +906,7 @@ const EventTypesPage = () => { const { query } = router; const { data: user } = useMeQuery(); const [showProfileBanner, setShowProfileBanner] = useState(false); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); function closeBanner() { setShowProfileBanner(false); diff --git a/packages/app-store/routing-forms/components/FormActions.tsx b/packages/app-store/routing-forms/components/FormActions.tsx index f705995660..10fcc42238 100644 --- a/packages/app-store/routing-forms/components/FormActions.tsx +++ b/packages/app-store/routing-forms/components/FormActions.tsx @@ -7,7 +7,7 @@ import { Controller } from "react-hook-form"; import { v4 as uuidv4 } from "uuid"; import { z } from "zod"; -import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks"; +import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider"; import { classNames } from "@calcom/lib"; import { CAL_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; @@ -402,7 +402,7 @@ export const FormAction = forwardRef(function FormAction { const { t } = useLocale(); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); const { data: query, pushItemToKey, removeItemByKeyAndValue, removeByKey } = useFilterQuery(); const [searchText, setSearchText] = useState(""); diff --git a/packages/features/ee/organizations/context/provider.ts b/packages/features/ee/organizations/context/provider.ts index e32e001611..bd09a395c8 100644 --- a/packages/features/ee/organizations/context/provider.ts +++ b/packages/features/ee/organizations/context/provider.ts @@ -22,19 +22,19 @@ export type OrganizationBranding = /** * Allows you to access the flags from context */ -const OrganizationBrandingContext = createContext(null); +const OrganizationBrandingContext = createContext<{ orgBrand: OrganizationBranding } | null>(null); /** * Accesses the branding for an organization from context. - * * You need to render a further up to be able to use * this component. + * + * @returns `undefined` when data isn't available yet, `null` when there's an error, and the data(which can be `null`) when it's available */ export function useOrgBranding() { const orgBrandingContext = useContext(OrganizationBrandingContext); - if (orgBrandingContext === null) - throw new Error("Error: useOrganizationBranding was used outside of OrgBrandingProvider."); - return orgBrandingContext as OrganizationBranding; + if (!orgBrandingContext) throw new Error("Error: useOrgBranding was used outside of OrgBrandingProvider."); + return orgBrandingContext.orgBrand; } /** @@ -60,7 +60,7 @@ export function useOrgBranding() { * `YourOwnComponent` or further down. * */ -export function OrgBrandingProvider(props: { +export function OrgBrandingProvider(props: { value: F; children: React.ReactNode; }) { diff --git a/packages/features/ee/organizations/hooks/index.ts b/packages/features/ee/organizations/hooks/index.ts deleted file mode 100644 index 6b3c264a33..0000000000 --- a/packages/features/ee/organizations/hooks/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { useSession } from "next-auth/react"; - -import { trpc } from "@calcom/trpc/react"; - -export function useOrgBrandingValues() { - const session = useSession(); - return trpc.viewer.organizations.getBrand.useQuery(undefined, { - // Only fetch if we have a session to avoid flooding logs with errors - enabled: session.status === "authenticated", - // This is important because usage of useOrgBrandingValues is spread across the app and there isn't going to be anything new on fetching on every new mount of a component - // We can just refetch the query only when org slug or logo changes which would be not frequent - staleTime: Infinity, - }).data; -} diff --git a/packages/features/ee/organizations/pages/settings/profile.tsx b/packages/features/ee/organizations/pages/settings/profile.tsx index ca2d9fb43f..0a111aa9f5 100644 --- a/packages/features/ee/organizations/pages/settings/profile.tsx +++ b/packages/features/ee/organizations/pages/settings/profile.tsx @@ -7,7 +7,6 @@ import { Controller, useForm } from "react-hook-form"; import { z } from "zod"; import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired"; -import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks"; import { subdomainSuffix } from "@calcom/features/ee/organizations/lib/orgDomains"; import { WEBAPP_URL } from "@calcom/lib/constants"; import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; @@ -30,6 +29,7 @@ import { } from "@calcom/ui"; import { getLayout } from "../../../../settings/layouts/SettingsLayout"; +import { useOrgBranding } from "../../../organizations/context/provider"; const orgProfileFormSchema = z.object({ name: z.string(), @@ -42,7 +42,7 @@ const OrgProfileView = () => { const router = useRouter(); const utils = trpc.useContext(); const [firstRender, setFirstRender] = useState(true); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); useLayoutEffect(() => { document.body.focus(); diff --git a/packages/features/ee/teams/components/CreateANewTeamForm.tsx b/packages/features/ee/teams/components/CreateANewTeamForm.tsx index abcb0ae483..2c32d8ead1 100644 --- a/packages/features/ee/teams/components/CreateANewTeamForm.tsx +++ b/packages/features/ee/teams/components/CreateANewTeamForm.tsx @@ -4,7 +4,6 @@ import { Controller, useForm } from "react-hook-form"; import { z } from "zod"; import { extractDomainFromWebsiteUrl } from "@calcom/ee/organizations/lib/utils"; -import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks"; import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import slugify from "@calcom/lib/slugify"; @@ -13,6 +12,7 @@ import { trpc } from "@calcom/trpc/react"; import { Avatar, Button, Form, ImageUploader, TextField, Alert, Label } from "@calcom/ui"; import { ArrowRight, Plus } from "@calcom/ui/components/icon"; +import { useOrgBranding } from "../../organizations/context/provider"; import type { NewTeamFormValues } from "../lib/types"; const querySchema = z.object({ @@ -26,7 +26,7 @@ export const CreateANewTeamForm = () => { const telemetry = useTelemetry(); const parsedQuery = querySchema.safeParse(router.query); const [serverErrorMessage, setServerErrorMessage] = useState(null); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); const returnToParam = (parsedQuery.success ? getSafeRedirectUrl(parsedQuery.data.returnTo) : "/settings/teams") || diff --git a/packages/features/ee/teams/components/MemberListItem.tsx b/packages/features/ee/teams/components/MemberListItem.tsx index 90394181de..726ba3ff1f 100644 --- a/packages/features/ee/teams/components/MemberListItem.tsx +++ b/packages/features/ee/teams/components/MemberListItem.tsx @@ -2,7 +2,6 @@ import classNames from "classnames"; import { signIn } from "next-auth/react"; import { useState } from "react"; -import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks"; import { WEBAPP_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { MembershipRole } from "@calcom/prisma/enums"; @@ -30,6 +29,7 @@ import { } from "@calcom/ui"; import { ExternalLink, MoreHorizontal, Edit2, Lock, UserX } from "@calcom/ui/components/icon"; +import { useOrgBranding } from "../../organizations/context/provider"; import MemberChangeRoleModal from "./MemberChangeRoleModal"; import TeamAvailabilityModal from "./TeamAvailabilityModal"; import TeamPill, { TeamRole } from "./TeamPill"; @@ -54,7 +54,7 @@ const checkIsOrg = (team: Props["team"]) => { export default function MemberListItem(props: Props) { const { t } = useLocale(); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); const utils = trpc.useContext(); const [showChangeMemberRoleModal, setShowChangeMemberRoleModal] = useState(false); diff --git a/packages/features/ee/teams/components/TeamListItem.tsx b/packages/features/ee/teams/components/TeamListItem.tsx index d23cfbc64f..e80da3a9f8 100644 --- a/packages/features/ee/teams/components/TeamListItem.tsx +++ b/packages/features/ee/teams/components/TeamListItem.tsx @@ -4,7 +4,6 @@ import { useState } from "react"; import InviteLinkSettingsModal from "@calcom/ee/teams/components/InviteLinkSettingsModal"; import MemberInvitationModal from "@calcom/ee/teams/components/MemberInvitationModal"; -import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks"; import classNames from "@calcom/lib/classNames"; import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; import { useLocale } from "@calcom/lib/hooks/useLocale"; @@ -40,6 +39,7 @@ import { X, } from "@calcom/ui/components/icon"; +import { useOrgBranding } from "../../organizations/context/provider"; import { TeamRole } from "./TeamPill"; interface Props { @@ -81,7 +81,7 @@ export default function TeamListItem(props: Props) { const acceptInvite = () => acceptOrLeave(true); const declineInvite = () => acceptOrLeave(false); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); const isOwner = props.team.role === MembershipRole.OWNER; const isInvitee = !props.team.accepted; diff --git a/packages/features/ee/workflows/components/WorkflowListPage.tsx b/packages/features/ee/workflows/components/WorkflowListPage.tsx index f1dd15334a..41e655b49b 100644 --- a/packages/features/ee/workflows/components/WorkflowListPage.tsx +++ b/packages/features/ee/workflows/components/WorkflowListPage.tsx @@ -22,7 +22,7 @@ import { } from "@calcom/ui"; import { Edit2, Link as LinkIcon, MoreHorizontal, Trash2 } from "@calcom/ui/components/icon"; -import { useOrgBrandingValues } from "../../organizations/hooks"; +import { useOrgBranding } from "../../organizations/context/provider"; import { subdomainSuffix } from "../../organizations/lib/orgDomains"; import { getActionIcon } from "../lib/getActionIcon"; import { DeleteDialog } from "./DeleteDialog"; @@ -58,7 +58,7 @@ export default function WorkflowListPage({ workflows }: Props) { const [workflowToDeleteId, setwWorkflowToDeleteId] = useState(0); const router = useRouter(); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); const urlPrefix = orgBranding ? `${orgBranding.slug}.${subdomainSuffix()}` : CAL_URL; return ( diff --git a/packages/features/eventtypes/components/CreateEventTypeDialog.tsx b/packages/features/eventtypes/components/CreateEventTypeDialog.tsx index f61ed0b6e2..c4bf578973 100644 --- a/packages/features/eventtypes/components/CreateEventTypeDialog.tsx +++ b/packages/features/eventtypes/components/CreateEventTypeDialog.tsx @@ -6,7 +6,7 @@ import { useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; -import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks"; +import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider"; import { useFlagMap } from "@calcom/features/flags/context/provider"; import { classNames } from "@calcom/lib"; import { useLocale } from "@calcom/lib/hooks/useLocale"; @@ -82,7 +82,7 @@ export default function CreateEventTypeDialog({ const { t } = useLocale(); const router = useRouter(); const [firstRender, setFirstRender] = useState(true); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); const { data: { teamId, eventPage: pageSlug }, diff --git a/packages/features/shell/Shell.tsx b/packages/features/shell/Shell.tsx index f73f389024..e238aa830f 100644 --- a/packages/features/shell/Shell.tsx +++ b/packages/features/shell/Shell.tsx @@ -13,7 +13,6 @@ import { useIsEmbed } from "@calcom/embed-core/embed-iframe"; import UnconfirmedBookingBadge from "@calcom/features/bookings/UnconfirmedBookingBadge"; import ImpersonatingBanner from "@calcom/features/ee/impersonation/components/ImpersonatingBanner"; import { OrgUpgradeBanner } from "@calcom/features/ee/organizations/components/OrgUpgradeBanner"; -import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks"; import HelpMenuItem from "@calcom/features/ee/support/components/HelpMenuItem"; import { TeamsUpgradeBanner } from "@calcom/features/ee/teams/components"; import { useFlagMap } from "@calcom/features/flags/context/provider"; @@ -79,6 +78,7 @@ import { } from "@calcom/ui/components/icon"; import { Discord } from "@calcom/ui/components/icon/Discord"; +import { useOrgBranding } from "../ee/organizations/context/provider"; import FreshChatProvider from "../ee/support/lib/freshchat/FreshChatProvider"; import { NProgressNextRouter } from "./NProgressPageIndicator"; import { TeamInviteBadge } from "./TeamInviteBadge"; @@ -801,7 +801,8 @@ const getOrganizationUrl = (slug: string) => function SideBar({ bannersHeight, user }: SideBarProps) { const { t, isLocaleReady } = useLocale(); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); + const isOrgBrandingDataFetched = orgBranding !== undefined; const publicPageUrl = useMemo(() => { if (!user?.organizationId) return `${process.env.NEXT_PUBLIC_WEBSITE_URL}/${user?.username}`; @@ -839,7 +840,7 @@ function SideBar({ bannersHeight, user }: SideBarProps) { className="desktop-transparent bg-muted border-muted fixed left-0 hidden h-full max-h-screen w-14 flex-col overflow-y-auto overflow-x-hidden border-r dark:bg-gradient-to-tr dark:from-[#2a2a2a] dark:to-[#1c1c1c] md:sticky md:flex lg:w-56 lg:px-3">
- {orgBranding ? ( + {!isOrgBrandingDataFetched ? null : orgBranding ? (
(null); const [state, dispatch] = useReducer(reducer, initialState); const { t } = useLocale(); - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); const { data, isLoading, fetchNextPage, isFetching } = trpc.viewer.organizations.listMembers.useInfiniteQuery( diff --git a/packages/features/users/components/UserTable/UserTableActions.tsx b/packages/features/users/components/UserTable/UserTableActions.tsx index d15148635b..c2cb24b4ad 100644 --- a/packages/features/users/components/UserTable/UserTableActions.tsx +++ b/packages/features/users/components/UserTable/UserTableActions.tsx @@ -14,7 +14,6 @@ import { DropdownMenuSeparator, } from "@calcom/ui"; -import { useOrgBrandingValues } from "../../../ee/organizations/hooks"; import type { Action } from "./UserListTable"; import type { User } from "./UserListTable"; @@ -34,7 +33,6 @@ export function TableActions({ }; }) { const { t } = useLocale(); - const orgBranding = useOrgBrandingValues(); const usersProfileUrl = domain + "/" + user.username; diff --git a/packages/lib/hooks/useBookerUrl.ts b/packages/lib/hooks/useBookerUrl.ts index 37e0253cec..7b2eab67b7 100644 --- a/packages/lib/hooks/useBookerUrl.ts +++ b/packages/lib/hooks/useBookerUrl.ts @@ -1,7 +1,7 @@ -import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks"; +import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider"; import { WEBAPP_URL } from "@calcom/lib/constants"; export const useBookerUrl = () => { - const orgBranding = useOrgBrandingValues(); + const orgBranding = useOrgBranding(); return orgBranding?.fullDomain ?? WEBAPP_URL; };