From 12bdaa11c4956db4e82ca6d096384bf96b4134b9 Mon Sep 17 00:00:00 2001 From: Arthri <41360489+Arthri@users.noreply.github.com> Date: Thu, 18 Apr 2024 20:18:20 +0800 Subject: [PATCH] Remove several functions from collections.js which have ES5 equivalents (#82417) --- tgui/packages/common/collections.ts | 239 +++++++++--------- tgui/packages/common/fp.js | 24 -- tgui/packages/common/vector.js | 48 ---- tgui/packages/common/vector.ts | 51 ++++ tgui/packages/tgui-panel/chat/selectors.ts | 2 +- tgui/packages/tgui/components/Chart.tsx | 17 +- tgui/packages/tgui/drag.ts | 14 +- tgui/packages/tgui/interfaces/ApcControl.jsx | 38 +-- .../tgui/interfaces/AtmosControlPanel.jsx | 9 +- .../tgui/interfaces/BluespaceSender.tsx | 9 +- .../tgui/interfaces/BluespaceVendor.tsx | 9 +- .../tgui/interfaces/CameraConsole.tsx | 21 +- tgui/packages/tgui/interfaces/Cargo.jsx | 14 +- .../tgui/interfaces/CommunicationsConsole.jsx | 10 +- tgui/packages/tgui/interfaces/CrewConsole.jsx | 2 +- .../tgui/interfaces/DestinationTagger.tsx | 19 +- .../DnaConsole/DnaConsoleStorage.jsx | 2 +- .../interfaces/DnaConsole/MutationInfo.jsx | 9 +- .../tgui/interfaces/ExperimentConfigure.jsx | 2 +- .../interfaces/Fabrication/DesignBrowser.tsx | 14 +- .../Fabrication/MaterialAccessBar.tsx | 2 +- tgui/packages/tgui/interfaces/Fax.tsx | 3 +- tgui/packages/tgui/interfaces/Filteriffic.jsx | 14 +- tgui/packages/tgui/interfaces/FishCatalog.tsx | 5 +- tgui/packages/tgui/interfaces/Gps.jsx | 44 ++-- .../tgui/interfaces/Hypertorus/Gases.tsx | 9 +- tgui/packages/tgui/interfaces/Jukebox.tsx | 3 +- .../packages/tgui/interfaces/LibraryAdmin.tsx | 27 +- .../tgui/interfaces/LibraryConsole.jsx | 25 +- .../tgui/interfaces/LibraryVisitor.jsx | 9 +- tgui/packages/tgui/interfaces/MatMarket.tsx | 2 +- .../interfaces/MedicalRecords/RecordTabs.tsx | 9 +- .../tgui/interfaces/NtosCrewManifest.jsx | 4 +- .../tgui/interfaces/NtosMessenger/index.tsx | 3 +- .../tgui/interfaces/NtosNetDownloader.tsx | 23 +- .../packages/tgui/interfaces/Orbit/helpers.ts | 23 +- tgui/packages/tgui/interfaces/Orbit/index.tsx | 12 +- .../tgui/interfaces/PersonalCrafting.tsx | 65 +++-- tgui/packages/tgui/interfaces/Photocopier.jsx | 2 +- .../tgui/interfaces/PlaneMasterDebug.tsx | 21 +- .../tgui/interfaces/PortableChemMixer.tsx | 3 +- .../packages/tgui/interfaces/PowerMonitor.jsx | 26 +- .../interfaces/PreferencesMenu/AntagsPage.tsx | 7 +- .../PreferencesMenu/GamePreferencesPage.tsx | 10 +- .../interfaces/PreferencesMenu/JobsPage.tsx | 5 +- .../PreferencesMenu/KeybindingsPage.tsx | 15 +- .../interfaces/PreferencesMenu/MainPage.tsx | 53 ++-- .../interfaces/PreferencesMenu/QuirksPage.tsx | 12 +- .../tgui/interfaces/PreferencesMenu/names.tsx | 6 +- .../preferences/features/base.tsx | 9 +- .../character_preferences/skin_tone.tsx | 5 +- .../features/game_preferences/ghost.tsx | 7 +- tgui/packages/tgui/interfaces/Radio.jsx | 4 +- .../RequestsConsole/MessageWriteTab.tsx | 8 +- .../tgui/interfaces/RestockTracker.jsx | 3 +- .../interfaces/SecurityRecords/RecordTabs.tsx | 9 +- .../tgui/interfaces/SeedExtractor.tsx | 8 +- .../tgui/interfaces/SelectEquipment.jsx | 21 +- .../tgui/interfaces/ShuttleManipulator.jsx | 4 +- .../tgui/interfaces/StackCrafting.tsx | 40 ++- .../tgui/interfaces/StationAlertConsole.jsx | 4 +- .../tgui/interfaces/StationTraitsPanel.tsx | 14 +- tgui/packages/tgui/interfaces/Supermatter.tsx | 13 +- .../tgui/interfaces/SurgeryInitiator.tsx | 3 +- tgui/packages/tgui/interfaces/Techweb.jsx | 11 +- .../tgui/interfaces/TrackedPlaytime.jsx | 2 +- .../tgui/interfaces/WarrantConsole.tsx | 2 +- .../tgui/interfaces/common/AccessConfig.tsx | 3 +- .../tgui/interfaces/common/AccessList.jsx | 3 +- 69 files changed, 590 insertions(+), 578 deletions(-) delete mode 100644 tgui/packages/common/vector.js create mode 100644 tgui/packages/common/vector.ts diff --git a/tgui/packages/common/collections.ts b/tgui/packages/common/collections.ts index 5bfcee85884..9aed42557dc 100644 --- a/tgui/packages/common/collections.ts +++ b/tgui/packages/common/collections.ts @@ -12,33 +12,36 @@ * If collection is 'null' or 'undefined', it will be returned "as is" * without emitting any errors (which can be useful in some cases). */ -export const filter = - (iterateeFn: (input: T, index: number, collection: T[]) => boolean) => - (collection: T[]): T[] => { - if (collection === null || collection === undefined) { - return collection; - } - if (Array.isArray(collection)) { - const result: T[] = []; - for (let i = 0; i < collection.length; i++) { - const item = collection[i]; - if (iterateeFn(item, i, collection)) { - result.push(item); - } +export const filter = ( + collection: T[], + iterateeFn: (input: T, index: number, collection: T[]) => boolean, +): T[] => { + if (collection === null || collection === undefined) { + return collection; + } + if (Array.isArray(collection)) { + const result: T[] = []; + for (let i = 0; i < collection.length; i++) { + const item = collection[i]; + if (iterateeFn(item, i, collection)) { + result.push(item); } - return result; } - throw new Error(`filter() can't iterate on type ${typeof collection}`); - }; + return result; + } + throw new Error(`filter() can't iterate on type ${typeof collection}`); +}; type MapFunction = { ( + collection: T[], iterateeFn: (value: T, index: number, collection: T[]) => U, - ): (collection: T[]) => U[]; + ): U[]; ( + collection: Record, iterateeFn: (value: T, index: K, collection: Record) => U, - ): (collection: Record) => U[]; + ): U[]; }; /** @@ -49,44 +52,30 @@ type MapFunction = { * If collection is 'null' or 'undefined', it will be returned "as is" * without emitting any errors (which can be useful in some cases). */ -export const map: MapFunction = - (iterateeFn) => - (collection: T[]): U[] => { - if (collection === null || collection === undefined) { - return collection; - } - - if (Array.isArray(collection)) { - return collection.map(iterateeFn); - } - - if (typeof collection === 'object') { - return Object.entries(collection).map(([key, value]) => { - return iterateeFn(value, key, collection); - }); - } - - throw new Error(`map() can't iterate on type ${typeof collection}`); - }; - -/** - * Given a collection, will run each element through an iteratee function. - * Will then filter out undefined values. - */ -export const filterMap = ( - collection: T[], - iterateeFn: (value: T) => U | undefined, -): U[] => { - const finalCollection: U[] = []; - - for (const value of collection) { - const output = iterateeFn(value); - if (output !== undefined) { - finalCollection.push(output); - } +export const map: MapFunction = (collection, iterateeFn) => { + if (collection === null || collection === undefined) { + return collection; } - return finalCollection; + if (Array.isArray(collection)) { + const result: unknown[] = []; + for (let i = 0; i < collection.length; i++) { + result.push(iterateeFn(collection[i], i, collection)); + } + return result; + } + + if (typeof collection === 'object') { + const result: unknown[] = []; + for (let i in collection) { + if (Object.prototype.hasOwnProperty.call(collection, i)) { + result.push(iterateeFn(collection[i], i, collection)); + } + } + return result; + } + + throw new Error(`map() can't iterate on type ${typeof collection}`); }; const COMPARATOR = (objA, objB) => { @@ -112,39 +101,38 @@ const COMPARATOR = (objA, objB) => { * * Iteratees are called with one argument (value). */ -export const sortBy = - (...iterateeFns: ((input: T) => unknown)[]) => - (array: T[]): T[] => { - if (!Array.isArray(array)) { - return array; - } - let length = array.length; - // Iterate over the array to collect criteria to sort it by - let mappedArray: { - criteria: unknown[]; - value: T; - }[] = []; - for (let i = 0; i < length; i++) { - const value = array[i]; - mappedArray.push({ - criteria: iterateeFns.map((fn) => fn(value)), - value, - }); - } - // Sort criteria using the base comparator - mappedArray.sort(COMPARATOR); +export const sortBy = ( + array: T[], + ...iterateeFns: ((input: T) => unknown)[] +): T[] => { + if (!Array.isArray(array)) { + return array; + } + let length = array.length; + // Iterate over the array to collect criteria to sort it by + let mappedArray: { + criteria: unknown[]; + value: T; + }[] = []; + for (let i = 0; i < length; i++) { + const value = array[i]; + mappedArray.push({ + criteria: iterateeFns.map((fn) => fn(value)), + value, + }); + } + // Sort criteria using the base comparator + mappedArray.sort(COMPARATOR); - // Unwrap values - const values: T[] = []; - while (length--) { - values[length] = mappedArray[length].value; - } - return values; - }; + // Unwrap values + const values: T[] = []; + while (length--) { + values[length] = mappedArray[length].value; + } + return values; +}; -export const sort = sortBy(); - -export const sortStrings = sortBy(); +export const sort = (array: T[]): T[] => sortBy(array); /** * Returns a range of numbers from start to end, exclusively. @@ -153,12 +141,34 @@ export const sortStrings = sortBy(); export const range = (start: number, end: number): number[] => new Array(end - start).fill(null).map((_, index) => index + start); +type ReduceFunction = { + ( + array: T[], + reducerFn: ( + accumulator: U, + currentValue: T, + currentIndex: number, + array: T[], + ) => U, + initialValue: U, + ): U; + ( + array: T[], + reducerFn: ( + accumulator: T, + currentValue: T, + currentIndex: number, + array: T[], + ) => T, + ): T; +}; + /** * A fast implementation of reduce. */ -export const reduce = (reducerFn, initialValue) => (array) => { +export const reduce: ReduceFunction = (array, reducerFn, initialValue?) => { const length = array.length; - let i; + let i: number; let result; if (initialValue === undefined) { i = 1; @@ -184,15 +194,16 @@ export const reduce = (reducerFn, initialValue) => (array) => { * is determined by the order they occur in the array. The iteratee is * invoked with one argument: value. */ -export const uniqBy = - (iterateeFn?: (value: T) => unknown) => - (array: T[]): T[] => { - const { length } = array; - const result: T[] = []; - const seen: unknown[] = iterateeFn ? [] : result; - let index = -1; - // prettier-ignore - outer: +export const uniqBy = ( + array: T[], + iterateeFn?: (value: T) => unknown, +): T[] => { + const { length } = array; + const result: T[] = []; + const seen: unknown[] = iterateeFn ? [] : result; + let index = -1; + // prettier-ignore + outer: while (++index < length) { let value: T | 0 = array[index]; const computed = iterateeFn ? iterateeFn(value) : value; @@ -214,10 +225,10 @@ export const uniqBy = result.push(value); } } - return result; - }; + return result; +}; -export const uniq = uniqBy(); +export const uniq = (array: T[]): T[] => uniqBy(array); type Zip = { [I in keyof T]: T[I] extends (infer U)[] ? U : never; @@ -247,17 +258,6 @@ export const zip = (...arrays: T): Zip => { return result; }; -/** - * This method is like "zip" except that it accepts iteratee to - * specify how grouped values should be combined. The iteratee is - * invoked with the elements of each group. - */ -export const zipWith = - (iterateeFn: (...values: T[]) => U) => - (...arrays: T[][]): U[] => { - return map((values: T[]) => iterateeFn(...values))(zip(...arrays)); - }; - const binarySearch = ( getKey: (value: T) => U, collection: readonly T[], @@ -293,13 +293,15 @@ const binarySearch = ( return compare > insertingKey ? middle : middle + 1; }; -export const binaryInsertWith = - (getKey: (value: T) => U) => - (collection: readonly T[], value: T) => { - const copy = [...collection]; - copy.splice(binarySearch(getKey, collection, value), 0, value); - return copy; - }; +export const binaryInsertWith = ( + collection: readonly T[], + value: T, + getKey: (value: T) => U, +): T[] => { + const copy = [...collection]; + copy.splice(binarySearch(getKey, collection, value), 0, value); + return copy; +}; /** * This method takes a collection of items and a number, returning a collection @@ -325,7 +327,8 @@ export const paginate = (collection: T[], maxPerPage: number): T[][] => { return pages; }; -const isObject = (obj: unknown) => typeof obj === 'object' && obj !== null; +const isObject = (obj: unknown): obj is object => + typeof obj === 'object' && obj !== null; // Does a deep merge of two objects. DO NOT FEED CIRCULAR OBJECTS!! export const deepMerge = (...objects: any[]): any => { diff --git a/tgui/packages/common/fp.js b/tgui/packages/common/fp.js index ba7df09d407..675e98d807e 100644 --- a/tgui/packages/common/fp.js +++ b/tgui/packages/common/fp.js @@ -23,27 +23,3 @@ export const flow = (...funcs) => (input, ...rest) => { } return output; }; - -/** - * Composes single-argument functions from right to left. - * - * All functions might accept a context in form of additional arguments. - * If the resulting function is called with more than 1 argument, rest of - * the arguments are passed to all functions unchanged. - * - * @param {...Function} funcs The functions to compose - * @returns {Function} A function obtained by composing the argument functions - * from right to left. For example, compose(f, g, h) is identical to doing - * (input, ...rest) => f(g(h(input, ...rest), ...rest), ...rest) - */ -export const compose = (...funcs) => { - if (funcs.length === 0) { - return (arg) => arg; - } - if (funcs.length === 1) { - return funcs[0]; - } - // prettier-ignore - return funcs.reduce((a, b) => (value, ...rest) => - a(b(value, ...rest), ...rest)); -}; diff --git a/tgui/packages/common/vector.js b/tgui/packages/common/vector.js deleted file mode 100644 index b1f85f7429d..00000000000 --- a/tgui/packages/common/vector.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * N-dimensional vector manipulation functions. - * - * Vectors are plain number arrays, i.e. [x, y, z]. - * - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -import { map, reduce, zipWith } from './collections'; - -const ADD = (a, b) => a + b; -const SUB = (a, b) => a - b; -const MUL = (a, b) => a * b; -const DIV = (a, b) => a / b; - -export const vecAdd = (...vecs) => { - return reduce((a, b) => zipWith(ADD)(a, b))(vecs); -}; - -export const vecSubtract = (...vecs) => { - return reduce((a, b) => zipWith(SUB)(a, b))(vecs); -}; - -export const vecMultiply = (...vecs) => { - return reduce((a, b) => zipWith(MUL)(a, b))(vecs); -}; - -export const vecDivide = (...vecs) => { - return reduce((a, b) => zipWith(DIV)(a, b))(vecs); -}; - -export const vecScale = (vec, n) => { - return map((x) => x * n)(vec); -}; - -export const vecInverse = (vec) => { - return map((x) => -x)(vec); -}; - -export const vecLength = (vec) => { - return Math.sqrt(reduce(ADD)(zipWith(MUL)(vec, vec))); -}; - -export const vecNormalize = (vec) => { - return vecDivide(vec, vecLength(vec)); -}; diff --git a/tgui/packages/common/vector.ts b/tgui/packages/common/vector.ts new file mode 100644 index 00000000000..c91715a8f99 --- /dev/null +++ b/tgui/packages/common/vector.ts @@ -0,0 +1,51 @@ +/** + * N-dimensional vector manipulation functions. + * + * Vectors are plain number arrays, i.e. [x, y, z]. + * + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { map, reduce, zip } from './collections'; + +const ADD = (a: number, b: number): number => a + b; +const SUB = (a: number, b: number): number => a - b; +const MUL = (a: number, b: number): number => a * b; +const DIV = (a: number, b: number): number => a / b; + +export type Vector = number[]; + +export const vecAdd = (...vecs: Vector[]): Vector => { + return map(zip(...vecs), (x) => reduce(x, ADD)); +}; + +export const vecSubtract = (...vecs: Vector[]): Vector => { + return map(zip(...vecs), (x) => reduce(x, SUB)); +}; + +export const vecMultiply = (...vecs: Vector[]): Vector => { + return map(zip(...vecs), (x) => reduce(x, MUL)); +}; + +export const vecDivide = (...vecs: Vector[]): Vector => { + return map(zip(...vecs), (x) => reduce(x, DIV)); +}; + +export const vecScale = (vec: Vector, n: number): Vector => { + return map(vec, (x) => x * n); +}; + +export const vecInverse = (vec: Vector): Vector => { + return map(vec, (x) => -x); +}; + +export const vecLength = (vec: Vector): number => { + return Math.sqrt(reduce(vecMultiply(vec, vec), ADD)); +}; + +export const vecNormalize = (vec: Vector): Vector => { + const length = vecLength(vec); + return map(vec, (c) => c / length); +}; diff --git a/tgui/packages/tgui-panel/chat/selectors.ts b/tgui/packages/tgui-panel/chat/selectors.ts index 2908f661264..3c1e0b4f429 100644 --- a/tgui/packages/tgui-panel/chat/selectors.ts +++ b/tgui/packages/tgui-panel/chat/selectors.ts @@ -9,7 +9,7 @@ import { map } from 'common/collections'; export const selectChat = (state) => state.chat; export const selectChatPages = (state) => - map((id: string) => state.chat.pageById[id])(state.chat.pages); + map(state.chat.pages, (id: string) => state.chat.pageById[id]); export const selectCurrentChatPage = (state) => state.chat.pageById[state.chat.currentPageId]; diff --git a/tgui/packages/tgui/components/Chart.tsx b/tgui/packages/tgui/components/Chart.tsx index 205dc6fbec1..bc33ff90606 100644 --- a/tgui/packages/tgui/components/Chart.tsx +++ b/tgui/packages/tgui/components/Chart.tsx @@ -4,7 +4,7 @@ * @license MIT */ -import { map, zipWith } from 'common/collections'; +import { map, zip } from 'common/collections'; import { Component, createRef, RefObject } from 'react'; import { Box, BoxProps } from './Box'; @@ -37,8 +37,8 @@ const normalizeData = ( return []; } - const min = zipWith(Math.min)(...data); - const max = zipWith(Math.max)(...data); + const min = map(zip(...data), (p) => Math.min(...p)); + const max = map(zip(...data), (p) => Math.max(...p)); if (rangeX !== undefined) { min[0] = rangeX[0]; @@ -50,11 +50,12 @@ const normalizeData = ( max[1] = rangeY[1]; } - const normalized = map((point: Point) => { - return zipWith((value: number, min: number, max: number, scale: number) => { - return ((value - min) / (max - min)) * scale; - })(point, min, max, scale); - })(data); + const normalized = map(data, (point) => + map( + zip(point, min, max, scale), + ([value, min, max, scale]) => ((value - min) / (max - min)) * scale, + ), + ); return normalized; }; diff --git a/tgui/packages/tgui/drag.ts b/tgui/packages/tgui/drag.ts index 584666a97a6..0884b1b0bd7 100644 --- a/tgui/packages/tgui/drag.ts +++ b/tgui/packages/tgui/drag.ts @@ -209,7 +209,7 @@ export const dragStartHandler = (event) => { dragPointOffset = vecSubtract( [event.screenX, event.screenY], getWindowPosition(), - ); + ) as [number, number]; // Focus click target (event.target as HTMLElement)?.focus(); document.addEventListener('mousemove', dragMoveHandler); @@ -234,7 +234,10 @@ const dragMoveHandler = (event: MouseEvent) => { } event.preventDefault(); setWindowPosition( - vecSubtract([event.screenX, event.screenY], dragPointOffset), + vecSubtract([event.screenX, event.screenY], dragPointOffset) as [ + number, + number, + ], ); }; @@ -247,7 +250,7 @@ export const resizeStartHandler = dragPointOffset = vecSubtract( [event.screenX, event.screenY], getWindowPosition(), - ); + ) as [number, number]; initialSize = getWindowSize(); // Focus click target (event.target as HTMLElement)?.focus(); @@ -278,7 +281,10 @@ const resizeMoveHandler = (event: MouseEvent) => { ); const delta = vecSubtract(currentOffset, dragPointOffset); // Extra 1x1 area is added to ensure the browser can see the cursor - size = vecAdd(initialSize, vecMultiply(resizeMatrix, delta), [1, 1]); + size = vecAdd(initialSize, vecMultiply(resizeMatrix, delta), [1, 1]) as [ + number, + number, + ]; // Sane window size values size[0] = Math.max(size[0], 150 * pixelRatio); size[1] = Math.max(size[1], 50 * pixelRatio); diff --git a/tgui/packages/tgui/interfaces/ApcControl.jsx b/tgui/packages/tgui/interfaces/ApcControl.jsx index acf46f9f0b8..fb29c78032d 100644 --- a/tgui/packages/tgui/interfaces/ApcControl.jsx +++ b/tgui/packages/tgui/interfaces/ApcControl.jsx @@ -160,18 +160,21 @@ const ApcControlScene = (props) => { const [sortByField] = useLocalState('sortByField', 'name'); const apcs = flow([ - map((apc, i) => ({ - ...apc, - // Generate a unique id - id: apc.name + i, - })), - sortByField === 'name' && sortBy((apc) => apc.name), - sortByField === 'charge' && sortBy((apc) => -apc.charge), + (apcs) => + map(apcs, (apc, i) => ({ + ...apc, + // Generate a unique id + id: apc.name + i, + })), + sortByField === 'name' && ((apcs) => sortBy(apcs, (apc) => apc.name)), + sortByField === 'charge' && ((apcs) => sortBy(apcs, (apc) => -apc.charge)), sortByField === 'draw' && - sortBy( - (apc) => -powerRank(apc.load), - (apc) => -parseFloat(apc.load), - ), + ((apcs) => + sortBy( + apcs, + (apc) => -powerRank(apc.load), + (apc) => -parseFloat(apc.load), + )), ])(data.apcs); return ( @@ -255,14 +258,11 @@ const ApcControlScene = (props) => { const LogPanel = (props) => { const { data } = useBackend(); - const logs = flow([ - map((line, i) => ({ - ...line, - // Generate a unique id - id: line.entry + i, - })), - (logs) => logs.reverse(), - ])(data.logs); + const logs = map(data.logs, (line, i) => ({ + ...line, + // Generate a unique id + id: line.entry + i, + })).reverse(); return ( {logs.map((line) => ( diff --git a/tgui/packages/tgui/interfaces/AtmosControlPanel.jsx b/tgui/packages/tgui/interfaces/AtmosControlPanel.jsx index 84d0ff08def..c39778c410f 100644 --- a/tgui/packages/tgui/interfaces/AtmosControlPanel.jsx +++ b/tgui/packages/tgui/interfaces/AtmosControlPanel.jsx @@ -1,5 +1,4 @@ import { map, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useBackend } from '../backend'; import { Box, Button, Flex, Section, Table } from '../components'; @@ -7,14 +6,14 @@ import { Window } from '../layouts'; export const AtmosControlPanel = (props) => { const { act, data } = useBackend(); - const groups = flow([ - map((group, i) => ({ + const groups = sortBy( + map(data.excited_groups, (group, i) => ({ ...group, // Generate a unique id id: group.area + i, })), - sortBy((group) => group.id), - ])(data.excited_groups); + (group) => group.id, + ); return (
diff --git a/tgui/packages/tgui/interfaces/BluespaceSender.tsx b/tgui/packages/tgui/interfaces/BluespaceSender.tsx index 24e88f6ef5d..c8c9e47b0c7 100644 --- a/tgui/packages/tgui/interfaces/BluespaceSender.tsx +++ b/tgui/packages/tgui/interfaces/BluespaceSender.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { toFixed } from 'common/math'; import { BooleanLike } from 'common/react'; import { multiline } from 'common/string'; @@ -43,10 +42,10 @@ export const BluespaceSender = (props) => { const { act, data } = useBackend(); const { gas_transfer_rate, credits, bluespace_network_gases = [], on } = data; - const gases: Gas[] = flow([ - filter((gas) => gas.amount >= 0.01), - sortBy((gas) => -gas.amount), - ])(bluespace_network_gases); + const gases: Gas[] = sortBy( + filter(bluespace_network_gases, (gas) => gas.amount >= 0.01), + (gas) => -gas.amount, + ); const gasMax = Math.max(1, ...gases.map((gas) => gas.amount)); diff --git a/tgui/packages/tgui/interfaces/BluespaceVendor.tsx b/tgui/packages/tgui/interfaces/BluespaceVendor.tsx index 3ba8289356b..6ce242e4e48 100644 --- a/tgui/packages/tgui/interfaces/BluespaceVendor.tsx +++ b/tgui/packages/tgui/interfaces/BluespaceVendor.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { toFixed } from 'common/math'; import { BooleanLike } from 'common/react'; import { multiline } from 'common/string'; @@ -50,10 +49,10 @@ export const BluespaceVendor = (props) => { tank_full, } = data; - const gases: Gas[] = flow([ - filter((gas) => gas.amount >= 0.01), - sortBy((gas) => -gas.amount), - ])(bluespace_network_gases); + const gases: Gas[] = sortBy( + filter(bluespace_network_gases, (gas) => gas.amount >= 0.01), + (gas) => -gas.amount, + ); const gasMax = Math.max(1, ...gases.map((gas) => gas.amount)); diff --git a/tgui/packages/tgui/interfaces/CameraConsole.tsx b/tgui/packages/tgui/interfaces/CameraConsole.tsx index adad4f8748e..3dafe1240df 100644 --- a/tgui/packages/tgui/interfaces/CameraConsole.tsx +++ b/tgui/packages/tgui/interfaces/CameraConsole.tsx @@ -1,5 +1,4 @@ -import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; +import { filter, sort } from 'common/collections'; import { BooleanLike, classes } from 'common/react'; import { createSearch } from 'common/string'; import { useState } from 'react'; @@ -68,15 +67,17 @@ const prevNextCamera = ( * Filters cameras, applies search terms and sorts the alphabetically. */ const selectCameras = (cameras: Camera[], searchText = ''): Camera[] => { - const testSearch = createSearch(searchText, (camera: Camera) => camera.name); + let queriedCameras = filter(cameras, (camera: Camera) => !!camera.name); + if (searchText) { + const testSearch = createSearch( + searchText, + (camera: Camera) => camera.name, + ); + queriedCameras = filter(queriedCameras, testSearch); + } + queriedCameras = sort(queriedCameras); - return flow([ - filter((camera: Camera) => !!camera.name), - // Optional search term - searchText && filter(testSearch), - // Slightly expensive, but way better than sorting in BYOND - sortBy((camera: Camera) => camera), - ])(cameras); + return queriedCameras; }; export const CameraConsole = (props) => { diff --git a/tgui/packages/tgui/interfaces/Cargo.jsx b/tgui/packages/tgui/interfaces/Cargo.jsx index f0d698702a0..24473e42fc7 100644 --- a/tgui/packages/tgui/interfaces/Cargo.jsx +++ b/tgui/packages/tgui/interfaces/Cargo.jsx @@ -1,5 +1,4 @@ -import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; +import { filter } from 'common/collections'; import { useBackend, useSharedState } from '../backend'; import { @@ -159,16 +158,17 @@ const CargoStatus = (props) => { const searchForSupplies = (supplies, search) => { search = search.toLowerCase(); - return flow([ - (categories) => categories.flatMap((category) => category.packs), + const queriedSupplies = sortBy( filter( + supplies.flatMap((category) => category.packs), (pack) => pack.name?.toLowerCase().includes(search.toLowerCase()) || pack.desc?.toLowerCase().includes(search.toLowerCase()), ), - sortBy((pack) => pack.name), - (packs) => packs.slice(0, 25), - ])(supplies); + (pack) => pack.name, + ); + + return queriedSupplies.slice(0, 25); }; export const CargoCatalog = (props) => { diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx b/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx index e816d27be1e..48a7d98a401 100644 --- a/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx +++ b/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx @@ -29,10 +29,12 @@ const SWIPE_NEEDED = 'SWIPE_NEEDED'; const EMAG_SHUTTLE_NOTICE = 'This shuttle is deemed significantly dangerous to the crew, and is only supplied by the Syndicate.'; -const sortShuttles = sortBy( - (shuttle) => !shuttle.emagOnly, - (shuttle) => shuttle.initial_cost, -); +const sortShuttles = (shuttles) => + sortBy( + shuttles, + (shuttle) => !shuttle.emagOnly, + (shuttle) => shuttle.initial_cost, + ); const AlertButton = (props) => { const { act, data } = useBackend(); diff --git a/tgui/packages/tgui/interfaces/CrewConsole.jsx b/tgui/packages/tgui/interfaces/CrewConsole.jsx index 8ed7efa5c11..ef3f8c31a38 100644 --- a/tgui/packages/tgui/interfaces/CrewConsole.jsx +++ b/tgui/packages/tgui/interfaces/CrewConsole.jsx @@ -86,7 +86,7 @@ export const CrewConsole = () => { const CrewTable = (props) => { const { act, data } = useBackend(); - const sensors = sortBy((s) => s.ijob)(data.sensors ?? []); + const sensors = sortBy(data.sensors ?? [], (s) => s.ijob); return ( diff --git a/tgui/packages/tgui/interfaces/DestinationTagger.tsx b/tgui/packages/tgui/interfaces/DestinationTagger.tsx index b1acfe192a7..5326f93e01b 100644 --- a/tgui/packages/tgui/interfaces/DestinationTagger.tsx +++ b/tgui/packages/tgui/interfaces/DestinationTagger.tsx @@ -1,5 +1,4 @@ import { map, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useBackend } from '../backend'; import { Button, Section, Stack } from '../components'; @@ -25,13 +24,17 @@ type DestinationInfo = { * @returns The alphetically sorted list of destinations. */ const sortDestinations = (locations: string[]): DestinationInfo[] => { - return flow([ - map((name, index) => ({ - name: name.toUpperCase(), - sorting_id: index + 1, - })), - sortBy((dest) => dest.name), - ])(locations); + return sortBy( + map( + locations, + (name, index) => + ({ + name: name.toUpperCase(), + sorting_id: index + 1, + }) as DestinationInfo, + ), + (dest) => dest.name, + ); }; export const DestinationTagger = (props) => { diff --git a/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleStorage.jsx b/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleStorage.jsx index 43c22b321a0..681b4f211d7 100644 --- a/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleStorage.jsx +++ b/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleStorage.jsx @@ -211,7 +211,7 @@ const StorageButtons = (props) => { const StorageChromosomes = (props) => { const { data, act } = useBackend(); const chromos = data.chromoStorage ?? []; - const uniqueChromos = uniqBy((chromo) => chromo.Name)(chromos); + const uniqueChromos = uniqBy(chromos, (chromo) => chromo.Name); const chromoName = data.view.storageChromoName; const chromo = chromos.find((chromo) => chromo.Name === chromoName); diff --git a/tgui/packages/tgui/interfaces/DnaConsole/MutationInfo.jsx b/tgui/packages/tgui/interfaces/DnaConsole/MutationInfo.jsx index 89a1468d953..2629a029fa8 100644 --- a/tgui/packages/tgui/interfaces/DnaConsole/MutationInfo.jsx +++ b/tgui/packages/tgui/interfaces/DnaConsole/MutationInfo.jsx @@ -1,5 +1,4 @@ import { filter, uniqBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useBackend } from '../../backend'; import { @@ -122,10 +121,10 @@ export const MutationInfo = (props) => { isSameMutation(x, mutation), ); const savedToDisk = diskMutations.find((x) => isSameMutation(x, mutation)); - const combinedMutations = flow([ - uniqBy((mutation) => mutation.Name), - filter((x) => x.Name !== mutation.Name), - ])([...diskMutations, ...mutationStorage]); + const combinedMutations = filter( + uniqBy([...diskMutations, ...mutationStorage], (mutation) => mutation.Name), + (x) => x.Name !== mutation.Name, + ); return ( <> diff --git a/tgui/packages/tgui/interfaces/ExperimentConfigure.jsx b/tgui/packages/tgui/interfaces/ExperimentConfigure.jsx index 0c632afd283..d91bc564e44 100644 --- a/tgui/packages/tgui/interfaces/ExperimentConfigure.jsx +++ b/tgui/packages/tgui/interfaces/ExperimentConfigure.jsx @@ -109,7 +109,7 @@ export const ExperimentConfigure = (props) => { const { always_active, has_start_callback } = data; let techwebs = data.techwebs ?? []; - const experiments = sortBy((exp) => exp.name)(data.experiments ?? []); + const experiments = sortBy(data.experiments ?? [], (exp) => exp.name); // Group servers together by web let webs = new Map(); diff --git a/tgui/packages/tgui/interfaces/Fabrication/DesignBrowser.tsx b/tgui/packages/tgui/interfaces/Fabrication/DesignBrowser.tsx index af5bddf4d5b..e4951bd0041 100644 --- a/tgui/packages/tgui/interfaces/Fabrication/DesignBrowser.tsx +++ b/tgui/packages/tgui/interfaces/Fabrication/DesignBrowser.tsx @@ -229,8 +229,9 @@ export const DesignBrowser = ( - {sortBy((category: Category) => category.title)( + {sortBy( Object.values(root.subcategories), + (category: Category) => category.title, ).map((category) => ( (
{searchText.length > 0 ? ( - {sortBy((design: T) => design.name)( + {sortBy( Object.values(root.descendants), + (design: T) => design.name, ) .filter((design) => design.name @@ -290,8 +292,9 @@ export const DesignBrowser = ( ) : selectedCategory === ALL_CATEGORY ? ( - {sortBy((design: T) => design.name)( + {sortBy( Object.values(root.descendants), + (design: T) => design.name, ).map((design) => buildRecipeElement( design, @@ -380,8 +383,9 @@ const DesignBrowserTab = ( Object.entries(category.subcategories).length > 0 && selectedCategory === category.title && (
- {sortBy((category: Category) => category.title)( + {sortBy( Object.values(category.subcategories), + (category: Category) => category.title, ).map((subcategory) => ( ( const body = ( - {sortBy((design: T) => design.name)(category.children).map((design) => + {sortBy(category.children, (design: T) => design.name).map((design) => buildRecipeElement( design, availableMaterials || {}, diff --git a/tgui/packages/tgui/interfaces/Fabrication/MaterialAccessBar.tsx b/tgui/packages/tgui/interfaces/Fabrication/MaterialAccessBar.tsx index 2936d93df3f..83923d5bd48 100644 --- a/tgui/packages/tgui/interfaces/Fabrication/MaterialAccessBar.tsx +++ b/tgui/packages/tgui/interfaces/Fabrication/MaterialAccessBar.tsx @@ -55,7 +55,7 @@ export const MaterialAccessBar = (props: MaterialAccessBarProps) => { return ( - {sortBy((m: Material) => MATERIAL_RARITY[m.name])(availableMaterials).map( + {sortBy(availableMaterials, (m: Material) => MATERIAL_RARITY[m.name]).map( (material) => ( { const { act } = useBackend(); const { data } = useBackend(); const faxes = data.faxes - ? sortBy((sortFax: FaxInfo) => sortFax.fax_name)( + ? sortBy( data.syndicate_network ? data.faxes.filter((filterFax: FaxInfo) => filterFax.visible) : data.faxes.filter( (filterFax: FaxInfo) => filterFax.visible && !filterFax.syndicate_network, ), + (sortFax: FaxInfo) => sortFax.fax_name, ) : []; return ( diff --git a/tgui/packages/tgui/interfaces/Filteriffic.jsx b/tgui/packages/tgui/interfaces/Filteriffic.jsx index b13cddc34f4..09bd1426452 100644 --- a/tgui/packages/tgui/interfaces/Filteriffic.jsx +++ b/tgui/packages/tgui/interfaces/Filteriffic.jsx @@ -155,10 +155,9 @@ const FilterFlagsEntry = (props) => { const filterInfo = data.filter_info; const flags = filterInfo[filterType]['flags']; - return map((bitField, flagName) => ( + return map(flags, (bitField, flagName) => ( act('modify_filter_value', { name: filterName, @@ -167,8 +166,11 @@ const FilterFlagsEntry = (props) => { }, }) } - /> - ))(flags); + key={flagName} + > + {flagName} + + )); }; const FilterDataEntry = (props) => { @@ -340,9 +342,9 @@ export const Filteriffic = (props) => { {!hasFilters ? ( No filters ) : ( - map((entry, key) => ( + map(filters, (entry, key) => ( - ))(filters) + )) )}
diff --git a/tgui/packages/tgui/interfaces/FishCatalog.tsx b/tgui/packages/tgui/interfaces/FishCatalog.tsx index 01efc5e3531..7bb85054fb0 100644 --- a/tgui/packages/tgui/interfaces/FishCatalog.tsx +++ b/tgui/packages/tgui/interfaces/FishCatalog.tsx @@ -1,5 +1,4 @@ import { sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { classes } from 'common/react'; import { capitalize } from 'common/string'; import { useState } from 'react'; @@ -38,9 +37,7 @@ type FishCatalogData = { export const FishCatalog = (props) => { const { act, data } = useBackend(); const { fish_info, sponsored_by } = data; - const fish_by_name = flow([sortBy((fish: FishInfo) => fish.name)])( - fish_info || [], - ); + const fish_by_name = sortBy(fish_info || [], (fish: FishInfo) => fish.name); const [currentFish, setCurrentFish] = useState(null); return ( diff --git a/tgui/packages/tgui/interfaces/Gps.jsx b/tgui/packages/tgui/interfaces/Gps.jsx index 662335fcdc3..c12ba6a9efd 100644 --- a/tgui/packages/tgui/interfaces/Gps.jsx +++ b/tgui/packages/tgui/interfaces/Gps.jsx @@ -7,30 +7,36 @@ import { useBackend } from '../backend'; import { Box, Button, Icon, LabeledList, Section, Table } from '../components'; import { Window } from '../layouts'; -const coordsToVec = (coords) => map(parseFloat)(coords.split(', ')); +const coordsToVec = (coords) => map(coords.split(', '), parseFloat); export const Gps = (props) => { const { act, data } = useBackend(); const { currentArea, currentCoords, globalmode, power, tag, updating } = data; const signals = flow([ - map((signal, index) => { - // Calculate distance to the target. BYOND distance is capped to 127, - // that's why we roll our own calculations here. - const dist = - signal.dist && - Math.round( - vecLength( - vecSubtract(coordsToVec(currentCoords), coordsToVec(signal.coords)), - ), - ); - return { ...signal, dist, index }; - }), - sortBy( - // Signals with distance metric go first - (signal) => signal.dist === undefined, - // Sort alphabetically - (signal) => signal.entrytag, - ), + (signals) => + map(signals, (signal, index) => { + // Calculate distance to the target. BYOND distance is capped to 127, + // that's why we roll our own calculations here. + const dist = + signal.dist && + Math.round( + vecLength( + vecSubtract( + coordsToVec(currentCoords), + coordsToVec(signal.coords), + ), + ), + ); + return { ...signal, dist, index }; + }), + (signals) => + sortBy( + signals, + // Signals with distance metric go first + (signal) => signal.dist === undefined, + // Sort alphabetically + (signal) => signal.entrytag, + ), ])(data.signals || []); return ( diff --git a/tgui/packages/tgui/interfaces/Hypertorus/Gases.tsx b/tgui/packages/tgui/interfaces/Hypertorus/Gases.tsx index 8482bd665ec..74457a365bb 100644 --- a/tgui/packages/tgui/interfaces/Hypertorus/Gases.tsx +++ b/tgui/packages/tgui/interfaces/Hypertorus/Gases.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { toFixed } from 'common/math'; import { useBackend } from 'tgui/backend'; import { @@ -90,10 +89,10 @@ const GasList = (props: GasListProps) => { } = props; const { start_power, start_cooling } = data; - const gases: HypertorusGas[] = flow([ - filter((gas: HypertorusGas) => gas.amount >= 0.01), - sortBy((gas: HypertorusGas) => -gas.amount), - ])(raw_gases); + const gases: HypertorusGas[] = sortBy( + filter(raw_gases, (gas) => gas.amount >= 0.01), + (gas) => -gas.amount, + ); if (stickyGases) { ensure_gases(gases, stickyGases); diff --git a/tgui/packages/tgui/interfaces/Jukebox.tsx b/tgui/packages/tgui/interfaces/Jukebox.tsx index 8e496aab5c2..fe9eb054aef 100644 --- a/tgui/packages/tgui/interfaces/Jukebox.tsx +++ b/tgui/packages/tgui/interfaces/Jukebox.tsx @@ -1,5 +1,4 @@ import { sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { BooleanLike } from '../../common/react'; import { useBackend } from '../backend'; @@ -32,7 +31,7 @@ export const Jukebox = () => { const { act, data } = useBackend(); const { active, looping, track_selected, volume, songs } = data; - const songs_sorted: Song[] = flow([sortBy((song: Song) => song.name)])(songs); + const songs_sorted: Song[] = sortBy(songs, (song: Song) => song.name); const song_selected: Song | undefined = songs.find( (song) => song.name === track_selected, ); diff --git a/tgui/packages/tgui/interfaces/LibraryAdmin.tsx b/tgui/packages/tgui/interfaces/LibraryAdmin.tsx index f805a46c7fb..fd88fdc48fb 100644 --- a/tgui/packages/tgui/interfaces/LibraryAdmin.tsx +++ b/tgui/packages/tgui/interfaces/LibraryAdmin.tsx @@ -1,5 +1,4 @@ import { map, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { capitalize } from 'common/string'; import { useState } from 'react'; @@ -81,10 +80,14 @@ type Book = { category: string; title: string; id: number; +}; + +type AdminBook = Book & { + author_ckey: string; deleted: boolean; }; -type DisplayBook = Book & { +type DisplayAdminBook = AdminBook & { key: number; }; @@ -120,14 +123,18 @@ const SearchAndDisplay = (props) => { view_raw, show_deleted, } = data; - const books = flow([ - map((book, i) => ({ - ...book, - // Generate a unique id - key: i, - })), - sortBy((book) => book.key), - ])(pages); + const books = sortBy( + map( + pages, + (book, i) => + ({ + ...book, + // Generate a unique id + key: i, + }) as DisplayAdminBook, + ), + (book) => book.key, + ); return (
diff --git a/tgui/packages/tgui/interfaces/LibraryConsole.jsx b/tgui/packages/tgui/interfaces/LibraryConsole.jsx index 83d034915b8..386ac3ca0c0 100644 --- a/tgui/packages/tgui/interfaces/LibraryConsole.jsx +++ b/tgui/packages/tgui/interfaces/LibraryConsole.jsx @@ -1,5 +1,4 @@ import { map, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { classes } from 'common/react'; import { useState } from 'react'; @@ -136,14 +135,14 @@ export const Inventory = (props) => { export const InventoryDetails = (props) => { const { act, data } = useBackend(); - const inventory = flow([ - map((book, i) => ({ + const inventory = sortBy( + map(data.inventory, (book, i) => ({ ...book, // Generate a unique id key: i, })), - sortBy((book) => book.key), - ])(data.inventory); + (book) => book.key, + ); return (
@@ -261,14 +260,14 @@ export const CheckoutEntries = (props) => { const CheckoutModal = (props) => { const { act, data } = useBackend(); - const inventory = flow([ - map((book, i) => ({ + const inventory = sortBy( + map(data.inventory, (book, i) => ({ ...book, // Generate a unique id key: i, })), - sortBy((book) => book.key), - ])(data.inventory); + (book) => book.key, + ); const [checkoutBook, setCheckoutBook] = useLocalState('CheckoutBook', false); const [bookName, setBookName] = useState('Insert Book name...'); @@ -387,14 +386,14 @@ export const SearchAndDisplay = (props) => { params_changed, can_db_request, } = data; - const records = flow([ - map((record, i) => ({ + const records = sortBy( + map(data.pages, (record, i) => ({ ...record, // Generate a unique id key: i, })), - sortBy((record) => record.key), - ])(data.pages); + (record) => record.key, + ); return ( diff --git a/tgui/packages/tgui/interfaces/LibraryVisitor.jsx b/tgui/packages/tgui/interfaces/LibraryVisitor.jsx index 6b8edb380be..cb27e42704d 100644 --- a/tgui/packages/tgui/interfaces/LibraryVisitor.jsx +++ b/tgui/packages/tgui/interfaces/LibraryVisitor.jsx @@ -1,5 +1,4 @@ import { map, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useBackend } from '../backend'; import { @@ -71,14 +70,14 @@ const SearchAndDisplay = (props) => { author, params_changed, } = data; - const records = flow([ - map((record, i) => ({ + const records = sortBy( + map(data.pages, (record, i) => ({ ...record, // Generate a unique id key: i, })), - sortBy((record) => record.key), - ])(data.pages); + (record) => record.key, + ); return (
diff --git a/tgui/packages/tgui/interfaces/MatMarket.tsx b/tgui/packages/tgui/interfaces/MatMarket.tsx index 11de67fb656..d793b8501f0 100644 --- a/tgui/packages/tgui/interfaces/MatMarket.tsx +++ b/tgui/packages/tgui/interfaces/MatMarket.tsx @@ -118,7 +118,7 @@ export const MatMarket = (props) => {
- {sortBy((tempmat: Material) => tempmat.rarity)(materials).map( + {sortBy(materials, (tempmat: Material) => tempmat.rarity).map( (material, i) => (
diff --git a/tgui/packages/tgui/interfaces/MedicalRecords/RecordTabs.tsx b/tgui/packages/tgui/interfaces/MedicalRecords/RecordTabs.tsx index 23aafd99a61..5d20abc59ce 100644 --- a/tgui/packages/tgui/interfaces/MedicalRecords/RecordTabs.tsx +++ b/tgui/packages/tgui/interfaces/MedicalRecords/RecordTabs.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useState } from 'react'; import { useBackend, useLocalState } from 'tgui/backend'; import { @@ -28,10 +27,10 @@ export const MedicalRecordTabs = (props) => { const [search, setSearch] = useState(''); - const sorted: MedicalRecord[] = flow([ - filter((record: MedicalRecord) => isRecordMatch(record, search)), - sortBy((record: MedicalRecord) => record.name?.toLowerCase()), - ])(records); + const sorted: MedicalRecord[] = sortBy( + filter(records, (record) => isRecordMatch(record, search)), + (record) => record.name?.toLowerCase(), + ); return ( diff --git a/tgui/packages/tgui/interfaces/NtosCrewManifest.jsx b/tgui/packages/tgui/interfaces/NtosCrewManifest.jsx index 5deb1d27c8a..74485397c2d 100644 --- a/tgui/packages/tgui/interfaces/NtosCrewManifest.jsx +++ b/tgui/packages/tgui/interfaces/NtosCrewManifest.jsx @@ -20,7 +20,7 @@ export const NtosCrewManifest = (props) => { /> } > - {map((entries, department) => ( + {map(manifest, (entries, department) => (
{entries.map((entry) => ( @@ -31,7 +31,7 @@ export const NtosCrewManifest = (props) => { ))}
- ))(manifest)} + ))} diff --git a/tgui/packages/tgui/interfaces/NtosMessenger/index.tsx b/tgui/packages/tgui/interfaces/NtosMessenger/index.tsx index 852ca62cc55..896ca90d602 100644 --- a/tgui/packages/tgui/interfaces/NtosMessenger/index.tsx +++ b/tgui/packages/tgui/interfaces/NtosMessenger/index.tsx @@ -100,7 +100,8 @@ const ContactsScreen = (props: any) => { const [searchUser, setSearchUser] = useState(''); - const sortByUnreads = sortBy((chat) => chat.unread_messages); + const sortByUnreads = (array: NtChat[]) => + sortBy(array, (chat) => chat.unread_messages); const searchChatByName = createSearch( searchUser, diff --git a/tgui/packages/tgui/interfaces/NtosNetDownloader.tsx b/tgui/packages/tgui/interfaces/NtosNetDownloader.tsx index 0bfdafc7b62..489c443f0c1 100644 --- a/tgui/packages/tgui/interfaces/NtosNetDownloader.tsx +++ b/tgui/packages/tgui/interfaces/NtosNetDownloader.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { scale, toFixed } from 'common/math'; import { BooleanLike } from 'common/react'; import { createSearch } from 'common/string'; @@ -70,20 +69,22 @@ export const NtosNetDownloader = (props) => { searchItem, (program) => program.filedesc, ); - const items = flow([ + let items = searchItem.length > 0 ? // If we have a query, search everything for it. - filter(search) + filter(programs, search) : // Otherwise, show respective programs for the category. - filter((program: ProgramData) => program.category === selectedCategory), - // This sorts all programs in the lists by name and compatibility - sortBy( - (program: ProgramData) => !program.compatible, - (program: ProgramData) => program.filedesc, - ), + filter(programs, (program) => program.category === selectedCategory); + // This sorts all programs in the lists by name and compatibility + items = sortBy( + items, + (program: ProgramData) => !program.compatible, + (program: ProgramData) => program.filedesc, + ); + if (!emagged) { // This filters the list to only contain verified programs - !emagged && filter((program: ProgramData) => program.verifiedsource === 1), - ])(programs); + items = filter(items, (program) => program.verifiedsource === 1); + } const disk_free_space = downloading ? disk_size - Number(toFixed(disk_used + downloadcompletion)) : disk_size - disk_used; diff --git a/tgui/packages/tgui/interfaces/Orbit/helpers.ts b/tgui/packages/tgui/interfaces/Orbit/helpers.ts index 8ad071c699b..c0668dd02d8 100644 --- a/tgui/packages/tgui/interfaces/Orbit/helpers.ts +++ b/tgui/packages/tgui/interfaces/Orbit/helpers.ts @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { HEALTH, THREAT } from './constants'; import type { AntagGroup, Antagonist, Observable } from './types'; @@ -18,7 +17,7 @@ export const getAntagCategories = (antagonists: Antagonist[]) => { categories[antag_group].push(player); }); - return sortBy(([key]) => key)(Object.entries(categories)); + return sortBy(Object.entries(categories), ([key]) => key); }; /** Returns a disguised name in case the person is wearing someone else's ID */ @@ -43,15 +42,19 @@ export const getMostRelevant = ( searchQuery: string, observables: Observable[][], ): Observable => { - return flow([ - // Filters out anything that doesn't match search - filter((observable) => - isJobOrNameMatch(observable, searchQuery), - ), + const queriedObservables = // Sorts descending by orbiters - sortBy((observable) => -(observable.orbiters || 0)), - // Makes a single Observables list for an easy search - ])(observables.flat())[0]; + sortBy( + // Filters out anything that doesn't match search + filter( + observables + // Makes a single Observables list for an easy search + .flat(), + (observable) => isJobOrNameMatch(observable, searchQuery), + ), + (observable) => -(observable.orbiters || 0), + ); + return queriedObservables[0]; }; /** Returns the display color for certain health percentages */ diff --git a/tgui/packages/tgui/interfaces/Orbit/index.tsx b/tgui/packages/tgui/interfaces/Orbit/index.tsx index 0d992d981a8..0103fe8750f 100644 --- a/tgui/packages/tgui/interfaces/Orbit/index.tsx +++ b/tgui/packages/tgui/interfaces/Orbit/index.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { capitalizeFirst, multiline } from 'common/string'; import { useBackend, useLocalState } from 'tgui/backend'; import { @@ -204,16 +203,13 @@ const ObservableSection = (props: { const [searchQuery] = useLocalState('searchQuery', ''); - const filteredSection: Observable[] = flow([ - filter((observable) => - isJobOrNameMatch(observable, searchQuery), - ), - sortBy((observable) => + const filteredSection = sortBy( + filter(section, (observable) => isJobOrNameMatch(observable, searchQuery)), + (observable) => getDisplayName(observable.full_name, observable.name) .replace(/^"/, '') .toLowerCase(), - ), - ])(section); + ); if (!filteredSection.length) { return null; diff --git a/tgui/packages/tgui/interfaces/PersonalCrafting.tsx b/tgui/packages/tgui/interfaces/PersonalCrafting.tsx index 58ba4d81708..dce7491e981 100644 --- a/tgui/packages/tgui/interfaces/PersonalCrafting.tsx +++ b/tgui/packages/tgui/interfaces/PersonalCrafting.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { BooleanLike, classes } from 'common/react'; import { createSearch } from 'common/string'; import { useState } from 'react'; @@ -183,44 +182,44 @@ export const PersonalCrafting = (props) => { const [activeType, setFoodType] = useState( Object.keys(craftability).length ? 'Can Make' : data.foodtypes[0], ); - const material_occurences = flow([ - sortBy((material) => -material.occurences), - ])(data.material_occurences); + const material_occurences = sortBy( + data.material_occurences, + (material) => -material.occurences, + ); const [activeMaterial, setMaterial] = useState( material_occurences[0].atom_id, ); const [tabMode, setTabMode] = useState(0); const searchName = createSearch(searchText, (item: Recipe) => item.name); - let recipes = flow([ - filter( - (recipe) => - // If craftable only is selected, then filter by craftability - (!display_craftable_only || Boolean(craftability[recipe.ref])) && - // Ignore categories and types when searching - (searchText.length > 0 || - // Is foodtype mode and the active type matches - (tabMode === TABS.foodtype && - mode === MODE.cooking && - ((activeType === 'Can Make' && Boolean(craftability[recipe.ref])) || - recipe.foodtypes?.includes(activeType))) || - // Is material mode and the active material or catalysts match - (tabMode === TABS.material && - Object.keys(recipe.reqs).includes(activeMaterial)) || - // Is category mode and the active categroy matches - (tabMode === TABS.category && - ((activeCategory === 'Can Make' && - Boolean(craftability[recipe.ref])) || - recipe.category === activeCategory))), - ), - sortBy((recipe) => [ - activeCategory === 'Can Make' - ? 99 - Object.keys(recipe.reqs).length - : Number(craftability[recipe.ref]), - recipe.name.toLowerCase(), - ]), - ])(data.recipes); + let recipes = filter( + data.recipes, + (recipe) => + // If craftable only is selected, then filter by craftability + (!display_craftable_only || Boolean(craftability[recipe.ref])) && + // Ignore categories and types when searching + (searchText.length > 0 || + // Is foodtype mode and the active type matches + (tabMode === TABS.foodtype && + mode === MODE.cooking && + ((activeType === 'Can Make' && Boolean(craftability[recipe.ref])) || + recipe.foodtypes?.includes(activeType))) || + // Is material mode and the active material or catalysts match + (tabMode === TABS.material && + Object.keys(recipe.reqs).includes(activeMaterial)) || + // Is category mode and the active categroy matches + (tabMode === TABS.category && + ((activeCategory === 'Can Make' && + Boolean(craftability[recipe.ref])) || + recipe.category === activeCategory))), + ); + recipes = sortBy(recipes, (recipe) => [ + activeCategory === 'Can Make' + ? 99 - Object.keys(recipe.reqs).length + : Number(craftability[recipe.ref]), + recipe.name.toLowerCase(), + ]); if (searchText.length > 0) { - recipes = recipes.filter(searchName); + recipes = filter(recipes, searchName); } const canMake = ['Can Make']; const categories = canMake diff --git a/tgui/packages/tgui/interfaces/Photocopier.jsx b/tgui/packages/tgui/interfaces/Photocopier.jsx index 29353918e19..e1b8b74bb0a 100644 --- a/tgui/packages/tgui/interfaces/Photocopier.jsx +++ b/tgui/packages/tgui/interfaces/Photocopier.jsx @@ -178,7 +178,7 @@ const Blanks = (props) => { const { act, data } = useBackend(); const { blanks, categories, category } = data; - const sortedBlanks = sortBy((blank) => blank.name)(blanks || []); + const sortedBlanks = sortBy(blanks || [], (blank) => blank.name); const selectedCategory = category ?? categories[0]; const visibleBlanks = sortedBlanks.filter( diff --git a/tgui/packages/tgui/interfaces/PlaneMasterDebug.tsx b/tgui/packages/tgui/interfaces/PlaneMasterDebug.tsx index b045304c4ca..1a623bb2079 100644 --- a/tgui/packages/tgui/interfaces/PlaneMasterDebug.tsx +++ b/tgui/packages/tgui/interfaces/PlaneMasterDebug.tsx @@ -158,7 +158,7 @@ const sortConnectionRefs = function ( direction: ConnectionDirection, connectSources: AssocConnected, ) { - refs = sortBy((connection: ConnectionRef) => connection.sort_by)(refs); + refs = sortBy(refs, (connection: ConnectionRef) => connection.sort_by); refs.map((connection, index) => { let connectSource = connectSources[connection.ref]; if (direction === ConnectionDirection.Outgoing) { @@ -264,14 +264,15 @@ const positionPlanes = (connectSources: AssocConnected) => { // and get rid of the now unneeded parent refs const stack = depth_stack.map((layer) => flow([ - sortBy((plane: string) => plane_info[plane].plane), - sortBy((plane: string) => { - const read_from = plane_info[layer[plane]]; - if (!read_from) { - return 0; - } - return read_from.plane; - }), + (planes) => sortBy(planes, (plane: string) => plane_info[plane].plane), + (planes) => + sortBy(planes, (plane: string) => { + const read_from = plane_info[layer[plane]]; + if (!read_from) { + return 0; + } + return read_from.plane; + }), ])(Object.keys(layer)), ); @@ -932,7 +933,7 @@ const AddModal = (props) => { ); const plane_list = Object.keys(plane_info).map((plane) => plane_info[plane]); - const planes = sortBy((plane: Plane) => -plane.plane)(plane_list); + const planes = sortBy(plane_list, (plane: Plane) => -plane.plane); const plane_options = planes.map((plane) => plane.name); diff --git a/tgui/packages/tgui/interfaces/PortableChemMixer.tsx b/tgui/packages/tgui/interfaces/PortableChemMixer.tsx index fc0b4cbb95e..9ffbe06b943 100644 --- a/tgui/packages/tgui/interfaces/PortableChemMixer.tsx +++ b/tgui/packages/tgui/interfaces/PortableChemMixer.tsx @@ -26,8 +26,9 @@ export const PortableChemMixer = (props) => { const { act, data } = useBackend(); const { beaker } = data; const beakerTransferAmounts = beaker ? beaker.transferAmounts : []; - const chemicals = sortBy((chem: DispensableReagent) => chem.id)( + const chemicals = sortBy( data.chemicals, + (chem: DispensableReagent) => chem.id, ); return ( diff --git a/tgui/packages/tgui/interfaces/PowerMonitor.jsx b/tgui/packages/tgui/interfaces/PowerMonitor.jsx index 6e0e0dca173..039e700f68c 100644 --- a/tgui/packages/tgui/interfaces/PowerMonitor.jsx +++ b/tgui/packages/tgui/interfaces/PowerMonitor.jsx @@ -48,18 +48,22 @@ export const PowerMonitorContent = (props) => { const maxValue = Math.max(PEAK_DRAW, ...history.supply, ...history.demand); // Process area data const areas = flow([ - map((area, i) => ({ - ...area, - // Generate a unique id - id: area.name + i, - })), - sortByField === 'name' && sortBy((area) => area.name), - sortByField === 'charge' && sortBy((area) => -area.charge), + (areas) => + map(areas, (area, i) => ({ + ...area, + // Generate a unique id + id: area.name + i, + })), + sortByField === 'name' && ((areas) => sortBy(areas, (area) => area.name)), + sortByField === 'charge' && + ((areas) => sortBy(areas, (area) => -area.charge)), sortByField === 'draw' && - sortBy( - (area) => -powerRank(area.load), - (area) => -parseFloat(area.load), - ), + ((areas) => + sortBy( + areas, + (area) => -powerRank(area.load), + (area) => -parseFloat(area.load), + )), ])(data.areas); return ( <> diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx index 2ff85058da6..c00889536e0 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx @@ -25,9 +25,10 @@ const antagsByCategory = new Map(); // This will break at priorities higher than 10, but that almost definitely // will not happen. -const binaryInsertAntag = binaryInsertWith((antag: Antagonist) => { - return `${antag.priority}_${antag.name}`; -}); +const binaryInsertAntag = (collection: Antagonist[], value: Antagonist) => + binaryInsertWith(collection, value, (antag) => { + return `${antag.priority}_${antag.name}`; + }); for (const antagKey of requireAntag.keys()) { const antag = requireAntag<{ diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx index 05845ffe548..ad81b2c27fd 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx @@ -13,11 +13,13 @@ type PreferenceChild = { children: ReactNode; }; -const binaryInsertPreference = binaryInsertWith( - (child) => child.name, -); +const binaryInsertPreference = ( + collection: PreferenceChild[], + value: PreferenceChild, +) => binaryInsertWith(collection, value, (child) => child.name); -const sortByName = sortBy<[string, PreferenceChild[]]>(([name]) => name); +const sortByName = (array: [string, PreferenceChild[]][]) => + sortBy(array, ([name]) => name); export const GamePreferencesPage = (props) => { const { act, data } = useBackend(); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx index 7df2acc06e3..27d0e73833c 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx @@ -14,10 +14,11 @@ import { import { ServerPreferencesFetcher } from './ServerPreferencesFetcher'; const sortJobs = (entries: [string, Job][], head?: string) => - sortBy<[string, Job]>( + sortBy( + entries, ([key, _]) => (key === head ? -1 : 1), ([key, _]) => key, - )(entries); + ); const PRIORITY_BUTTON_SIZE = '18px'; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx index d8c2bccc61c..32d39c287df 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx @@ -67,15 +67,14 @@ const KEY_CODE_TO_BYOND: Record = { */ const DOM_KEY_LOCATION_NUMPAD = 3; -const sortKeybindings = sortBy(([_, keybinding]: [string, Keybinding]) => { - return keybinding.name; -}); +const sortKeybindings = (array: [string, Keybinding][]) => + sortBy(array, ([_, keybinding]) => { + return keybinding.name; + }); -const sortKeybindingsByCategory = sortBy( - ([category, _]: [string, Record]) => { - return category; - }, -); +const sortKeybindingsByCategory = ( + array: [string, Record][], +) => sortBy(array, ([category, _]) => category); const formatKeyboardEvent = (event: KeyboardEvent): string => { let text = ''; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx index dbb31c73cbb..c79827d2abe 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx @@ -1,10 +1,8 @@ -import { filterMap, sortBy } from 'common/collections'; +import { filter, map, sortBy } from 'common/collections'; import { classes } from 'common/react'; +import { createSearch } from 'common/string'; import { useState } from 'react'; -import { filter } from '../../../common/collections'; -import { flow } from '../../../common/fp'; -import { createSearch } from '../../../common/string'; import { sendAct, useBackend } from '../../backend'; import { Autofocus, @@ -202,8 +200,14 @@ const ChoicedSelection = (props: { }; const searchInCatalog = (searchText = '', catalog: Record) => { - const maybeSearch = createSearch(searchText, ([name, _icon]) => name); - return flow([searchText && filter(maybeSearch)])(Object.entries(catalog)); + let items = Object.entries(catalog); + if (searchText) { + items = filter( + items, + createSearch(searchText, ([name, _icon]) => name), + ); + } + return items; }; const GenderButton = (props: { @@ -368,10 +372,11 @@ const createSetRandomization = }); }; -const sortPreferences = sortBy<[string, unknown]>(([featureId, _]) => { - const feature = features[featureId]; - return feature?.name; -}); +const sortPreferences = (array: [string, unknown][]) => + sortBy(array, ([featureId, _]) => { + const feature = features[featureId]; + return feature?.name; + }); export const PreferenceList = (props: { act: typeof sendAct; @@ -451,22 +456,20 @@ export const getRandomization = ( const { data } = useBackend(); + if (!randomBodyEnabled) { + return {}; + } + return Object.fromEntries( - filterMap(Object.keys(preferences), (preferenceKey) => { - if (serverData.random.randomizable.indexOf(preferenceKey) === -1) { - return undefined; - } - - if (!randomBodyEnabled) { - return undefined; - } - - return [ - preferenceKey, - data.character_preferences.randomization[preferenceKey] || - RandomSetting.Disabled, - ]; - }), + map( + filter(Object.keys(preferences), (key) => + serverData.random.randomizable.includes(key), + ), + (key) => [ + key, + data.character_preferences.randomization[key] || RandomSetting.Disabled, + ], + ), ); }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx index c145b7eb3db..2dde5ab60cc 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx @@ -1,4 +1,4 @@ -import { filterMap } from 'common/collections'; +import { filter } from 'common/collections'; import { useState } from 'react'; import { useBackend } from '../../backend'; @@ -23,13 +23,9 @@ function getCorrespondingPreferences( relevant_preferences: Record, ) { return Object.fromEntries( - filterMap(Object.keys(relevant_preferences), (key) => { - if (!customization_options.includes(key)) { - return undefined; - } - - return [key, relevant_preferences[key]]; - }), + filter(Object.entries(relevant_preferences), ([key, value]) => + customization_options.includes(key), + ), ); } diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx index eadb6e94d42..039fb8027ad 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx @@ -21,9 +21,11 @@ type NameWithKey = { name: Name; }; -const binaryInsertName = binaryInsertWith(({ key }) => key); +const binaryInsertName = (collection: NameWithKey[], value: NameWithKey) => + binaryInsertWith(collection, value, ({ key }) => key); -const sortNameWithKeyEntries = sortBy<[string, NameWithKey[]]>(([key]) => key); +const sortNameWithKeyEntries = (array: [string, NameWithKey[]][]) => + sortBy(array, ([key]) => key); export const MultiNameInput = (props: { handleClose: () => void; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx index f481c129d28..8878292e657 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx @@ -1,4 +1,4 @@ -import { sortBy, sortStrings } from 'common/collections'; +import { sort, sortBy } from 'common/collections'; import { BooleanLike, classes } from 'common/react'; import { ComponentType, @@ -21,7 +21,8 @@ import { import { createSetPreference, PreferencesMenuData } from '../../data'; import { ServerPreferencesFetcher } from '../../ServerPreferencesFetcher'; -export const sortChoices = sortBy<[string, ReactNode]>(([name]) => name); +export const sortChoices = (array: [string, ReactNode][]) => + sortBy(array, ([name]) => name); export type Feature< TReceiving, @@ -209,7 +210,7 @@ export const FeatureDropdownInput = ( return ( ; }; -const sortHexValues = sortBy<[string, HexValue]>( - ([_, hexValue]) => -hexValue.lightness, -); +const sortHexValues = (array: [string, HexValue][]) => + sortBy(array, ([_, hexValue]) => -hexValue.lightness); export const skin_tone: Feature = { name: 'Skin tone', diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx index cfa323dce2c..ab973e3659e 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx @@ -22,10 +22,13 @@ export const ghost_accs: FeatureChoiced = { component: FeatureDropdownInput, }; -const insertGhostForm = binaryInsertWith<{ +type GhostForm = { displayText: ReactNode; value: string; -}>(({ value }) => value); +}; + +const insertGhostForm = (collection: GhostForm[], value: GhostForm) => + binaryInsertWith(collection, value, ({ value }) => value); const GhostFormInput = ( props: FeatureValueProps, diff --git a/tgui/packages/tgui/interfaces/Radio.jsx b/tgui/packages/tgui/interfaces/Radio.jsx index d25409e033a..d852149571a 100644 --- a/tgui/packages/tgui/interfaces/Radio.jsx +++ b/tgui/packages/tgui/interfaces/Radio.jsx @@ -23,10 +23,10 @@ export const Radio = (props) => { const tunedChannel = RADIO_CHANNELS.find( (channel) => channel.freq === frequency, ); - const channels = map((value, key) => ({ + const channels = map(data.channels, (value, key) => ({ name: key, status: !!value, - }))(data.channels); + })); // Calculate window height let height = 106; if (subspace) { diff --git a/tgui/packages/tgui/interfaces/RequestsConsole/MessageWriteTab.tsx b/tgui/packages/tgui/interfaces/RequestsConsole/MessageWriteTab.tsx index 5195c451838..117df20b8c0 100644 --- a/tgui/packages/tgui/interfaces/RequestsConsole/MessageWriteTab.tsx +++ b/tgui/packages/tgui/interfaces/RequestsConsole/MessageWriteTab.tsx @@ -1,4 +1,4 @@ -import { sortStrings } from 'common/collections'; +import { sort } from 'common/collections'; import { useState } from 'react'; import { useBackend, useLocalState } from '../../backend'; @@ -22,9 +22,9 @@ export const MessageWriteTab = (props) => { information_consoles = [], } = data; - const sorted_assistance = sortStrings(assistance_consoles); - const sorted_supply = sortStrings(supply_consoles); - const sorted_information = sortStrings(information_consoles); + const sorted_assistance = sort(assistance_consoles); + const sorted_supply = sort(supply_consoles); + const sorted_information = sort(information_consoles); const resetMessage = () => { setMessageText(''); diff --git a/tgui/packages/tgui/interfaces/RestockTracker.jsx b/tgui/packages/tgui/interfaces/RestockTracker.jsx index 236f486069c..7df606adffc 100644 --- a/tgui/packages/tgui/interfaces/RestockTracker.jsx +++ b/tgui/packages/tgui/interfaces/RestockTracker.jsx @@ -17,8 +17,9 @@ export const Restock = (props) => { export const RestockTracker = (props) => { const { data } = useBackend(); - const vending_list = sortBy((vend) => vend.percentage)( + const vending_list = sortBy( data.vending_list ?? [], + (vend) => vend.percentage, ); return (
diff --git a/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx b/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx index 492321b3d3a..a00f4a3be14 100644 --- a/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx +++ b/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useState } from 'react'; import { useBackend, useLocalState } from 'tgui/backend'; import { @@ -29,10 +28,10 @@ export const SecurityRecordTabs = (props) => { const [search, setSearch] = useState(''); - const sorted: SecurityRecord[] = flow([ - filter((record: SecurityRecord) => isRecordMatch(record, search)), - sortBy((record: SecurityRecord) => record.name), - ])(records); + const sorted = sortBy( + filter(records, (record) => isRecordMatch(record, search)), + (record) => record.name, + ); return ( diff --git a/tgui/packages/tgui/interfaces/SeedExtractor.tsx b/tgui/packages/tgui/interfaces/SeedExtractor.tsx index 3c05b3d0498..87955da243f 100644 --- a/tgui/packages/tgui/interfaces/SeedExtractor.tsx +++ b/tgui/packages/tgui/interfaces/SeedExtractor.tsx @@ -1,5 +1,4 @@ import { sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { classes } from 'common/react'; import { createSearch } from 'common/string'; import { useState } from 'react'; @@ -67,9 +66,10 @@ export const SeedExtractor = (props) => { const search = createSearch(searchText, (item: SeedData) => item.name); const seeds_filtered = searchText.length > 0 ? data.seeds.filter(search) : data.seeds; - const seeds = flow([ - sortBy((item: SeedData) => item[sortField as keyof SeedData]), - ])(seeds_filtered || []); + const seeds = sortBy( + seeds_filtered || [], + (item: SeedData) => item[sortField as keyof SeedData], + ); sortField !== 'name' && seeds.reverse(); return ( diff --git a/tgui/packages/tgui/interfaces/SelectEquipment.jsx b/tgui/packages/tgui/interfaces/SelectEquipment.jsx index d43d28961e2..d8df6c71e7c 100644 --- a/tgui/packages/tgui/interfaces/SelectEquipment.jsx +++ b/tgui/packages/tgui/interfaces/SelectEquipment.jsx @@ -1,5 +1,4 @@ import { filter, map, sortBy, uniq } from 'common/collections'; -import { flow } from 'common/fp'; import { createSearch } from 'common/string'; import { useState } from 'react'; @@ -30,10 +29,10 @@ export const SelectEquipment = (props) => { const isFavorited = (entry) => favorites?.includes(entry.path); - const outfits = map((entry) => ({ + const outfits = map([...data.outfits, ...data.custom_outfits], (entry) => ({ ...entry, favorite: isFavorited(entry), - }))([...data.outfits, ...data.custom_outfits]); + })); // even if no custom outfits were sent, we still want to make sure there's // at least a 'Custom' tab so the button to create a new one pops up @@ -49,15 +48,15 @@ export const SelectEquipment = (props) => { (entry) => entry.name + entry.path, ); - const visibleOutfits = flow([ - filter((entry) => entry.category === tab), - filter(searchFilter), - sortBy( - (entry) => !entry.favorite, - (entry) => !entry.priority, - (entry) => entry.name, + const visibleOutfits = sortBy( + filter( + filter(outfits, (entry) => entry.category === tab), + searchFilter, ), - ])(outfits); + (entry) => !entry.favorite, + (entry) => !entry.priority, + (entry) => entry.name, + ); const getOutfitEntry = (current_outfit) => outfits.find((outfit) => getOutfitKey(outfit) === current_outfit); diff --git a/tgui/packages/tgui/interfaces/ShuttleManipulator.jsx b/tgui/packages/tgui/interfaces/ShuttleManipulator.jsx index e64f47086cd..8cff913e8e9 100644 --- a/tgui/packages/tgui/interfaces/ShuttleManipulator.jsx +++ b/tgui/packages/tgui/interfaces/ShuttleManipulator.jsx @@ -104,7 +104,7 @@ export const ShuttleManipulatorTemplates = (props) => { - {map((template, templateId) => ( + {map(templateObject, (template, templateId) => ( { > {template.port_id} - ))(templateObject)} + ))} diff --git a/tgui/packages/tgui/interfaces/StackCrafting.tsx b/tgui/packages/tgui/interfaces/StackCrafting.tsx index 255d8f501f1..7395306c11a 100644 --- a/tgui/packages/tgui/interfaces/StackCrafting.tsx +++ b/tgui/packages/tgui/interfaces/StackCrafting.tsx @@ -1,5 +1,3 @@ -import { filter, map, reduce, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { clamp } from 'common/math'; import { createSearch } from 'common/string'; import { useState } from 'react'; @@ -49,8 +47,7 @@ type RecipeBoxProps = { }; // RecipeList converted via Object.entries() for filterRecipeList -type RecipeListEntry = [string, RecipeList | Recipe]; -type RecipeListFilterableEntry = [string, RecipeList | Recipe | undefined]; +type RecipeListFilterableEntry = [string, RecipeList | Recipe]; /** * Type guard for recipe vs recipe list @@ -70,30 +67,29 @@ function isRecipeList(value: Recipe | RecipeList): value is RecipeList { const filterRecipeList = ( list: RecipeList, keyFilter: (key: string) => boolean, -) => { - const filteredList: RecipeList = flow([ - map((entry: RecipeListEntry): RecipeListFilterableEntry => { - const [key, recipe] = entry; +): RecipeList | undefined => { + const filteredList = Object.fromEntries( + Object.entries(list) + .flatMap((entry): RecipeListFilterableEntry[] => { + const [key, recipe] = entry; - if (isRecipeList(recipe)) { // If category name matches, return the whole thing. if (keyFilter(key)) { - return entry; + return [entry]; } - // otherwise, filter sub-entries. - return [key, filterRecipeList(recipe, keyFilter)]; - } + if (isRecipeList(recipe)) { + // otherwise, filter sub-entries. + const subEntries = filterRecipeList(recipe, keyFilter); + if (subEntries !== undefined) { + return [[key, subEntries]]; + } + } - return keyFilter(key) ? entry : [key, undefined]; - }), - filter((entry: RecipeListFilterableEntry) => entry[1] !== undefined), - sortBy((entry: RecipeListEntry) => entry[0].toLowerCase()), - reduce((obj: RecipeList, entry: RecipeListEntry) => { - obj[entry[0]] = entry[1]; - return obj; - }, {}), - ])(Object.entries(list)); + return []; + }) + .sort(([a], [b]) => (a < b ? -1 : a !== b ? 1 : 0)), + ); return Object.keys(filteredList).length ? filteredList : undefined; }; diff --git a/tgui/packages/tgui/interfaces/StationAlertConsole.jsx b/tgui/packages/tgui/interfaces/StationAlertConsole.jsx index f80a5826b06..c4ff6812955 100644 --- a/tgui/packages/tgui/interfaces/StationAlertConsole.jsx +++ b/tgui/packages/tgui/interfaces/StationAlertConsole.jsx @@ -1,5 +1,4 @@ import { sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useBackend } from '../backend'; import { Button, Section, Stack } from '../components'; @@ -30,8 +29,9 @@ export const StationAlertConsoleContent = (props) => { Camera: 5, }; - const sortedAlarms = flow([sortBy((alarm) => sortingKey[alarm.name])])( + const sortedAlarms = sortBy( data.alarms || [], + (alarm) => sortingKey[alarm.name], ); return ( diff --git a/tgui/packages/tgui/interfaces/StationTraitsPanel.tsx b/tgui/packages/tgui/interfaces/StationTraitsPanel.tsx index f3176415a4f..e6e7b02ce67 100644 --- a/tgui/packages/tgui/interfaces/StationTraitsPanel.tsx +++ b/tgui/packages/tgui/interfaces/StationTraitsPanel.tsx @@ -1,4 +1,4 @@ -import { filterMap } from 'common/collections'; +import { filter, map } from 'common/collections'; import { exhaustiveCheck } from 'common/exhaustive'; import { BooleanLike } from 'common/react'; import { useState } from 'react'; @@ -110,15 +110,9 @@ const FutureStationTraitsPage = (props) => { icon="times" onClick={() => { act('setup_future_traits', { - station_traits: filterMap( - future_station_traits, - (otherTrait) => { - if (otherTrait.path === trait.path) { - return undefined; - } else { - return otherTrait.path; - } - }, + station_traits: filter( + map(future_station_traits, (t) => t.path), + (p) => p !== trait.path, ), }); }} diff --git a/tgui/packages/tgui/interfaces/Supermatter.tsx b/tgui/packages/tgui/interfaces/Supermatter.tsx index 9bb8661ca29..35d176db6cd 100644 --- a/tgui/packages/tgui/interfaces/Supermatter.tsx +++ b/tgui/packages/tgui/interfaces/Supermatter.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { toFixed } from 'common/math'; import { BooleanLike } from 'common/react'; import { ReactNode, useState } from 'react'; @@ -123,10 +122,14 @@ export const SupermatterContent = (props: SupermatterProps) => { gas_metadata, } = props; const [allGasActive, setAllGasActive] = useState(false); - const gas_composition: [gas_path: string, amount: number][] = flow([ - !allGasActive && filter(([gas_path, amount]) => amount !== 0), - sortBy(([gas_path, amount]) => -amount), - ])(Object.entries(props.gas_composition)); + let gas_composition = Object.entries(props.gas_composition); + if (!allGasActive) { + gas_composition = filter( + gas_composition, + ([gas_path, amount]) => amount !== 0, + ); + } + gas_composition = sortBy(gas_composition, ([gas_path, amount]) => -amount); return ( diff --git a/tgui/packages/tgui/interfaces/SurgeryInitiator.tsx b/tgui/packages/tgui/interfaces/SurgeryInitiator.tsx index f48915068d9..62d71d614db 100644 --- a/tgui/packages/tgui/interfaces/SurgeryInitiator.tsx +++ b/tgui/packages/tgui/interfaces/SurgeryInitiator.tsx @@ -19,7 +19,8 @@ type SurgeryInitiatorData = { target_name: string; }; -const sortSurgeries = sortBy((surgery: Surgery) => surgery.name); +const sortSurgeries = (array: Surgery[]) => + sortBy(array, (surgery) => surgery.name); type SurgeryInitiatorInnerState = { selectedSurgeryIndex: number; diff --git a/tgui/packages/tgui/interfaces/Techweb.jsx b/tgui/packages/tgui/interfaces/Techweb.jsx index 96b52983ade..4328cc01b89 100644 --- a/tgui/packages/tgui/interfaces/Techweb.jsx +++ b/tgui/packages/tgui/interfaces/Techweb.jsx @@ -1,4 +1,4 @@ -import { map, sortBy } from 'common/collections'; +import { sortBy } from 'common/collections'; import { useState } from 'react'; import { useBackend, useLocalState } from '../backend'; @@ -41,9 +41,9 @@ const selectRemappedStaticData = (data) => { ...node, id: remapId(id), costs, - prereq_ids: map(remapId)(node.prereq_ids || []), - design_ids: map(remapId)(node.design_ids || []), - unlock_ids: map(remapId)(node.unlock_ids || []), + prereq_ids: map(node.prereq_ids || [], remapId), + design_ids: map(node.design_ids || [], remapId), + unlock_ids: map(node.unlock_ids || [], remapId), required_experiments: node.required_experiments || [], discount_experiments: node.discount_experiments || [], }; @@ -251,10 +251,11 @@ const TechwebOverview = (props) => { ); }); } else { - displayedNodes = sortBy((x) => node_cache[x.id].name)( + displayedNodes = sortBy( tabIndex < 2 ? nodes.filter((x) => x.tier === tabIndex) : nodes.filter((x) => x.tier >= tabIndex), + (x) => node_cache[x.id].name, ); } diff --git a/tgui/packages/tgui/interfaces/TrackedPlaytime.jsx b/tgui/packages/tgui/interfaces/TrackedPlaytime.jsx index 3f1a3dcb543..f59b01b8b5d 100644 --- a/tgui/packages/tgui/interfaces/TrackedPlaytime.jsx +++ b/tgui/packages/tgui/interfaces/TrackedPlaytime.jsx @@ -7,7 +7,7 @@ import { Window } from '../layouts'; const JOB_REPORT_MENU_FAIL_REASON_TRACKING_DISABLED = 1; const JOB_REPORT_MENU_FAIL_REASON_NO_RECORDS = 2; -const sortByPlaytime = sortBy(([_, playtime]) => -playtime); +const sortByPlaytime = (array) => sortBy(array, ([_, playtime]) => -playtime); const PlaytimeSection = (props) => { const { playtimes } = props; diff --git a/tgui/packages/tgui/interfaces/WarrantConsole.tsx b/tgui/packages/tgui/interfaces/WarrantConsole.tsx index 14415b3ee79..a87de8e017c 100644 --- a/tgui/packages/tgui/interfaces/WarrantConsole.tsx +++ b/tgui/packages/tgui/interfaces/WarrantConsole.tsx @@ -63,7 +63,7 @@ export const WarrantConsole = (props) => { const RecordList = (props) => { const { act, data } = useBackend(); const { records = [] } = data; - const sorted = sortBy((record: WarrantRecord) => record.crew_name)(records); + const sorted = sortBy(records, (record) => record.crew_name); const [selectedRecord, setSelectedRecord] = useLocalState< WarrantRecord | undefined diff --git a/tgui/packages/tgui/interfaces/common/AccessConfig.tsx b/tgui/packages/tgui/interfaces/common/AccessConfig.tsx index 99f783dbdbc..766a6724d77 100644 --- a/tgui/packages/tgui/interfaces/common/AccessConfig.tsx +++ b/tgui/packages/tgui/interfaces/common/AccessConfig.tsx @@ -72,8 +72,9 @@ export function AccessConfig(props: ConfigProps) { accesses.find((access) => access.name === selectedAccessName) || accesses[0]; - const selectedAccessEntries = sortBy((entry: Area) => entry.desc)( + const selectedAccessEntries = sortBy( selectedAccess?.accesses || [], + (entry: Area) => entry.desc, ); function checkAccessIcon(accesses: Area[]) { diff --git a/tgui/packages/tgui/interfaces/common/AccessList.jsx b/tgui/packages/tgui/interfaces/common/AccessList.jsx index a4096b6ce30..9ac1fe913b3 100644 --- a/tgui/packages/tgui/interfaces/common/AccessList.jsx +++ b/tgui/packages/tgui/interfaces/common/AccessList.jsx @@ -248,8 +248,9 @@ const RegionAccessList = (props) => { const selectedAccess = accesses.find( (access) => access.name === selectedAccessName, ); - const selectedAccessEntries = sortBy((entry) => entry.desc)( + const selectedAccessEntries = sortBy( selectedAccess?.accesses || [], + (entry) => entry.desc, ); const allWildcards = Object.keys(wildcardSlots);