diff --git a/console/src/components/ShowCreateBlock.tsx b/console/src/components/ShowCreateBlock.tsx index 74c00f6b2ad8b..095c6106c2ab8 100644 --- a/console/src/components/ShowCreateBlock.tsx +++ b/console/src/components/ShowCreateBlock.tsx @@ -79,3 +79,5 @@ export const ShowCreateBlockInner = ({ return ; }; + +export default ShowCreateBlock; diff --git a/console/src/config/AppConfig.ts b/console/src/config/AppConfig.ts index fd662e9632358..6a67bd0cdd71c 100644 --- a/console/src/config/AppConfig.ts +++ b/console/src/config/AppConfig.ts @@ -160,7 +160,15 @@ export class CloudAppConfig implements IBaseAppConfig { stackSwitcherEnabled = !this.isImpersonating && this.#consoleEnvironment !== "production"; - stripePromise = getStripePromise(this.#consoleEnvironment); + // Lazy so the Stripe.js script only loads when the billing route reads + // this property; never fetched on cold load for other routes. + #stripePromise?: ReturnType; + get stripePromise() { + if (!this.#stripePromise) { + this.#stripePromise = getStripePromise(this.#consoleEnvironment); + } + return this.#stripePromise; + } // Whether query retries are enabled. False for test environments. // TODO (password-auth): Remove the possibility of defaultStack being "test". Should be able to figure diff --git a/console/src/config/apiKeys.ts b/console/src/config/apiKeys.ts index 653e27cc593fe..7491f05101e3b 100644 --- a/console/src/config/apiKeys.ts +++ b/console/src/config/apiKeys.ts @@ -7,7 +7,7 @@ // the Business Source License, use of this software will be governed // by the Apache License, Version 2.0. -import { loadStripe } from "@stripe/stripe-js"; +import type { Stripe } from "@stripe/stripe-js"; export const getLaunchDarklyKey = (consoleEnv: string) => { if (consoleEnv === "production") { @@ -43,12 +43,16 @@ export const getSegmentApiKey = ({ return "dGeQYRjmGVsqDI0KIARrAhTvk1BdJJhk"; }; -export const getStripePromise = (consoleEnv: string) => { - if (consoleEnv === "production") { - // Production key - return loadStripe("pk_live_eILilEpJ7DCwmw9I4JHuBLEB001qSrxuw0"); - } - - // Stripe development key - return loadStripe("pk_test_XUB2NGLtbtzx9HQbTKlP1ZCw00FZqUgrXf"); +export const getStripePromise = async ( + consoleEnv: string, +): Promise => { + // Dynamic import keeps @stripe/stripe-js out of the main bundle; combined + // with the lazy getter on AppConfig.stripePromise, the Stripe.js script + // only loads when the billing route is visited. + const { loadStripe } = await import("@stripe/stripe-js"); + const key = + consoleEnv === "production" + ? "pk_live_eILilEpJ7DCwmw9I4JHuBLEB001qSrxuw0" + : "pk_test_XUB2NGLtbtzx9HQbTKlP1ZCw00FZqUgrXf"; + return loadStripe(key); }; diff --git a/console/src/hooks/useIntercom.ts b/console/src/hooks/useIntercom.ts index 046627fb48413..620fd2880efc3 100644 --- a/console/src/hooks/useIntercom.ts +++ b/console/src/hooks/useIntercom.ts @@ -7,18 +7,34 @@ // the Business Source License, use of this software will be governed // by the Apache License, Version 2.0. -import Intercom from "@intercom/messenger-js-sdk"; import { useEffect } from "react"; import { useIntercomJwt } from "~/api/auth"; const APP_ID = "r8661p0d"; +/** + * Loads the Intercom SDK and boots it off the critical path. The SDK module is + * dynamically imported (own chunk) and the boot call is scheduled in idle time + * so it does not compete with first-paint bandwidth/CPU. + */ +function bootIntercomDeferred(config: { + app_id: string; + intercom_user_jwt?: string; +}) { + const schedule = + typeof window.requestIdleCallback === "function" + ? (cb: () => void) => + window.requestIdleCallback(cb, { timeout: 3000 }) + : (cb: () => void) => window.setTimeout(cb, 1000); + schedule(async () => { + const { default: Intercom } = await import("@intercom/messenger-js-sdk"); + Intercom(config); + }); +} + export function useIntercomAnonymous() { useEffect(() => { - const anonymousConfig = { - app_id: APP_ID, - }; - Intercom(anonymousConfig); + bootIntercomDeferred({ app_id: APP_ID }); }, []); } @@ -28,7 +44,7 @@ export function useIntercom() { useEffect(() => { if (jwt) { - Intercom({ app_id: APP_ID, intercom_user_jwt: jwt }); + bootIntercomDeferred({ app_id: APP_ID, intercom_user_jwt: jwt }); } }, [jwt]); } diff --git a/console/src/platform/maintained-objects/ObjectMetadata.tsx b/console/src/platform/maintained-objects/ObjectMetadata.tsx index b4b77b474964e..eb9dc77ba36b6 100644 --- a/console/src/platform/maintained-objects/ObjectMetadata.tsx +++ b/console/src/platform/maintained-objects/ObjectMetadata.tsx @@ -12,12 +12,15 @@ import React from "react"; import { ShowCreateObjectType } from "~/api/materialize/showCreate"; import { LoadingContainer } from "~/components/LoadingContainer"; -import { ShowCreateBlock } from "~/components/ShowCreateBlock"; import { ObjectColumnsList } from "~/platform/object-explorer/ObjectColumns"; import { MaintainedObjectListItem } from "./queries"; import { SourceDiagnostics } from "./SourceDiagnostics"; +const ShowCreateBlock = React.lazy( + () => import("~/components/ShowCreateBlock"), +); + export interface ObjectMetadataProps { item: MaintainedObjectListItem; } diff --git a/console/src/platform/object-explorer/ConnectionDetails.tsx b/console/src/platform/object-explorer/ConnectionDetails.tsx index 5c46e9abe8bfa..15ea121f2534c 100644 --- a/console/src/platform/object-explorer/ConnectionDetails.tsx +++ b/console/src/platform/object-explorer/ConnectionDetails.tsx @@ -25,7 +25,6 @@ import { ConnectionDependency } from "~/api/materialize/object-explorer/connecti import { ShowCreateObjectType } from "~/api/materialize/showCreate"; import { AppErrorBoundary } from "~/components/AppErrorBoundary"; import { LoadingContainer } from "~/components/LoadingContainer"; -import { ShowCreateBlock } from "~/components/ShowCreateBlock"; import TextLink from "~/components/TextLink"; import { MainContentContainer } from "~/layouts/BaseLayout"; import { MaterializeTheme } from "~/theme"; @@ -37,6 +36,10 @@ import { useConnectionDependencies, useObjectDetails } from "./queries"; import { relativeObjectPath } from "./routerHelpers"; import { ObjectDetailsParams } from "./useSchemaObjectParams"; +const ShowCreateBlock = React.lazy( + () => import("~/components/ShowCreateBlock"), +); + const ConnectionDependencyTable = ({ connectionDependencies, }: { @@ -126,10 +129,12 @@ export const ConnectionDetailsContent = ({ - + }> + + {connectionDependencies.length > 0 && ( import("~/platform/clusters/DataflowVisualizer"), ); +const ShowCreateBlock = React.lazy( + () => import("~/components/ShowCreateBlock"), +); + +const WorkflowGraph = React.lazy( + () => import("~/components/WorkflowGraph/WorkflowGraph"), +); + const SIMPLE_OBJECTS_WITH_INDEXES = ["materialized-view", "view", "table"]; const DATAFLOW_VISUALIZER_OBJECTS = ["materialized-view", "index"]; @@ -86,10 +92,12 @@ export const SimpleObjectDetailsContent = ({ sourceType={object?.sourceType} /> {canShowCreate && ( - + }> + + )} @@ -194,7 +202,11 @@ export const SimpleObjectDetailRoutes = () => { {shouldShowWorkflowGraph && ( } + element={ + }> + + + } /> )} {shouldShowIndexes && ( diff --git a/console/src/platform/object-explorer/SinkDetailRoutes.tsx b/console/src/platform/object-explorer/SinkDetailRoutes.tsx index dade863ff6ed4..4e7b7f9d135d3 100644 --- a/console/src/platform/object-explorer/SinkDetailRoutes.tsx +++ b/console/src/platform/object-explorer/SinkDetailRoutes.tsx @@ -12,7 +12,6 @@ import { Navigate, Outlet, Route, useNavigate } from "react-router-dom"; import { AppErrorBoundary } from "~/components/AppErrorBoundary"; import { LoadingContainer } from "~/components/LoadingContainer"; -import WorkflowGraph from "~/components/WorkflowGraph/WorkflowGraph"; import { Tab } from "~/layouts/BaseLayout"; import { useSinkList } from "~/platform/sinks/queries"; import SinkErrors from "~/platform/sinks/SinkErrors"; @@ -24,6 +23,10 @@ import { SimpleObjectDetailsContainer } from "./SimpleObjectDetailRoutes"; import { useSchemaObjectParams } from "./useSchemaObjectParams"; import { useToastIfObjectNotExtant } from "./useToastIfObjectNotExtant"; +const WorkflowGraph = React.lazy( + () => import("~/components/WorkflowGraph/WorkflowGraph"), +); + const WORKFLOW_TAB = { label: "Workflow", href: `../workflow`, @@ -120,7 +123,11 @@ export const SinkDetailRoutes = () => { } /> } + element={ + }> + + + } /> } /> diff --git a/console/src/platform/object-explorer/SourceDetailRoutes.tsx b/console/src/platform/object-explorer/SourceDetailRoutes.tsx index 874628fd4d27b..e847800a6dc5e 100644 --- a/console/src/platform/object-explorer/SourceDetailRoutes.tsx +++ b/console/src/platform/object-explorer/SourceDetailRoutes.tsx @@ -13,7 +13,6 @@ import { Navigate, Outlet, Route, useNavigate } from "react-router-dom"; import { Source } from "~/api/materialize/source/sourceList"; import { AppErrorBoundary } from "~/components/AppErrorBoundary"; import { LoadingContainer } from "~/components/LoadingContainer"; -import WorkflowGraph from "~/components/WorkflowGraph/WorkflowGraph"; import { Tab } from "~/layouts/BaseLayout"; import { useSourcesList } from "~/platform/sources/queries"; import SourceErrors from "~/platform/sources/SourceErrors"; @@ -29,6 +28,10 @@ import { SimpleObjectDetailsContainer } from "./SimpleObjectDetailRoutes"; import { useSchemaObjectParams } from "./useSchemaObjectParams"; import { useToastIfObjectNotExtant } from "./useToastIfObjectNotExtant"; +const WorkflowGraph = React.lazy( + () => import("~/components/WorkflowGraph/WorkflowGraph"), +); + export const SourceOverviewContainer = () => { return ( @@ -173,7 +176,11 @@ export const SourceDetailRoutes = () => { } /> } + element={ + }> + + + } /> ) : ( @@ -183,7 +190,11 @@ export const SourceDetailRoutes = () => { } /> } + element={ + }> + + + } /> } /> } />