mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-26 06:29:23 +01:00
[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:
@@ -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,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);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -125,7 +125,6 @@ export const Button = (props: Props) => {
|
||||
// Refocus layout on pressing escape.
|
||||
if (event.key === KEY.Escape) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
}}
|
||||
{...computeBoxProps(rest)}
|
||||
|
||||
@@ -70,8 +70,6 @@ export const Input = (props: Props) => {
|
||||
|
||||
event.currentTarget.value = toInputValue(value);
|
||||
event.currentTarget.blur();
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -109,7 +109,7 @@ export const NtosWindow = (props) => {
|
||||
className="NtosHeader__icon"
|
||||
src={resolveAsset(PC_batteryicon)}
|
||||
/>
|
||||
{PC_batterypercent && PC_batterypercent}
|
||||
{PC_batterypercent}
|
||||
</Box>
|
||||
)}
|
||||
{!!PC_showexitprogram && (
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -65,6 +65,7 @@ export const getRoutedComponent = () => {
|
||||
return require('./debug').KitchenSink;
|
||||
}
|
||||
}
|
||||
|
||||
const name = config?.interface;
|
||||
const interfacePathBuilders = [
|
||||
(name: string) => `./${name}.tsx`,
|
||||
|
||||
Reference in New Issue
Block a user