Open enso devtools on call of toggleDevtools() (#11423)

This PR changes the behavior of `toggleDevtools()` function and shows `ensoDevtools` with `tanstack` devtools
This commit is contained in:
Sergei Garin 2024-10-28 18:53:40 +03:00 committed by GitHub
parent 12267c6d98
commit 5bf064f97f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 65 additions and 30 deletions

View File

@ -519,13 +519,11 @@ function AppRouter(props: AppRouterProps) {
<LocalBackendPathSynchronizer /> <LocalBackendPathSynchronizer />
<VersionChecker /> <VersionChecker />
{routes} {routes}
{detect.IS_DEV_MODE && ( <suspense.Suspense>
<suspense.Suspense> <errorBoundary.ErrorBoundary>
<errorBoundary.ErrorBoundary> <devtools.EnsoDevtools />
<devtools.EnsoDevtools /> </errorBoundary.ErrorBoundary>
</errorBoundary.ErrorBoundary> </suspense.Suspense>
</suspense.Suspense>
)}
</errorBoundary.ErrorBoundary> </errorBoundary.ErrorBoundary>
</DriveProvider> </DriveProvider>
</InputBindingsProvider> </InputBindingsProvider>

View File

@ -24,6 +24,7 @@ import {
useEnableVersionChecker, useEnableVersionChecker,
usePaywallDevtools, usePaywallDevtools,
useSetEnableVersionChecker, useSetEnableVersionChecker,
useShowDevtools,
} from './EnsoDevtoolsProvider' } from './EnsoDevtoolsProvider'
import * as ariaComponents from '#/components/AriaComponents' import * as ariaComponents from '#/components/AriaComponents'
@ -54,6 +55,9 @@ export function EnsoDevtools() {
const { authQueryKey, session } = authProvider.useAuth() const { authQueryKey, session } = authProvider.useAuth()
const queryClient = reactQuery.useQueryClient() const queryClient = reactQuery.useQueryClient()
const { getFeature } = billing.usePaywallFeatures() const { getFeature } = billing.usePaywallFeatures()
const showDevtools = useShowDevtools()
const { features, setFeature } = usePaywallDevtools() const { features, setFeature } = usePaywallDevtools()
const enableVersionChecker = useEnableVersionChecker() const enableVersionChecker = useEnableVersionChecker()
const setEnableVersionChecker = useSetEnableVersionChecker() const setEnableVersionChecker = useSetEnableVersionChecker()
@ -66,6 +70,10 @@ export function EnsoDevtools() {
const featureFlags = useFeatureFlags() const featureFlags = useFeatureFlags()
const setFeatureFlags = useSetFeatureFlags() const setFeatureFlags = useSetFeatureFlags()
if (!showDevtools) {
return null
}
return ( return (
<Portal> <Portal>
<ariaComponents.DialogTrigger> <ariaComponents.DialogTrigger>

View File

@ -3,6 +3,8 @@
* This file provides a zustand store that contains the state of the Enso devtools. * This file provides a zustand store that contains the state of the Enso devtools.
*/ */
import type { PaywallFeatureName } from '#/hooks/billing' 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' import * as zustand from 'zustand'
/** Configuration for a paywall feature. */ /** Configuration for a paywall feature. */
@ -16,13 +18,23 @@ export interface PaywallDevtoolsFeatureConfiguration {
/** The state of this zustand store. */ /** The state of this zustand store. */
interface EnsoDevtoolsStore { interface EnsoDevtoolsStore {
readonly showDevtools: boolean
readonly setShowDevtools: (showDevtools: boolean) => void
readonly toggleDevtools: () => void
readonly showVersionChecker: boolean | null readonly showVersionChecker: boolean | null
readonly paywallFeatures: Record<PaywallFeatureName, PaywallDevtoolsFeatureConfiguration> readonly paywallFeatures: Record<PaywallFeatureName, PaywallDevtoolsFeatureConfiguration>
readonly setPaywallFeature: (feature: PaywallFeatureName, isForceEnabled: boolean | null) => void readonly setPaywallFeature: (feature: PaywallFeatureName, isForceEnabled: boolean | null) => void
readonly setEnableVersionChecker: (showVersionChecker: boolean | null) => void readonly setEnableVersionChecker: (showVersionChecker: boolean | null) => void
} }
const ensoDevtoolsStore = zustand.createStore<EnsoDevtoolsStore>((set) => ({ export const ensoDevtoolsStore = zustand.createStore<EnsoDevtoolsStore>((set) => ({
showDevtools: IS_DEV_MODE,
setShowDevtools: (showDevtools) => {
set({ showDevtools })
},
toggleDevtools: () => {
set(({ showDevtools }) => ({ showDevtools: !showDevtools }))
},
showVersionChecker: false, showVersionChecker: false,
paywallFeatures: { paywallFeatures: {
share: { isForceEnabled: null }, share: { isForceEnabled: null },
@ -67,3 +79,23 @@ export function usePaywallDevtools() {
setFeature: state.setPaywallFeature, 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}</>
}

View File

@ -4,6 +4,7 @@ import * as React from 'react'
import * as reactQuery from '@tanstack/react-query' import * as reactQuery from '@tanstack/react-query'
import * as reactQueryDevtools from '@tanstack/react-query-devtools' import * as reactQueryDevtools from '@tanstack/react-query-devtools'
import * as errorBoundary from 'react-error-boundary' import * as errorBoundary from 'react-error-boundary'
import { useShowDevtools } from './EnsoDevtoolsProvider'
const ReactQueryDevtoolsProduction = React.lazy(() => const ReactQueryDevtoolsProduction = React.lazy(() =>
import('@tanstack/react-query-devtools/build/modern/production.js').then((d) => ({ 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. */ /** Show the React Query Devtools and provide the ability to show them in production. */
export function ReactQueryDevtools() { export function ReactQueryDevtools() {
const [showDevtools, setShowDevtools] = React.useState(false) const showDevtools = useShowDevtools()
// It is safer to pass the client directly to the devtools // 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`, // 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, // in case we forget to update the devtools, npm messes up the versions,
// or there are hoisting issues. // or there are hoisting issues.
const client = reactQuery.useQueryClient() const client = reactQuery.useQueryClient()
React.useEffect(() => {
window.toggleDevtools = () => {
setShowDevtools((old) => !old)
}
}, [])
return ( return (
<errorBoundary.ErrorBoundary <errorBoundary.ErrorBoundary
fallbackRender={({ resetErrorBoundary }) => { fallbackRender={({ resetErrorBoundary }) => {

View File

@ -21,7 +21,7 @@ import LoggerProvider, { type Logger } from '#/providers/LoggerProvider'
import LoadingScreen from '#/pages/authentication/LoadingScreen' import LoadingScreen from '#/pages/authentication/LoadingScreen'
import { ReactQueryDevtools } from '#/components/Devtools' import { DevtoolsProvider, ReactQueryDevtools } from '#/components/Devtools'
import { ErrorBoundary } from '#/components/ErrorBoundary' import { ErrorBoundary } from '#/components/ErrorBoundary'
import { OfflineNotificationManager } from '#/components/OfflineNotificationManager' import { OfflineNotificationManager } from '#/components/OfflineNotificationManager'
import { Suspense } from '#/components/Suspense' import { Suspense } from '#/components/Suspense'
@ -113,21 +113,23 @@ export function run(props: DashboardProps) {
reactDOM.createRoot(root).render( reactDOM.createRoot(root).render(
<React.StrictMode> <React.StrictMode>
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<ErrorBoundary> <DevtoolsProvider>
<Suspense fallback={<LoadingScreen />}> <ErrorBoundary>
<OfflineNotificationManager> <Suspense fallback={<LoadingScreen />}>
<LoggerProvider logger={logger}> <OfflineNotificationManager>
<HttpClientProvider httpClient={httpClient}> <LoggerProvider logger={logger}>
<UIProviders locale="en-US" portalRoot={portalRoot}> <HttpClientProvider httpClient={httpClient}>
<App {...props} supportsDeepLinks={actuallySupportsDeepLinks} /> <UIProviders locale="en-US" portalRoot={portalRoot}>
</UIProviders> <App {...props} supportsDeepLinks={actuallySupportsDeepLinks} />
</HttpClientProvider> </UIProviders>
</LoggerProvider> </HttpClientProvider>
</OfflineNotificationManager> </LoggerProvider>
</Suspense> </OfflineNotificationManager>
</ErrorBoundary> </Suspense>
</ErrorBoundary>
<ReactQueryDevtools /> <ReactQueryDevtools />
</DevtoolsProvider>
</QueryClientProvider> </QueryClientProvider>
</React.StrictMode>, </React.StrictMode>,
) )