From 2ac6816317c40bebce23a66bcdd685eafdfe0393 Mon Sep 17 00:00:00 2001 From: Aleksej Komarov Date: Sat, 11 Apr 2020 17:30:18 +0300 Subject: [PATCH] Add useGlobal for storing global UI vars in Redux store. --- tgui/packages/tgui/interfaces/Uplink.js | 225 +++++++++++------------- tgui/packages/tgui/store.js | 49 ++++++ 2 files changed, 154 insertions(+), 120 deletions(-) diff --git a/tgui/packages/tgui/interfaces/Uplink.js b/tgui/packages/tgui/interfaces/Uplink.js index be245dc4d2f..68102af2456 100644 --- a/tgui/packages/tgui/interfaces/Uplink.js +++ b/tgui/packages/tgui/interfaces/Uplink.js @@ -1,130 +1,115 @@ import { decodeHtmlEntities } from 'common/string'; -import { Component, Fragment } from 'inferno'; -import { act } from '../byond'; -import { Box, Button, Input, Section, Table, Tabs } from '../components'; +import { Fragment } from 'inferno'; import { useBackend } from '../backend'; +import { Box, Button, Input, Section, Table, Tabs } from '../components'; import { Window } from '../layouts'; +import { useGlobal } from '../store'; // It's a class because we need to store state in the form of the current // hovered item, and current search terms -export class Uplink extends Component { - constructor() { - super(); - this.state = { - hoveredItem: {}, - currentSearch: '', - }; - } - - setHoveredItem(hoveredItem) { - this.setState({ - hoveredItem, - }); - } - - setSearchText(currentSearch) { - this.setState({ - currentSearch, - }); - } - - render() { - const { act, data } = useBackend(this.context); - const { - compact_mode, - lockable, - telecrystals, - categories = [], - } = data; - const { hoveredItem, currentSearch } = this.state; - return ( - - -
0 ? 'good' : 'bad'}> - {telecrystals} TC - - )} - buttons={( - - Search - this.setSearchText(value)} - ml={1} - mr={1} /> +export const Uplink = (props, context) => { + const { act, data } = useBackend(context); + const { + compact_mode, + lockable, + telecrystals, + categories = [], + } = data; + const [ + hoveredItem, + setHoveredItem, + ] = useGlobal(context, 'hoveredItem', {}); + const [ + searchText, + setSearchText, + ] = useGlobal(context, 'searchText', ''); + return ( + + +
0 ? 'good' : 'bad'}> + {telecrystals} TC + + )} + buttons={( + + Search + setSearchText(value)} + ml={1} + mr={1} /> +
-
-
- ); - } -} + icon="lock" + content="Lock" + onClick={() => act('lock')} /> + )} +
+ )}> + {searchText.length > 0 ? ( + + { + return category.items || []; + }) + .filter(item => { + const searchTerm = searchText.toLowerCase(); + const searchableString = String(item.name + item.desc) + .toLowerCase(); + return searchableString.includes(searchTerm); + })} + hoveredItem={hoveredItem} + onBuyMouseOver={item => setHoveredItem(item)} + onBuyMouseOut={item => setHoveredItem({})} + onBuy={item => act('buy', { + item: item.name, + })} /> +
+ ) : ( + + {categories.map(category => { + const { name, items } = category; + if (items === null) { + return; + } + return ( + + {() => ( + setHoveredItem(item)} + onBuyMouseOut={item => setHoveredItem({})} + onBuy={item => act('buy', { + item: item.name, + })} /> + )} + + ); + })} + + )} +
+
+
+ ); +}; const ItemList = props => { const { diff --git a/tgui/packages/tgui/store.js b/tgui/packages/tgui/store.js index 2fc95f6476e..a0cc9eb72c7 100644 --- a/tgui/packages/tgui/store.js +++ b/tgui/packages/tgui/store.js @@ -11,6 +11,7 @@ export const createStore = () => { // Global state reducers backendReducer, hotKeyReducer, + globalStateReducer, ]); const middleware = [ // loggingMiddleware, @@ -29,3 +30,51 @@ export class StoreProvider extends Component { return this.props.children; } } + +export const useDispatch = context => { + return context.store.dispatch; +}; + +/** + * Allocates a global variable on Redux store. + * + * Great when you want to store some UI state globally, without having to + * modify DM object you're working with to have that var and export it via + * ui_data. + * + * @param {any} context React context. + * @param {string} key Key which uniquely identifies this state in Redux store. + * @param {any} initialState Initializes your global variable with this value. + */ +export const useGlobal = (context, key, initialState) => { + const { store } = context; + const globalObj = store.getState().global ?? {}; + const state = (key in globalObj) + ? globalObj[key] + : initialState; + const setState = nextState => { + store.dispatch({ + type: 'setGlobal', + payload: { key, nextState }, + }); + }; + return [state, setState]; +}; + +/** + * Reducer, which handles actions coming from useGlobal. + */ +const globalStateReducer = (state, action) => { + const { type, payload } = action; + if (type === 'setGlobal') { + const { key, nextState } = payload; + return { + ...state, + global: { + ...state.global, + [key]: nextState, + }, + }; + } + return state; +};