Skip to content

Commit

Permalink
Load environment names in the settings UI
Browse files Browse the repository at this point in the history
By loading the latest value when clicking the button to open the settings.
  • Loading branch information
sebmarkbage committed Aug 30, 2024
1 parent 45d2c2e commit 073397a
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 13 deletions.
19 changes: 19 additions & 0 deletions packages/react-devtools-shared/src/backend/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ export default class Agent extends EventEmitter<{
this.updateConsolePatchSettings,
);
bridge.addListener('updateComponentFilters', this.updateComponentFilters);
bridge.addListener('getEnvironmentNames', this.getEnvironmentNames);

// Temporarily support older standalone front-ends sending commands to newer embedded backends.
// We do this because React Native embeds the React DevTools backend,
Expand Down Expand Up @@ -814,6 +815,24 @@ export default class Agent extends EventEmitter<{
}
};

getEnvironmentNames: () => void = () => {
let accumulatedNames = null;
for (const rendererID in this._rendererInterfaces) {
const renderer = this._rendererInterfaces[+rendererID];
const names = renderer.getEnvironmentNames();
if (accumulatedNames === null) {
accumulatedNames = names;
} else {
for (let i = 0; i < names.length; i++) {
if (accumulatedNames.indexOf(names[i]) === -1) {
accumulatedNames.push(names[i]);
}
}
}
}
this._bridge.send('environmentNames', accumulatedNames || []);
};

onTraceUpdates: (nodes: Set<HostInstance>) => void = nodes => {
this.emit('traceUpdates', nodes);
};
Expand Down
5 changes: 5 additions & 0 deletions packages/react-devtools-shared/src/backend/fiber/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,10 @@ export function attach(
flushPendingEvents();
}

function getEnvironmentNames(): Array<string> {
return Array.from(knownEnvironmentNames);
}

function shouldFilterVirtual(
data: ReactComponentInfo,
secondaryEnv: null | string,
Expand Down Expand Up @@ -5801,5 +5805,6 @@ export function attach(
storeAsGlobal,
unpatchConsoleForStrictMode,
updateComponentFilters,
getEnvironmentNames,
};
}
6 changes: 6 additions & 0 deletions packages/react-devtools-shared/src/backend/legacy/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,11 @@ export function attach(
// Not implemented.
}

function getEnvironmentNames(): Array<string> {
// No RSC support.
return [];
}

function setTraceUpdatesEnabled(enabled: boolean) {
// Not implemented.
}
Expand Down Expand Up @@ -1152,5 +1157,6 @@ export function attach(
storeAsGlobal,
unpatchConsoleForStrictMode,
updateComponentFilters,
getEnvironmentNames,
};
}
1 change: 1 addition & 0 deletions packages/react-devtools-shared/src/backend/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ export type RendererInterface = {
) => void,
unpatchConsoleForStrictMode: () => void,
updateComponentFilters: (componentFilters: Array<ComponentFilter>) => void,
getEnvironmentNames: () => Array<string>,

// Timeline profiler interface

Expand Down
2 changes: 2 additions & 0 deletions packages/react-devtools-shared/src/bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ export type BackendEvents = {
operations: [Array<number>],
ownersList: [OwnersList],
overrideComponentFilters: [Array<ComponentFilter>],
environmentNames: [Array<string>],
profilingData: [ProfilingDataBackend],
profilingStatus: [boolean],
reloadAppForProfiling: [],
Expand Down Expand Up @@ -237,6 +238,7 @@ type FrontendEvents = {
stopProfiling: [],
storeAsGlobal: [StoreAsGlobalParams],
updateComponentFilters: [Array<ComponentFilter>],
getEnvironmentNames: [],
updateConsolePatchSettings: [ConsolePatchSettings],
viewAttributeSource: [ViewAttributeSourceParams],
viewElementSource: [ElementAndRendererID],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
useMemo,
useRef,
useState,
use,
} from 'react';
import {
LOCAL_STORAGE_OPEN_IN_EDITOR_URL,
Expand Down Expand Up @@ -58,7 +59,11 @@ import type {

const vscodeFilepath = 'vscode://file/{path}:{line}';

export default function ComponentsSettings(_: {}): React.Node {
export default function ComponentsSettings({
environmentNames,
}: {
environmentNames: Promise<Array<string>>,
}): React.Node {
const store = useContext(StoreContext);
const {parseHookNames, setParseHookNames} = useContext(SettingsContext);

Expand Down Expand Up @@ -103,6 +108,23 @@ export default function ComponentsSettings(_: {}): React.Node {
Array<ComponentFilter>,
>(() => [...store.componentFilters]);

const usedEnvironmentNames = use(environmentNames);

const resolvedEnvironmentNames = useMemo(() => {
const set = new Set(usedEnvironmentNames);
// Client is special and is always available as a default.
set.add('Client');
// If there are other filters already specified but are not currently
// on the page, we still allow them as options.
for (let i = 0; i < componentFilters.length; i++) {
const filter = componentFilters[i];
if (filter.type === ComponentFilterEnvironmentName) {
set.add(filter.value);
}
}
return Array.from(set);
}, [usedEnvironmentNames, componentFilters]);

const addFilter = useCallback(() => {
setComponentFilters(prevComponentFilters => {
return [
Expand Down Expand Up @@ -479,8 +501,11 @@ export default function ComponentsSettings(_: {}): React.Node {
currentTarget.value,
)
}>
<option value={'Client'}>Client</option>
<option value={'Server'}>Server</option>
{resolvedEnvironmentNames.map(name => (
<option key={name} value={name}>
{name}
</option>
))}
</select>
)}
</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export default function SettingsModal(_: {}): React.Node {
}

function SettingsModalImpl(_: {}) {
const {setIsModalShowing} = useContext(SettingsModalContext);
const {setIsModalShowing, environmentNames} =
useContext(SettingsModalContext);
const dismissModal = useCallback(
() => setIsModalShowing(false),
[setIsModalShowing],
Expand All @@ -81,7 +82,7 @@ function SettingsModalImpl(_: {}) {
let view = null;
switch (selectedTabID) {
case 'components':
view = <ComponentsSettings />;
view = <ComponentsSettings environmentNames={environmentNames} />;
break;
// $FlowFixMe[incompatible-type] is this missing in TabID?
case 'debugging':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,67 @@
import type {ReactContext} from 'shared/ReactTypes';

import * as React from 'react';
import {createContext, useMemo, useState} from 'react';
import {
createContext,
useContext,
useCallback,
useState,
startTransition,
} from 'react';

import {BridgeContext} from '../context';
import type {FrontendBridge} from '../../../bridge';

export type DisplayDensity = 'comfortable' | 'compact';
export type Theme = 'auto' | 'light' | 'dark';

type Context = {
isModalShowing: boolean,
setIsModalShowing: (value: boolean) => void,
...
environmentNames: null | Promise<Array<string>>,
};

const SettingsModalContext: ReactContext<Context> = createContext<Context>(
((null: any): Context),
);
SettingsModalContext.displayName = 'SettingsModalContext';

function fetchEnvironmentNames(bridge: FrontendBridge): Promise<Array<string>> {
return new Promise(resolve => {
function onEnvironmentNames(names: Array<string>) {
bridge.removeListener('environmentNames', onEnvironmentNames);
resolve(names);
}
bridge.addListener('environmentNames', onEnvironmentNames);
bridge.send('getEnvironmentNames');
});
}

function SettingsModalContextController({
children,
}: {
children: React$Node,
}): React.Node {
const [isModalShowing, setIsModalShowing] = useState<boolean>(false);
const bridge = useContext(BridgeContext);

const value = useMemo(
() => ({isModalShowing, setIsModalShowing}),
[isModalShowing, setIsModalShowing],
);
const setIsModalShowing: boolean => void = useCallback((value: boolean) => {
startTransition(() => {
setContext({
isModalShowing: value,
setIsModalShowing,
environmentNames: value ? fetchEnvironmentNames(bridge) : null,
});
});
});

const [currentContext, setContext] = useState<Context>({
isModalShowing: false,
setIsModalShowing,
environmentNames: null,
});

return (
<SettingsModalContext.Provider value={value}>
<SettingsModalContext.Provider value={currentContext}>
{children}
</SettingsModalContext.Provider>
);
Expand Down

0 comments on commit 073397a

Please sign in to comment.