diff --git a/packages/trace-viewer/src/ui/embeddedWorkbenchLoader.tsx b/packages/trace-viewer/src/ui/embeddedWorkbenchLoader.tsx index 17a2211c41..587f930a70 100644 --- a/packages/trace-viewer/src/ui/embeddedWorkbenchLoader.tsx +++ b/packages/trace-viewer/src/ui/embeddedWorkbenchLoader.tsx @@ -86,7 +86,7 @@ export const EmbeddedWorkbenchLoader: React.FunctionComponent = () => {
- + {!traceURLs.length &&
Select test to see the trace
} diff --git a/packages/trace-viewer/src/ui/uiModeTraceView.tsx b/packages/trace-viewer/src/ui/uiModeTraceView.tsx index a652904683..6ee4feeed2 100644 --- a/packages/trace-viewer/src/ui/uiModeTraceView.tsx +++ b/packages/trace-viewer/src/ui/uiModeTraceView.tsx @@ -25,15 +25,13 @@ import type { ContextEntry } from '../entries'; import type { SourceLocation } from './modelUtil'; import { idForAction, MultiTraceModel } from './modelUtil'; import { Workbench } from './workbench'; -import { type Setting } from '@web/uiUtils'; export const TraceView: React.FC<{ - showRouteActionsSetting: Setting, item: { treeItem?: TreeItem, testFile?: SourceLocation, testCase?: reporterTypes.TestCase }, rootDir?: string, onOpenExternally?: (location: SourceLocation) => void, revealSource?: boolean, -}> = ({ showRouteActionsSetting, item, rootDir, onOpenExternally, revealSource }) => { +}> = ({ item, rootDir, onOpenExternally, revealSource }) => { const [model, setModel] = React.useState<{ model: MultiTraceModel, isLive: boolean } | undefined>(); const [counter, setCounter] = React.useState(0); const pollTimer = React.useRef(null); @@ -91,7 +89,6 @@ export const TraceView: React.FC<{ return = ({
, openPage?: (url: string, target?: string) => Window | any, onOpenExternally?: (location: modelUtil.SourceLocation) => void, revealSource?: boolean, -}> = ({ showRouteActionsSetting, model, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged, isLive, status, inert, openPage, onOpenExternally, revealSource }) => { + showSettings?: boolean, +}> = ({ model, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged, isLive, status, inert, openPage, onOpenExternally, revealSource, showSettings }) => { const [selectedAction, setSelectedActionImpl] = React.useState(undefined); const [revealedStack, setRevealedStack] = React.useState(undefined); const [highlightedAction, setHighlightedAction] = React.useState(); @@ -70,11 +70,7 @@ export const Workbench: React.FunctionComponent<{ const activeAction = model ? highlightedAction || selectedAction : undefined; const [selectedTime, setSelectedTime] = React.useState(); const [sidebarLocation, setSidebarLocation] = useSetting<'bottom' | 'right'>('propertiesSidebarLocation', 'bottom'); - const [, , showRouteActionsSettingInternal] = useSetting(showRouteActionsSetting ? undefined : 'show-route-actions', true, 'Show route actions'); - - const showSettings = !showRouteActionsSetting; - showRouteActionsSetting ||= showRouteActionsSettingInternal; - const showRouteActions = showRouteActionsSetting[0]; + const [showRouteActions, , showRouteActionsSetting] = useSetting('show-route-actions', true, 'Show route actions'); const filteredActions = React.useMemo(() => { return (model?.actions || []).filter(action => showRouteActions || action.class !== 'Route'); diff --git a/packages/trace-viewer/src/ui/workbenchLoader.tsx b/packages/trace-viewer/src/ui/workbenchLoader.tsx index 0260df0d7a..e7f94e969a 100644 --- a/packages/trace-viewer/src/ui/workbenchLoader.tsx +++ b/packages/trace-viewer/src/ui/workbenchLoader.tsx @@ -165,7 +165,7 @@ export const WorkbenchLoader: React.FunctionComponent<{
- + {fileForLocalModeError &&
Trace Viewer uses Service Workers to show traces. To view trace:
diff --git a/packages/web/src/components/splitView.tsx b/packages/web/src/components/splitView.tsx index a252996e73..2db241dfc4 100644 --- a/packages/web/src/components/splitView.tsx +++ b/packages/web/src/components/splitView.tsx @@ -38,8 +38,14 @@ export const SplitView: React.FC> = ({ settingName, children }) => { - const [hSize, setHSize] = useSetting(settingName ? settingName + '.' + orientation + ':size' : undefined, Math.max(minSidebarSize, sidebarSize) * window.devicePixelRatio); - const [vSize, setVSize] = useSetting(settingName ? settingName + '.' + orientation + ':size' : undefined, Math.max(minSidebarSize, sidebarSize) * window.devicePixelRatio); + const defaultSize = Math.max(minSidebarSize, sidebarSize) * window.devicePixelRatio; + const hSetting = useSetting((settingName ?? 'unused') + '.' + orientation + ':size', defaultSize); + const vSetting = useSetting((settingName ?? 'unused') + '.' + orientation + ':size', defaultSize); + const hState = React.useState(defaultSize); + const vState = React.useState(defaultSize); + const [hSize, setHSize] = settingName ? hSetting : hState; + const [vSize, setVSize] = settingName ? vSetting : vState; + const [resizing, setResizing] = React.useState<{ offset: number, size: number } | null>(null); const [measure, ref] = useMeasure(); diff --git a/packages/web/src/uiUtils.ts b/packages/web/src/uiUtils.ts index 9901a24bde..4499ed81f5 100644 --- a/packages/web/src/uiUtils.ts +++ b/packages/web/src/uiUtils.ts @@ -141,26 +141,32 @@ export function copy(text: string) { export type Setting = readonly [T, (value: T) => void, string]; -export function useSetting(name: string | undefined, defaultValue: S, title?: string): [S, React.Dispatch>, Setting] { - if (name) - defaultValue = settings.getObject(name, defaultValue); - const [value, setValue] = React.useState(defaultValue); - const setValueWrapper = React.useCallback((value: React.SetStateAction) => { - if (name) - settings.setObject(name, value); - setValue(value); - }, [name, setValue]); +export function useSetting(name: string, defaultValue: S, title?: string): [S, (v: S) => void, Setting] { + const subscribe = React.useCallback((onStoreChange: () => void) => { + settings.onChangeEmitter.addEventListener(name, onStoreChange); + return () => settings.onChangeEmitter.removeEventListener(name, onStoreChange); + }, [name]); + + const value = React.useSyncExternalStore(subscribe, () => settings.getObject(name, defaultValue)); + + const setValueWrapper = React.useCallback((value: S) => { + settings.setObject(name, value); + }, [name]); + const setting = [value, setValueWrapper, title || name || ''] as Setting; return [value, setValueWrapper, setting]; } export class Settings { + onChangeEmitter = new EventTarget(); + getString(name: string, defaultValue: string): string { return localStorage[name] || defaultValue; } setString(name: string, value: string) { localStorage[name] = value; + this.onChangeEmitter.dispatchEvent(new Event(name)); if ((window as any).saveSettings) (window as any).saveSettings(); } @@ -177,6 +183,8 @@ export class Settings { setObject(name: string, value: T) { localStorage[name] = JSON.stringify(value); + this.onChangeEmitter.dispatchEvent(new Event(name)); + if ((window as any).saveSettings) (window as any).saveSettings(); }