[MIRROR] Fixes radar issues [MDB IGNORE] (#25785)

* Fixes radar issues (#80453)

## About The Pull Request
Went through tgui:sonar to search for bugs. Some of these are valid -
they've been addressed. Others, not so. Telling me I need to make icon
names and css classes into constants is a nit at best. Radar seems to be
bugged in certain areas: Classnames ok to dupe, but not ones with
conditionals. Most of the function duplication errors rely on hooks.

I think this should close #79815 and we should remove radar shortly
after. See my comment
[here](https://github.com/tgstation/tgstation/issues/79815#issuecomment-1859286784)
## Why It's Good For The Game
Fixes #79815
## Changelog
N/A none of this was player facing

* Fixes radar issues

---------

Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com>
This commit is contained in:
SkyratBot
2023-12-22 22:02:38 -05:00
committed by GitHub
co-authored by Jeremiah
parent 60df240a94
commit 63a443ed30
18 changed files with 125 additions and 174 deletions
+1 -1
View File
@@ -95,7 +95,7 @@ export const applyMiddleware = (
return (reducer, ...args): Store => {
const store = createStoreFunction(reducer, ...args);
let dispatch: Dispatch = () => {
let dispatch: Dispatch = (action, ...args) => {
throw new Error(
'Dispatching while constructing your middleware is not allowed.',
);
+31 -33
View File
@@ -31,11 +31,9 @@ const ensureConnection = () => {
};
}
}
};
if (process.env.NODE_ENV !== 'production') {
window.onunload = () => socket && socket.close();
}
};
const subscribe = (fn) => subscribers.push(fn);
@@ -136,38 +134,38 @@ const sendLogEntry = (level, ns, ...args) => {
const setupHotReloading = () => {
if (
// prettier-ignore
process.env.NODE_ENV !== 'production'
&& process.env.WEBPACK_HMR_ENABLED
&& window.WebSocket
process.env.NODE_ENV === 'production' ||
!process.env.WEBPACK_HMR_ENABLED ||
!window.WebSocket
) {
if (module.hot) {
ensureConnection();
sendLogEntry(0, null, 'setting up hot reloading');
subscribe((msg) => {
const { type } = msg;
sendLogEntry(0, null, 'received', type);
if (type === 'hotUpdate') {
const status = module.hot.status();
if (status !== 'idle') {
sendLogEntry(0, null, 'hot reload status:', status);
return;
}
module.hot
.check({
ignoreUnaccepted: true,
ignoreDeclined: true,
ignoreErrored: true,
})
.then((modules) => {
sendLogEntry(0, null, 'outdated modules', modules);
})
.catch((err) => {
sendLogEntry(0, null, 'reload error', err);
});
return;
}
if (module.hot) {
ensureConnection();
sendLogEntry(0, null, 'setting up hot reloading');
subscribe((msg) => {
const { type } = msg;
sendLogEntry(0, null, 'received', type);
if (type === 'hotUpdate') {
const status = module.hot.status();
if (status !== 'idle') {
sendLogEntry(0, null, 'hot reload status:', status);
return;
}
});
}
module.hot
.check({
ignoreUnaccepted: true,
ignoreDeclined: true,
ignoreErrored: true,
})
.then((modules) => {
sendLogEntry(0, null, 'outdated modules', modules);
})
.catch((err) => {
sendLogEntry(0, null, 'reload error', err);
});
}
});
}
};
+2 -2
View File
@@ -36,8 +36,8 @@ export const regQuery = async (path, key) => {
logger.error('could not find the start of the key value');
return null;
}
const value = stdout.substring(indexOfValue + 4, indexOfEol);
return value;
return stdout.substring(indexOfValue + 4, indexOfEol);
} catch (err) {
logger.error(err);
return null;
-1
View File
@@ -125,7 +125,6 @@ export const Button = (props: Props) => {
// Refocus layout on pressing escape.
if (event.key === KEY.Escape) {
event.preventDefault();
return;
}
}}
{...computeBoxProps(rest)}
-2
View File
@@ -70,8 +70,6 @@ export const Input = (props: Props) => {
event.currentTarget.value = toInputValue(value);
event.currentTarget.blur();
return;
}
};
-1
View File
@@ -122,7 +122,6 @@ window.addEventListener('focusin', (e) => {
setWindowFocus(true);
if (canStealFocus(e.target as HTMLElement)) {
stealFocus(e.target as HTMLElement);
return;
}
});
@@ -40,7 +40,7 @@ export const BasketballPanel = (props) => {
/>
<Button
icon="basketball"
disabled={!(data.total_votes >= data.players_min)}
disabled={data.total_votes < data.players_min}
onClick={() => act('basketball_start')}
>
Start
@@ -50,8 +50,7 @@ export const CellularEmporium = (props) => {
buttons={
<Stack>
<Stack.Item fontSize="16px">
{genetic_points_count && genetic_points_count}{' '}
<Icon name="dna" color="#DD66DD" />
{genetic_points_count} <Icon name="dna" color="#DD66DD" />
</Stack.Item>
<Stack.Item>
<Button
@@ -56,17 +56,19 @@ const CentComName = (props) => {
const { act, data } = useBackend<Data>();
const { command_name, command_name_presets = [], custom_name } = data;
const sendName = (value) => {
act('update_command_name', {
updated_name: value,
});
};
return (
<Section title="Set Central Command name" textAlign="center">
<Dropdown
width="100%"
selected={command_name}
options={command_name_presets}
onSelected={(value) =>
act('update_command_name', {
updated_name: value,
})
}
onSelected={(value) => sendName(value)}
/>
{!!custom_name && (
<Input
@@ -74,11 +76,7 @@ const CentComName = (props) => {
mt={1}
value={command_name}
placeholder={command_name}
onChange={(_, value) =>
act('update_command_name', {
updated_name: value,
})
}
onChange={(_, value) => sendName(value)}
/>
)}
</Section>
@@ -199,6 +199,10 @@ export const DesignBrowser = <T extends Design = Design>(
}
}
const designWrapper = (design: T) => {
buildRecipeElement(design, availableMaterials || {}, onPrintDesign || NOOP);
};
return (
<Stack fill>
{/* Left Column */}
@@ -279,24 +283,12 @@ export const DesignBrowser = <T extends Design = Design>(
.toLowerCase()
.includes(searchText.toLowerCase()),
)
.map((design) =>
buildRecipeElement(
design,
availableMaterials || {},
onPrintDesign || NOOP,
),
)
.map((design) => designWrapper(design))
) : selectedCategory === ALL_CATEGORY ? (
<>
{sortBy((design: T) => design.name)(
Object.values(root.descendants),
).map((design) =>
buildRecipeElement(
design,
availableMaterials || {},
onPrintDesign || NOOP,
),
)}
).map((design) => designWrapper(design))}
</>
) : (
root.subcategories[selectedCategory] && (
@@ -22,18 +22,14 @@ export const NumberInputModal = (props) => {
const { act, data } = useBackend<NumberInputData>();
const { init_value, large_buttons, message = '', timeout, title } = data;
const [input, setInput] = useState(init_value);
const onChange = (value: number) => {
if (value === input) {
return;
}
setInput(value);
};
const onClick = (value: number) => {
const setValue = (value: number) => {
if (value === input) {
return;
}
setInput(value);
};
// Dynamically changes the window height based on the message.
const windowHeight =
140 +
@@ -59,7 +55,7 @@ export const NumberInputModal = (props) => {
<Box color="label">{message}</Box>
</Stack.Item>
<Stack.Item>
<InputArea input={input} onClick={onClick} onChange={onChange} />
<InputArea input={input} onClick={setValue} onChange={setValue} />
</Stack.Item>
<Stack.Item>
<InputButtons input={input} />
+3 -10
View File
@@ -18,11 +18,7 @@ export const getAntagCategories = (antagonists: Antagonist[]) => {
categories[antag_group].push(player);
});
const sortedAntagonists = sortBy<AntagGroup>(([key]) => key)(
Object.entries(categories),
);
return sortedAntagonists;
return sortBy<AntagGroup>(([key]) => key)(Object.entries(categories));
};
/** Returns a disguised name in case the person is wearing someone else's ID */
@@ -46,9 +42,8 @@ export const getDisplayName = (full_name: string, name?: string) => {
export const getMostRelevant = (
searchQuery: string,
observables: Observable[][],
) => {
/** Returns the most orbited observable that matches the search. */
const mostRelevant: Observable = flow([
): Observable => {
return flow([
// Filters out anything that doesn't match search
filter<Observable>((observable) =>
isJobOrNameMatch(observable, searchQuery),
@@ -57,8 +52,6 @@ export const getMostRelevant = (
sortBy<Observable>((observable) => -(observable.orbiters || 0)),
// Makes a single Observables list for an easy search
])(observables.flat())[0];
return mostRelevant;
};
/** Returns the display color for certain health percentages */
+32 -52
View File
@@ -411,6 +411,34 @@ export class PrimaryView extends Component {
}
}
const tokenizer = (src: string) => {
const rule = /^\[_+\]/;
const match = src.match(rule);
if (match) {
return {
type: 'inputField',
raw: match[0],
};
}
};
// Override function, any links and images should
// kill any other marked tokens we don't want here
const walkTokens = (token) => {
switch (token.type) {
case 'url':
case 'autolink':
case 'reflink':
case 'link':
case 'image':
token.type = 'text';
// Once asset system is up change to some default image
// or rewrite for icon images
token.href = '';
break;
}
};
/**
* Real-time text preview section. When not editing, this is simply
* the component that builds and renders the final HTML output.
@@ -458,38 +486,13 @@ export class PreviewView extends Component<PreviewViewProps> {
return src.match(/\[/)?.index;
},
tokenizer(src: string) {
const rule = /^\[_+\]/;
const match = src.match(rule);
if (match) {
const token = {
type: 'inputField',
raw: match[0],
};
return token;
}
},
tokenizer,
renderer(token) {
return `${token.raw}`;
},
};
// Override function, any links and images should
// kill any other marked tokens we don't want here
const walkTokens = (token) => {
switch (token.type) {
case 'url':
case 'autolink':
case 'reflink':
case 'link':
case 'image':
token.type = 'text';
// Once asset system is up change to some default image
// or rewrite for icon images
token.href = '';
break;
}
walkTokens,
};
marked.use({
@@ -679,20 +682,7 @@ export class PreviewView extends Component<PreviewViewProps> {
runMarkedDefault = (rawText: string): string => {
// Override function, any links and images should
// kill any other marked tokens we don't want here
const walkTokens = (token) => {
switch (token.type) {
case 'url':
case 'autolink':
case 'reflink':
case 'link':
case 'image':
token.type = 'text';
// Once asset system is up change to some default image
// or rewrite for icon images
token.href = '';
break;
}
};
walkTokens;
// This is an extension for marked defining a complete custom tokenizer.
// This tokenizer should run before the the non-custom ones, and gives us
@@ -709,17 +699,7 @@ export class PreviewView extends Component<PreviewViewProps> {
return src.match(/\[/)?.index;
},
tokenizer(src: string) {
const rule = /^\[_+\]/;
const match = src.match(rule);
if (match) {
const token = {
type: 'inputField',
raw: match[0],
};
return token;
}
},
tokenizer,
renderer(token) {
return `${token.raw}`;
@@ -129,10 +129,9 @@ const textWidth = (text, font, fontsize) => {
// default font height is 12 in tgui
font = fontsize + 'x ' + font;
const c = document.createElement('canvas');
const ctx = c.getContext('2d') as any;
const ctx = c.getContext('2d') as CanvasRenderingContext2D;
ctx.font = font;
const width = ctx.measureText(text).width;
return width;
return ctx.measureText(text).width;
};
const planeToPosition = function (plane: Plane, index, is_incoming): Position {
@@ -673,6 +672,13 @@ const PlaneWindow = (props) => {
const doc_html = {
__html: workingPlane.documentation,
};
const setAlpha = (event, value) =>
act('set_alpha', {
edit: workingPlane.our_ref,
alpha: value,
});
return (
<Section
top="27px"
@@ -776,18 +782,8 @@ const PlaneWindow = (props) => {
maxValue={255}
step={1}
stepPixelSize={1.9}
onDrag={(e, value) =>
act('set_alpha', {
edit: workingPlane.our_ref,
alpha: value,
})
}
onChange={(e, value) =>
act('set_alpha', {
edit: workingPlane.our_ref,
alpha: value,
})
}
onDrag={setAlpha}
onChange={setAlpha}
>
Alpha ({workingPlane.alpha})
</Slider>
@@ -85,20 +85,17 @@ const ImplantDisplay = (props: { implant: ImplantInfo }) => {
};
// When given a list of implants, sorts them by category
const sortImplants = (implants: ImplantInfo[]) => {
const implantsByCategory: Record<string, ImplantInfo[]> = implants.reduce(
(acc, implant) => {
if (implant.category in acc) {
acc[implant.category].push(implant);
} else {
acc[implant.category] = [implant];
}
return acc;
},
{},
);
return implantsByCategory;
const sortImplants = (
implants: ImplantInfo[],
): Record<string, ImplantInfo[]> => {
return implants.reduce((acc, implant) => {
if (implant.category in acc) {
acc[implant.category].push(implant);
} else {
acc[implant.category] = [implant];
}
return acc;
}, {});
};
// Converts a category ("tracking implant") to a more readable format ("Tracking")
+1 -1
View File
@@ -109,7 +109,7 @@ export const NtosWindow = (props) => {
className="NtosHeader__icon"
src={resolveAsset(PC_batteryicon)}
/>
{PC_batterypercent && PC_batterypercent}
{PC_batterypercent}
</Box>
)}
{!!PC_showexitprogram && (
+12 -7
View File
@@ -23,9 +23,14 @@ type CreateRenderer = <T extends unknown[] = [unknown]>(
getVNode?: (...args: T) => any,
) => (...args: T) => void;
enum Render {
Start = 'render/start',
Finish = 'render/finish',
}
// prettier-ignore
export const createRenderer: CreateRenderer = (getVNode) => (...args) => {
perf.mark('render/start');
perf.mark(Render.Start);
// Start rendering
if (!reactRoot) {
reactRoot = document.getElementById('react-root');
@@ -36,7 +41,7 @@ export const createRenderer: CreateRenderer = (getVNode) => (...args) => {
else {
render(args[0] as any, reactRoot);
}
perf.mark('render/finish');
perf.mark(Render.Finish);
if (suspended) {
return;
}
@@ -44,22 +49,22 @@ export const createRenderer: CreateRenderer = (getVNode) => (...args) => {
if (process.env.NODE_ENV !== 'production') {
if (initialRender === 'resumed') {
logger.log('rendered in',
perf.measure('render/start', 'render/finish'));
perf.measure(Render.Start, Render.Finish));
}
else if (initialRender) {
logger.debug('serving from:', location.href);
logger.debug('bundle entered in',
perf.measure('inception', 'init'));
logger.debug('initialized in',
perf.measure('init', 'render/start'));
perf.measure('init', Render.Start));
logger.log('rendered in',
perf.measure('render/start', 'render/finish'));
perf.measure(Render.Start, Render.Finish));
logger.log('fully loaded in',
perf.measure('inception', 'render/finish'));
perf.measure('inception', Render.Finish));
}
else {
logger.debug('rendered in',
perf.measure('render/start', 'render/finish'));
perf.measure(Render.Start, Render.Finish));
}
}
if (initialRender) {
+1
View File
@@ -65,6 +65,7 @@ export const getRoutedComponent = () => {
return require('./debug').KitchenSink;
}
}
const name = config?.interface;
const interfacePathBuilders = [
(name: string) => `./${name}.tsx`,