From d878c8dd647810b3be754e7e0ae7937c28a66a86 Mon Sep 17 00:00:00 2001 From: paulrpg Date: Mon, 27 May 2024 18:22:19 +0100 Subject: [PATCH 1/4] first pass of orbit rework --- tgui/packages/tgui/interfaces/Orbit/index.tsx | 254 +++++++++--------- 1 file changed, 133 insertions(+), 121 deletions(-) diff --git a/tgui/packages/tgui/interfaces/Orbit/index.tsx b/tgui/packages/tgui/interfaces/Orbit/index.tsx index 76a2ce874497..ffe0d31d8112 100644 --- a/tgui/packages/tgui/interfaces/Orbit/index.tsx +++ b/tgui/packages/tgui/interfaces/Orbit/index.tsx @@ -1,8 +1,8 @@ -import { filter, sortBy } from 'common/collections'; import { capitalizeFirst } from 'common/string'; -import { useState } from 'react'; +import { createContext, useContext, useState } from 'react'; import { useBackend } from 'tgui/backend'; import { + Box, Button, Collapsible, ColorBox, @@ -23,37 +23,41 @@ import { } from './helpers'; import type { Observable, OrbitData } from './types'; -export const Orbit = (props) => { +type search = { + value: string; + setValue: (value: string) => void; +}; + +const SearchContext = createContext({ value: '', setValue: () => {} }); + +export const Orbit = () => { const [searchQuery, setSearchQuery] = useState(''); return ( - - - - - -
- -
-
-
+ + + + + + +
+ +
+
+
+
); }; /** Controls filtering out the list of observables via search */ -const ObservableSearch = (props: { - readonly searchQuery: string; - readonly setSearchQuery: React.Dispatch>; -}) => { +const ObservableSearch = () => { const { act, data } = useBackend(); - const { searchQuery, setSearchQuery } = props; const { humans = [], marines = [], survivors = [], xenos = [] } = data; let auto_observe = data.auto_observe; @@ -74,6 +78,8 @@ const ObservableSearch = (props: { } }; + const { value, setValue } = useContext(SearchContext); + return (
@@ -85,9 +91,9 @@ const ObservableSearch = (props: { autoFocus fluid onEnter={(event, value) => orbitMostRelevant(value)} - onInput={(event, value) => setSearchQuery(value)} + onInput={(event, value) => setValue(value)} placeholder="Search..." - value={searchQuery} + value={value} /> @@ -114,14 +120,92 @@ const ObservableSearch = (props: { ); }; +const MarineObservable = (props: { + readonly color?: string; + readonly section: Array; + readonly title: string; +}) => { + const { color, section = [], title } = props; + + const { value: searchQuery } = useContext(SearchContext); + + if (!section.length) { + return null; + } + + const filteredSection = section + .filter((observable) => isJobOrNameMatch(observable, searchQuery)) + .sort((a, b) => + a.full_name + .toLocaleLowerCase() + .localeCompare(b.full_name.toLocaleLowerCase()), + ); + + if (!filteredSection.length) { + return null; + } + + const alphaSquad: Array = []; + const bravoSquad: Array = []; + const charlieSquad: Array = []; + const deltaSquad: Array = []; + const foxtrotSquad: Array = []; + const other: Array = []; + + section.forEach((x) => { + if (x.job?.includes('Alpha')) { + alphaSquad.push(x); + } else if (x.job?.includes('Bravo')) { + bravoSquad.push(x); + } else if (x.job?.includes('Charlie')) { + charlieSquad.push(x); + } else if (x.job?.includes('Delta')) { + deltaSquad.push(x); + } else { + other.push(x); + } + }); + + return ( + + + + + + + + + + + + + ); +}; + /** * The primary content display for points of interest. * Renders a scrollable section replete with subsections for each * observable group. */ -const ObservableContent = (props: { readonly searchQuery: string }) => { +const ObservableContent = () => { const { data } = useBackend(); - const { searchQuery } = props; const { humans = [], marines = [], @@ -150,138 +234,65 @@ const ObservableContent = (props: { readonly searchQuery: string }) => { return ( + + + + - - - - + - + - - - - - - - - - + + + + + + + + ); }; @@ -294,21 +305,22 @@ const ObservableSection = (props: { readonly color?: string; readonly section: Array; readonly title: string; - readonly searchQuery: string; }) => { - const { color, section = [], title, searchQuery } = props; + const { color, section = [], title } = props; + + const { value: searchQuery } = useContext(SearchContext); if (!section.length) { return null; } - const filteredSection = sortBy( - filter(section, (observable) => isJobOrNameMatch(observable, searchQuery)), - (observable) => - getDisplayName(observable.full_name, observable.nickname) - .replace(/^"/, '') - .toLowerCase(), - ); + const filteredSection = section + .filter((observable) => isJobOrNameMatch(observable, searchQuery)) + .sort((a, b) => + a.full_name + .toLocaleLowerCase() + .localeCompare(b.full_name.toLocaleLowerCase()), + ); if (!filteredSection.length) { return null; From 1615d29698c2b351dee5146b6aabeed0ce3fc58b Mon Sep 17 00:00:00 2001 From: paulrpg Date: Mon, 27 May 2024 19:06:40 +0100 Subject: [PATCH 2/4] build squad list off filtered users --- tgui/packages/tgui/interfaces/Orbit/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tgui/packages/tgui/interfaces/Orbit/index.tsx b/tgui/packages/tgui/interfaces/Orbit/index.tsx index ffe0d31d8112..0ffe416b3631 100644 --- a/tgui/packages/tgui/interfaces/Orbit/index.tsx +++ b/tgui/packages/tgui/interfaces/Orbit/index.tsx @@ -152,7 +152,7 @@ const MarineObservable = (props: { const foxtrotSquad: Array = []; const other: Array = []; - section.forEach((x) => { + filteredSection.forEach((x) => { if (x.job?.includes('Alpha')) { alphaSquad.push(x); } else if (x.job?.includes('Bravo')) { From 74545e8e5d8c2a978522a1fe0b38ee0351571f3c Mon Sep 17 00:00:00 2001 From: PaulMullen Date: Mon, 27 May 2024 23:52:55 +0100 Subject: [PATCH 3/4] refactor and added xenos --- tgui/packages/tgui/interfaces/Orbit/index.tsx | 124 ++++++++++++------ tgui/packages/tgui/interfaces/Orbit/types.ts | 18 +++ 2 files changed, 100 insertions(+), 42 deletions(-) diff --git a/tgui/packages/tgui/interfaces/Orbit/index.tsx b/tgui/packages/tgui/interfaces/Orbit/index.tsx index 0ffe416b3631..00dcfcd7a1c2 100644 --- a/tgui/packages/tgui/interfaces/Orbit/index.tsx +++ b/tgui/packages/tgui/interfaces/Orbit/index.tsx @@ -21,7 +21,12 @@ import { getMostRelevant, isJobOrNameMatch, } from './helpers'; -import type { Observable, OrbitData } from './types'; +import { + buildSquadObservable, + type Observable, + type OrbitData, + SquadObservable, +} from './types'; type search = { value: string; @@ -120,10 +125,64 @@ const ObservableSearch = () => { ); }; -const MarineObservable = (props: { +const xenoSplitter = (members: Array) => { + const primeHive: Array = []; + const corruptedHive: Array = []; + + members.forEach((x) => { + if (x.full_name?.includes('Corrupted')) { + corruptedHive.push(x); + } else { + primeHive.push(x); + } + }); + const squads = [ + buildSquadObservable('Prime', 'xeno', primeHive), + buildSquadObservable('Corrupted', 'green', corruptedHive), + ]; + return squads; +}; + +const marineSplitter = (members: Array) => { + const alphaSquad: Array = []; + const bravoSquad: Array = []; + const charlieSquad: Array = []; + const deltaSquad: Array = []; + const foxtrotSquad: Array = []; + const other: Array = []; + + members.forEach((x) => { + if (x.job?.includes('Alpha')) { + alphaSquad.push(x); + } else if (x.job?.includes('Bravo')) { + bravoSquad.push(x); + } else if (x.job?.includes('Charlie')) { + charlieSquad.push(x); + } else if (x.job?.includes('Delta')) { + deltaSquad.push(x); + } else if (x.job?.includes('Foxtrot')) { + foxtrotSquad.push(x); + } else { + other.push(x); + } + }); + + const squads = [ + buildSquadObservable('Alpha', 'red', alphaSquad), + buildSquadObservable('Bravo', 'yellow', bravoSquad), + buildSquadObservable('Charlie', 'purple', charlieSquad), + buildSquadObservable('Delta', 'blue', deltaSquad), + buildSquadObservable('Foxtrot', 'teal', foxtrotSquad), + buildSquadObservable('Other', 'grey', other), + ]; + return squads; +}; + +const GroupedObservable = (props: { readonly color?: string; readonly section: Array; readonly title: string; + readonly splitter: (members: Array) => Array; }) => { const { color, section = [], title } = props; @@ -145,26 +204,7 @@ const MarineObservable = (props: { return null; } - const alphaSquad: Array = []; - const bravoSquad: Array = []; - const charlieSquad: Array = []; - const deltaSquad: Array = []; - const foxtrotSquad: Array = []; - const other: Array = []; - - filteredSection.forEach((x) => { - if (x.job?.includes('Alpha')) { - alphaSquad.push(x); - } else if (x.job?.includes('Bravo')) { - bravoSquad.push(x); - } else if (x.job?.includes('Charlie')) { - charlieSquad.push(x); - } else if (x.job?.includes('Delta')) { - deltaSquad.push(x); - } else { - other.push(x); - } - }); + const squads = props.splitter(filteredSection); return ( @@ -175,24 +215,14 @@ const MarineObservable = (props: { title={title + ` - (${filteredSection.length})`} > - - - - - - + {squads.map((x) => ( + + ))} @@ -234,9 +264,19 @@ const ObservableContent = () => { return ( - + - + ; + color: string; + title: string; +}; + +export const buildSquadObservable: ( + title: string, + color: string, + members: Array, +) => SquadObservable = (title, color, members = []) => { + return { + members: members, + color: color, + title: title, + }; +}; From 49bd6008930a9d997406d5d6ce63db9bd99830cf Mon Sep 17 00:00:00 2001 From: PaulMullen Date: Tue, 28 May 2024 08:10:39 +0100 Subject: [PATCH 4/4] added rank sorting for marines --- tgui/packages/tgui/interfaces/Orbit/index.tsx | 33 ++++++++++++++++--- tgui/packages/tgui/interfaces/Orbit/types.ts | 3 ++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/tgui/packages/tgui/interfaces/Orbit/index.tsx b/tgui/packages/tgui/interfaces/Orbit/index.tsx index 00dcfcd7a1c2..bc440e733939 100644 --- a/tgui/packages/tgui/interfaces/Orbit/index.tsx +++ b/tgui/packages/tgui/interfaces/Orbit/index.tsx @@ -23,9 +23,10 @@ import { } from './helpers'; import { buildSquadObservable, + groupSorter, type Observable, type OrbitData, - SquadObservable, + splitter, } from './types'; type search = { @@ -107,7 +108,7 @@ const ObservableSearch = () => { color={auto_observe ? 'good' : 'transparent'} icon={auto_observe ? 'toggle-on' : 'toggle-off'} onClick={() => act('toggle_auto_observe')} - tooltip={`Toggle Full Observe. When active, you'll see the UI / full inventory of whoever you're orbiting. Neat!`} + tooltip={`Toggle Full Observe. When active, you'll see the UI / full inventory of whoever you're orbiting.`} tooltipPosition="bottom-start" /> @@ -178,11 +179,31 @@ const marineSplitter = (members: Array) => { return squads; }; +const rankList = [ + 'Rifleman', + 'Spotter', + 'Hospital Corpsman', + 'Combat Technician', + 'Smartgunner', + 'Weapons Specialist', + 'Fireteam Leader', + 'Squad Leader', +]; +const marineSort = (a: Observable, b: Observable) => { + const a_index = rankList.findIndex((str) => a.job?.includes(str)) ?? 0; + const b_index = rankList.findIndex((str) => b.job?.includes(str)) ?? 0; + if (a_index === b_index) { + return a.full_name.localeCompare(b.full_name); + } + return a_index > b_index ? -1 : 1; +}; + const GroupedObservable = (props: { readonly color?: string; readonly section: Array; readonly title: string; - readonly splitter: (members: Array) => Array; + readonly splitter: splitter; + readonly sorter?: groupSorter; }) => { const { color, section = [], title } = props; @@ -219,7 +240,7 @@ const GroupedObservable = (props: { ))} @@ -269,6 +290,7 @@ const ObservableContent = () => { section={marines} title="Marines" splitter={marineSplitter} + sorter={marineSort} /> {displayHealth && } + {!!icon && ( + + )} {capitalizeFirst(getDisplayName(full_name, nickname))} {!!orbiters && ( <> diff --git a/tgui/packages/tgui/interfaces/Orbit/types.ts b/tgui/packages/tgui/interfaces/Orbit/types.ts index 5dab18dfb30a..8318a91f1c89 100644 --- a/tgui/packages/tgui/interfaces/Orbit/types.ts +++ b/tgui/packages/tgui/interfaces/Orbit/types.ts @@ -57,3 +57,6 @@ export const buildSquadObservable: ( title: title, }; }; + +export type splitter = (members: Array) => Array; +export type groupSorter = (a: Observable, b: Observable) => number;