Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove 'context' parameter from TGUIs #12172

Merged
merged 4 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
19 changes: 9 additions & 10 deletions tgui/docs/tutorial-and-examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ recommend getting yourself introduced to

A React component is not a regular HTML template. A component is a
javascript function, which accepts a `props` object (that contains
properties passed to a component) and a `context` object (which is
necessary to access UI data) as arguments, and outputs an HTML-like
properties passed to a component) as an argument, and outputs an HTML-like
structure.

So let's create our first React Component. Create a file with a name
Expand All @@ -119,8 +118,8 @@ import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components';
import { Window } from '../layouts';

export const SampleInterface = (props, context) => {
const { act, data } = useBackend(context);
export const SampleInterface = (props) => {
const { act, data } = useBackend();
// Extract `health` and `color` variables from the `data` object.
const {
health,
Expand Down Expand Up @@ -150,7 +149,7 @@ export const SampleInterface = (props, context) => {
};
```

Here are the key variables you get from a `useBackend(context)` function:
Here are the key variables you get from a `useBackend()` function:

- `config` is part of core tgui. It contains meta-information about the
interface and who uses it, BYOND refs to various objects, and so forth.
Expand Down Expand Up @@ -251,7 +250,7 @@ import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components';
import { Window } from '../layouts';

export const SampleInterface = (props, context) => {
export const SampleInterface = (props) => {
return (
<Window>
<Window.Content scrollable>
Expand All @@ -261,8 +260,8 @@ export const SampleInterface = (props, context) => {
);
};

const HealthStatus = (props, context) => {
const { act, data } = useBackend(context);
const HealthStatus = (props) => {
const { act, data } = useBackend();
const {
user,
} = props;
Expand Down Expand Up @@ -322,8 +321,8 @@ import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components';
import { Window } from '../layouts';

export const SampleInterface = (props, context) => {
const { act, data } = useBackend(context);
export const SampleInterface = (props) => {
const { act, data } = useBackend();
// Extract `health` and `color` variables from the `data` object.
const {
health,
Expand Down
8 changes: 6 additions & 2 deletions tgui/packages/common/redux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,16 @@ export const createAction = <TAction extends string>(type: TAction, prepare?: (.
export const useDispatch = <TAction extends Action = AnyAction>(context: {
store: Store<unknown, TAction>;
}): Dispatch<TAction> => {
return context.store.dispatch;
return context?.store?.dispatch;
};

export const useSelector = <State, Selected>(
context: { store: Store<State, Action> },
selector: (state: State) => Selected
): Selected => {
return selector(context.store.getState());
if (!context) {
return {} as Selected;
}

return selector(context?.store?.getState());
};
3 changes: 3 additions & 0 deletions tgui/packages/tgui-panel/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { pingMiddleware, pingReducer } from './ping';
import { settingsMiddleware, settingsReducer } from './settings';
import { statMiddleware, statReducer } from './stat';
import { telemetryMiddleware } from './telemetry';
import { setGlobalStore } from 'tgui/backend';

perf.mark('inception', window.performance?.timing?.navigationStart);
perf.mark('init');
Expand Down Expand Up @@ -50,6 +51,8 @@ const store = configureStore({
});

const renderApp = createRenderer(() => {
setGlobalStore(store);

const { Panel } = require('./Panel');
return (
<StoreProvider store={store}>
Expand Down
34 changes: 20 additions & 14 deletions tgui/packages/tgui/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ import { resumeRenderer, suspendRenderer } from './renderer';

const logger = createLogger('backend');

export let globalStore;

export const setGlobalStore = (store) => {
globalStore = store;
};

export const backendUpdate = createAction('backend/update');
export const backendSetSharedState = createAction('backend/setSharedState');
export const backendSuspendStart = createAction('backend/suspendStart');
Expand Down Expand Up @@ -246,6 +252,10 @@ type BackendState<TData> = {
shared: Record<string, any>;
suspending: boolean;
suspended: boolean;
debug?: {
debugLayout: boolean;
kitchenSink: boolean;
};
};

/**
Expand All @@ -261,9 +271,9 @@ export const selectBackend = <TData>(state: any): BackendState<TData> => state.b
*
* You can make
*/
export const useBackend = <TData>(context: any) => {
const { store } = context;
const state = selectBackend<TData>(store.getState());
export const useBackend = <TData>() => {
const state: BackendState<TData> = globalStore?.getState()?.backend;

return {
...state,
act: sendAct,
Expand All @@ -284,19 +294,17 @@ type StateWithSetter<T> = [T, (nextState: T) => void];
*
* It is a lot more performant than `setSharedState`.
*
* @param context React context.
* @param key Key which uniquely identifies this state in Redux store.
* @param initialState Initializes your global variable with this value.
*/
export const useLocalState = <T>(context: any, key: string, initialState: T): StateWithSetter<T> => {
const { store } = context;
const state = selectBackend(store.getState());
const sharedStates = state.shared ?? {};
export const useLocalState = <T>(key: string, initialState: T): StateWithSetter<T> => {
const state = globalStore?.getState()?.backend;
const sharedStates = state?.shared ?? {};
const sharedState = key in sharedStates ? sharedStates[key] : initialState;
return [
sharedState,
(nextState) => {
store.dispatch(
globalStore.dispatch(
backendSetSharedState({
key,
nextState: typeof nextState === 'function' ? nextState(sharedState) : nextState,
Expand All @@ -316,14 +324,12 @@ export const useLocalState = <T>(context: any, key: string, initialState: T): St
*
* This makes creation of observable s
*
* @param context React context.
* @param key Key which uniquely identifies this state in Redux store.
* @param initialState Initializes your global variable with this value.
*/
export const useSharedState = <T>(context: any, key: string, initialState: T): StateWithSetter<T> => {
const { store } = context;
const state = selectBackend(store.getState());
const sharedStates = state.shared ?? {};
export const useSharedState = <T>(key: string, initialState: T): StateWithSetter<T> => {
const state = globalStore?.getState()?.backend;
const sharedStates = state?.shared ?? {};
const sharedState = key in sharedStates ? sharedStates[key] : initialState;
return [
sharedState,
Expand Down
4 changes: 2 additions & 2 deletions tgui/packages/tgui/components/CollapsibleSection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useLocalState } from '../backend';
import { Section } from './Section';
import { Button } from './Button';

export const CollapsibleSection = (props, context) => {
export const CollapsibleSection = (props) => {
const {
children,
startOpen = true,
Expand All @@ -13,7 +13,7 @@ export const CollapsibleSection = (props, context) => {
showButton = !forceOpen,
...rest
} = props;
const [isOpen, setOpen] = useLocalState(context, `open_collapsible_${sectionKey}`, startOpen);
const [isOpen, setOpen] = useLocalState(`open_collapsible_${sectionKey}`, startOpen);
return (
<Section
fitted={!forceOpen && !isOpen}
Expand Down
41 changes: 18 additions & 23 deletions tgui/packages/tgui/components/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,29 +182,24 @@ export class Dropdown extends Component<DropdownProps, DropdownState> {

const to_render = ops.length ? ops : 'No Options Found';

render(
<div>{to_render}</div>,
renderedMenu,
() => {
let singletonPopper = Dropdown.singletonPopper;
if (singletonPopper === undefined) {
singletonPopper = createPopper(Dropdown.virtualElement, renderedMenu!, {
...DEFAULT_OPTIONS,
placement: 'bottom-start',
});

Dropdown.singletonPopper = singletonPopper;
} else {
singletonPopper.setOptions({
...DEFAULT_OPTIONS,
placement: 'bottom-start',
});

singletonPopper.update();
}
},
this.context
);
render(<div>{to_render}</div>, renderedMenu, () => {
let singletonPopper = Dropdown.singletonPopper;
if (singletonPopper === undefined) {
singletonPopper = createPopper(Dropdown.virtualElement, renderedMenu!, {
...DEFAULT_OPTIONS,
placement: 'bottom-start',
});

Dropdown.singletonPopper = singletonPopper;
} else {
singletonPopper.setOptions({
...DEFAULT_OPTIONS,
placement: 'bottom-start',
});

singletonPopper.update();
}
});
}

setOpen(open: boolean) {
Expand Down
2 changes: 1 addition & 1 deletion tgui/packages/tgui/components/Popper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class Popper extends Component<PopperProps> {
renderPopperContent(callback: () => void) {
// `render` errors when given false, so we convert it to `null`,
// which is supported.
render(this.props.popperContent || null, this.renderedContent, callback, this.context);
render(this.props.popperContent || null, this.renderedContent, callback);
}

render() {
Expand Down
4 changes: 2 additions & 2 deletions tgui/packages/tgui/components/TextArea.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { toInputValue } from './Input';
import { KEY_ENTER, KEY_ESCAPE, KEY_TAB } from 'common/keycodes';

export class TextArea extends Component {
constructor(props, context) {
super(props, context);
constructor(props) {
super(props);
this.textareaRef = props.innerRef || createRef();
this.fillerRef = createRef();
this.state = {
Expand Down
41 changes: 18 additions & 23 deletions tgui/packages/tgui/components/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,29 +100,24 @@ export class Tooltip extends Component<TooltipProps, TooltipState> {
return;
}

render(
<span>{this.props.content}</span>,
renderedTooltip,
() => {
let singletonPopper = Tooltip.singletonPopper;
if (singletonPopper === undefined) {
singletonPopper = createPopper(Tooltip.virtualElement, renderedTooltip!, {
...DEFAULT_OPTIONS,
placement: this.props.position || 'auto',
});

Tooltip.singletonPopper = singletonPopper;
} else {
singletonPopper.setOptions({
...DEFAULT_OPTIONS,
placement: this.props.position || 'auto',
});

singletonPopper.update();
}
},
this.context
);
render(<span>{this.props.content}</span>, renderedTooltip, () => {
let singletonPopper = Tooltip.singletonPopper;
if (singletonPopper === undefined) {
singletonPopper = createPopper(Tooltip.virtualElement, renderedTooltip!, {
...DEFAULT_OPTIONS,
placement: this.props.position || 'auto',
});

Tooltip.singletonPopper = singletonPopper;
} else {
singletonPopper.setOptions({
...DEFAULT_OPTIONS,
placement: this.props.position || 'auto',
});

singletonPopper.update();
}
});
}

componentDidUpdate() {
Expand Down
6 changes: 3 additions & 3 deletions tgui/packages/tgui/debug/KitchenSink.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ const r = require.context('../stories', false, /\.stories\.jsx$/);
*/
const getStories = () => r.keys().map((path) => r(path));

export const KitchenSink = (props, context) => {
export const KitchenSink = (props) => {
const { panel } = props;
const [theme] = useLocalState(context, 'kitchenSinkTheme');
const [pageIndex, setPageIndex] = useLocalState(context, 'pageIndex', 0);
const [theme] = useLocalState('kitchenSinkTheme');
const [pageIndex, setPageIndex] = useLocalState('pageIndex', 0);
const stories = getStories();
const story = stories[pageIndex];
const Layout = panel ? Pane : Window;
Expand Down
11 changes: 5 additions & 6 deletions tgui/packages/tgui/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,28 +43,27 @@ import './styles/themes/retro.scss';
import './styles/themes/syndicate.scss';
import './styles/themes/thinktronic-classic.scss';

import { StoreProvider, configureStore } from './store';
import { configureStore } from './store';

import { captureExternalLinks } from './links';
import { createRenderer } from './renderer';
import { perf } from 'common/perf';
import { setupGlobalEvents } from './events';
import { setupHotKeys } from './hotkeys';
import { setupHotReloading } from 'tgui-dev-server/link/client.cjs';
import { setGlobalStore } from './backend';

perf.mark('inception', window.performance?.timing?.navigationStart);
perf.mark('init');

const store = configureStore();

const renderApp = createRenderer(() => {
setGlobalStore(store);

const { getRoutedComponent } = require('./routes');
const Component = getRoutedComponent(store);
return (
<StoreProvider store={store}>
<Component />
</StoreProvider>
);
return <Component />;
});

const setupApp = () => {
Expand Down
Loading
Loading