diff --git a/app/gui/src/dashboard/App.tsx b/app/gui/src/dashboard/App.tsx index 7b43c605ff9..0cd3820ce00 100644 --- a/app/gui/src/dashboard/App.tsx +++ b/app/gui/src/dashboard/App.tsx @@ -519,13 +519,11 @@ function AppRouter(props: AppRouterProps) { {routes} - {detect.IS_DEV_MODE && ( - - - - - - )} + + + + + diff --git a/app/gui/src/dashboard/components/Devtools/EnsoDevtools.tsx b/app/gui/src/dashboard/components/Devtools/EnsoDevtools.tsx index fbe1a7f1e65..93148def8d5 100644 --- a/app/gui/src/dashboard/components/Devtools/EnsoDevtools.tsx +++ b/app/gui/src/dashboard/components/Devtools/EnsoDevtools.tsx @@ -24,6 +24,7 @@ import { useEnableVersionChecker, usePaywallDevtools, useSetEnableVersionChecker, + useShowDevtools, } from './EnsoDevtoolsProvider' import * as ariaComponents from '#/components/AriaComponents' @@ -54,6 +55,9 @@ export function EnsoDevtools() { const { authQueryKey, session } = authProvider.useAuth() const queryClient = reactQuery.useQueryClient() const { getFeature } = billing.usePaywallFeatures() + + const showDevtools = useShowDevtools() + const { features, setFeature } = usePaywallDevtools() const enableVersionChecker = useEnableVersionChecker() const setEnableVersionChecker = useSetEnableVersionChecker() @@ -66,6 +70,10 @@ export function EnsoDevtools() { const featureFlags = useFeatureFlags() const setFeatureFlags = useSetFeatureFlags() + if (!showDevtools) { + return null + } + return ( diff --git a/app/gui/src/dashboard/components/Devtools/EnsoDevtoolsProvider.tsx b/app/gui/src/dashboard/components/Devtools/EnsoDevtoolsProvider.tsx index 213db13bb8e..4aa70641030 100644 --- a/app/gui/src/dashboard/components/Devtools/EnsoDevtoolsProvider.tsx +++ b/app/gui/src/dashboard/components/Devtools/EnsoDevtoolsProvider.tsx @@ -3,6 +3,8 @@ * This file provides a zustand store that contains the state of the Enso devtools. */ import type { PaywallFeatureName } from '#/hooks/billing' +import { IS_DEV_MODE } from 'enso-common/src/detect' +import * as React from 'react' import * as zustand from 'zustand' /** Configuration for a paywall feature. */ @@ -16,13 +18,23 @@ export interface PaywallDevtoolsFeatureConfiguration { /** The state of this zustand store. */ interface EnsoDevtoolsStore { + readonly showDevtools: boolean + readonly setShowDevtools: (showDevtools: boolean) => void + readonly toggleDevtools: () => void readonly showVersionChecker: boolean | null readonly paywallFeatures: Record readonly setPaywallFeature: (feature: PaywallFeatureName, isForceEnabled: boolean | null) => void readonly setEnableVersionChecker: (showVersionChecker: boolean | null) => void } -const ensoDevtoolsStore = zustand.createStore((set) => ({ +export const ensoDevtoolsStore = zustand.createStore((set) => ({ + showDevtools: IS_DEV_MODE, + setShowDevtools: (showDevtools) => { + set({ showDevtools }) + }, + toggleDevtools: () => { + set(({ showDevtools }) => ({ showDevtools: !showDevtools })) + }, showVersionChecker: false, paywallFeatures: { share: { isForceEnabled: null }, @@ -67,3 +79,23 @@ export function usePaywallDevtools() { setFeature: state.setPaywallFeature, })) } + +/** A hook that provides access to the show devtools state. */ +export function useShowDevtools() { + return zustand.useStore(ensoDevtoolsStore, (state) => state.showDevtools) +} + +// ================================= +// === DevtoolsProvider === +// ================================= + +/** + * Provide the Enso devtools to the app. + */ +export function DevtoolsProvider(props: { children: React.ReactNode }) { + React.useEffect(() => { + window.toggleDevtools = ensoDevtoolsStore.getState().toggleDevtools + }, []) + + return <>{props.children} +} diff --git a/app/gui/src/dashboard/components/Devtools/ReactQueryDevtools.tsx b/app/gui/src/dashboard/components/Devtools/ReactQueryDevtools.tsx index e786fec979a..6ddb0664d51 100644 --- a/app/gui/src/dashboard/components/Devtools/ReactQueryDevtools.tsx +++ b/app/gui/src/dashboard/components/Devtools/ReactQueryDevtools.tsx @@ -4,6 +4,7 @@ import * as React from 'react' import * as reactQuery from '@tanstack/react-query' import * as reactQueryDevtools from '@tanstack/react-query-devtools' import * as errorBoundary from 'react-error-boundary' +import { useShowDevtools } from './EnsoDevtoolsProvider' const ReactQueryDevtoolsProduction = React.lazy(() => import('@tanstack/react-query-devtools/build/modern/production.js').then((d) => ({ @@ -13,19 +14,13 @@ const ReactQueryDevtoolsProduction = React.lazy(() => /** Show the React Query Devtools and provide the ability to show them in production. */ export function ReactQueryDevtools() { - const [showDevtools, setShowDevtools] = React.useState(false) + const showDevtools = useShowDevtools() // It is safer to pass the client directly to the devtools // since there might be a chance that we have multiple versions of `react-query`, // in case we forget to update the devtools, npm messes up the versions, // or there are hoisting issues. const client = reactQuery.useQueryClient() - React.useEffect(() => { - window.toggleDevtools = () => { - setShowDevtools((old) => !old) - } - }, []) - return ( { diff --git a/app/gui/src/dashboard/index.tsx b/app/gui/src/dashboard/index.tsx index 943408afe52..3dfdcb99318 100644 --- a/app/gui/src/dashboard/index.tsx +++ b/app/gui/src/dashboard/index.tsx @@ -21,7 +21,7 @@ import LoggerProvider, { type Logger } from '#/providers/LoggerProvider' import LoadingScreen from '#/pages/authentication/LoadingScreen' -import { ReactQueryDevtools } from '#/components/Devtools' +import { DevtoolsProvider, ReactQueryDevtools } from '#/components/Devtools' import { ErrorBoundary } from '#/components/ErrorBoundary' import { OfflineNotificationManager } from '#/components/OfflineNotificationManager' import { Suspense } from '#/components/Suspense' @@ -113,21 +113,23 @@ export function run(props: DashboardProps) { reactDOM.createRoot(root).render( - - }> - - - - - - - - - - - + + + }> + + + + + + + + + + + - + + , )