mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 09:42:29 +00:00
Enable strictNullChecks on tgui (#60961)
* Enable strictNullChecks * ArgumentType -> ArgumentsOf * Fix map signature
This commit is contained in:
@@ -668,7 +668,7 @@ rules:
|
||||
# react/sort-prop-types: error
|
||||
## Enforce the state initialization style to be either in a constructor or
|
||||
## with a class property
|
||||
react/state-in-constructor: error
|
||||
# react/state-in-constructor: error
|
||||
## Enforces where React component static properties should be positioned.
|
||||
# react/static-property-placement: error
|
||||
## Enforce style prop value being an object
|
||||
|
||||
@@ -4,64 +4,6 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts a given collection to an array.
|
||||
*
|
||||
* - Arrays are returned unmodified;
|
||||
* - If object was provided, keys will be discarded;
|
||||
* - Everything else will result in an empty array.
|
||||
*
|
||||
* @returns {any[]}
|
||||
*/
|
||||
export const toArray = collection => {
|
||||
if (Array.isArray(collection)) {
|
||||
return collection;
|
||||
}
|
||||
if (typeof collection === 'object') {
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
const result = [];
|
||||
for (let i in collection) {
|
||||
if (hasOwnProperty.call(collection, i)) {
|
||||
result.push(collection[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a given object to an array, and appends a key to every
|
||||
* object inside of that array.
|
||||
*
|
||||
* Example input (object):
|
||||
* ```
|
||||
* {
|
||||
* 'Foo': { info: 'Hello world!' },
|
||||
* 'Bar': { info: 'Hello world!' },
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Example output (array):
|
||||
* ```
|
||||
* [
|
||||
* { key: 'Foo', info: 'Hello world!' },
|
||||
* { key: 'Bar', info: 'Hello world!' },
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @template T
|
||||
* @param {{ [key: string]: T }} obj Object, or in DM terms, an assoc array
|
||||
* @param {string} keyProp Property, to which key will be assigned
|
||||
* @returns {T[]} Array of keyed objects
|
||||
*/
|
||||
export const toKeyedArray = (obj, keyProp = 'key') => {
|
||||
return map((item, key) => ({
|
||||
[keyProp]: key,
|
||||
...item,
|
||||
}))(obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterates over elements of collection, returning an array of all elements
|
||||
* iteratee returns truthy for. The predicate is invoked with three
|
||||
@@ -72,21 +14,40 @@ export const toKeyedArray = (obj, keyProp = 'key') => {
|
||||
*
|
||||
* @returns {any[]}
|
||||
*/
|
||||
export const filter = iterateeFn => collection => {
|
||||
if (collection === null || collection === undefined) {
|
||||
return collection;
|
||||
}
|
||||
if (Array.isArray(collection)) {
|
||||
const result = [];
|
||||
for (let i = 0; i < collection.length; i++) {
|
||||
const item = collection[i];
|
||||
if (iterateeFn(item, i, collection)) {
|
||||
result.push(item);
|
||||
export const filter = <T>(iterateeFn: (
|
||||
input: T,
|
||||
index: number,
|
||||
collection: T[],
|
||||
) => boolean) =>
|
||||
(collection: T[]): T[] => {
|
||||
if (collection === null || collection === undefined) {
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
throw new Error(`filter() can't iterate on type ${typeof 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}`);
|
||||
};
|
||||
|
||||
type MapFunction = {
|
||||
<T, U>(iterateeFn: (
|
||||
value: T,
|
||||
index: number,
|
||||
collection: T[],
|
||||
) => U): (collection: T[]) => U[];
|
||||
|
||||
<T, U, K extends string | number>(iterateeFn: (
|
||||
value: T,
|
||||
index: K,
|
||||
collection: Record<K, T>,
|
||||
) => U): (collection: Record<K, T>) => U[];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -96,32 +57,25 @@ export const filter = iterateeFn => collection => {
|
||||
*
|
||||
* If collection is 'null' or 'undefined', it will be returned "as is"
|
||||
* without emitting any errors (which can be useful in some cases).
|
||||
*
|
||||
* @returns {any[]}
|
||||
*/
|
||||
export const map = iterateeFn => collection => {
|
||||
if (collection === null || collection === undefined) {
|
||||
return collection;
|
||||
}
|
||||
if (Array.isArray(collection)) {
|
||||
const result = [];
|
||||
for (let i = 0; i < collection.length; i++) {
|
||||
result.push(iterateeFn(collection[i], i, collection));
|
||||
export const map: MapFunction = <T, U>(iterateeFn) =>
|
||||
(collection: T[]): U[] => {
|
||||
if (collection === null || collection === undefined) {
|
||||
return collection;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (typeof collection === 'object') {
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
const result = [];
|
||||
for (let i in collection) {
|
||||
if (hasOwnProperty.call(collection, i)) {
|
||||
result.push(iterateeFn(collection[i], i, collection));
|
||||
}
|
||||
|
||||
if (Array.isArray(collection)) {
|
||||
return collection.map(iterateeFn);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
throw new Error(`map() can't iterate on type ${typeof collection}`);
|
||||
};
|
||||
|
||||
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}`);
|
||||
};
|
||||
|
||||
const COMPARATOR = (objA, objB) => {
|
||||
const criteriaA = objA.criteria;
|
||||
@@ -148,28 +102,35 @@ const COMPARATOR = (objA, objB) => {
|
||||
*
|
||||
* @returns {any[]}
|
||||
*/
|
||||
export const sortBy = (...iterateeFns) => array => {
|
||||
if (!Array.isArray(array)) {
|
||||
return array;
|
||||
}
|
||||
let length = array.length;
|
||||
// Iterate over the array to collect criteria to sort it by
|
||||
let mappedArray = [];
|
||||
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
|
||||
while (length--) {
|
||||
mappedArray[length] = mappedArray[length].value;
|
||||
}
|
||||
return mappedArray;
|
||||
};
|
||||
export const sortBy = <T>(
|
||||
...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);
|
||||
|
||||
// Unwrap values
|
||||
const values: T[] = [];
|
||||
while (length--) {
|
||||
values[length] = mappedArray[length].value;
|
||||
}
|
||||
return values;
|
||||
};
|
||||
|
||||
export const sort = sortBy();
|
||||
|
||||
@@ -212,40 +173,38 @@ export const reduce = (reducerFn, initialValue) => array => {
|
||||
* is determined by the order they occur in the array. The iteratee is
|
||||
* invoked with one argument: value.
|
||||
*/
|
||||
/* eslint-disable indent */
|
||||
export const uniqBy = <T extends unknown>(
|
||||
iterateeFn?: (value: T) => unknown
|
||||
) => (array: T[]) => {
|
||||
const { length } = array;
|
||||
const result = [];
|
||||
const seen = iterateeFn ? [] : result;
|
||||
let index = -1;
|
||||
outer:
|
||||
while (++index < length) {
|
||||
let value: T | 0 = array[index];
|
||||
const computed = iterateeFn ? iterateeFn(value) : value;
|
||||
value = value !== 0 ? value : 0;
|
||||
if (computed === computed) {
|
||||
let seenIndex = seen.length;
|
||||
while (seenIndex--) {
|
||||
if (seen[seenIndex] === computed) {
|
||||
continue outer;
|
||||
) => (array: T[]): T[] => {
|
||||
const { length } = array;
|
||||
const result: T[] = [];
|
||||
const seen: unknown[] = iterateeFn ? [] : result;
|
||||
let index = -1;
|
||||
outer:
|
||||
while (++index < length) {
|
||||
let value: T | 0 = array[index];
|
||||
const computed = iterateeFn ? iterateeFn(value) : value;
|
||||
if (computed === computed) {
|
||||
let seenIndex = seen.length;
|
||||
while (seenIndex--) {
|
||||
if (seen[seenIndex] === computed) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
if (iterateeFn) {
|
||||
seen.push(computed);
|
||||
}
|
||||
result.push(value);
|
||||
}
|
||||
if (iterateeFn) {
|
||||
seen.push(computed);
|
||||
else if (!seen.includes(computed)) {
|
||||
if (seen !== result) {
|
||||
seen.push(computed);
|
||||
}
|
||||
result.push(value);
|
||||
}
|
||||
result.push(value);
|
||||
}
|
||||
else if (!seen.includes(computed)) {
|
||||
if (seen !== result) {
|
||||
seen.push(computed);
|
||||
}
|
||||
result.push(value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return result;
|
||||
};
|
||||
/* eslint-enable indent */
|
||||
|
||||
export const uniq = uniqBy();
|
||||
@@ -261,17 +220,19 @@ type Zip<T extends unknown[][]> = {
|
||||
*/
|
||||
export const zip = <T extends unknown[][]>(...arrays: T): Zip<T> => {
|
||||
if (arrays.length === 0) {
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
const numArrays = arrays.length;
|
||||
const numValues = arrays[0].length;
|
||||
const result = [];
|
||||
const result: Zip<T> = [];
|
||||
for (let valueIndex = 0; valueIndex < numValues; valueIndex++) {
|
||||
const entry = [];
|
||||
const entry: unknown[] = [];
|
||||
for (let arrayIndex = 0; arrayIndex < numArrays; arrayIndex++) {
|
||||
entry.push(arrays[arrayIndex][valueIndex]);
|
||||
}
|
||||
result.push(entry);
|
||||
|
||||
// I tried everything to remove this any, and have no idea how to do it.
|
||||
result.push(entry as any);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -280,9 +241,8 @@ export const zip = <T extends unknown[][]>(...arrays: T): Zip<T> => {
|
||||
* 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.
|
||||
*
|
||||
* @returns {any[]}
|
||||
*/
|
||||
export const zipWith = iterateeFn => (...arrays) => {
|
||||
return map(values => iterateeFn(...values))(zip(...arrays));
|
||||
};
|
||||
export const zipWith = <T, U>(iterateeFn: (...values: T[]) => U) =>
|
||||
(...arrays: T[][]): U[] => {
|
||||
return map((values: T[]) => iterateeFn(...values))(zip(...arrays));
|
||||
};
|
||||
|
||||
5
tgui/packages/common/types.ts
Normal file
5
tgui/packages/common/types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Returns the arguments of a function F as an array.
|
||||
*/
|
||||
export type ArgumentsOf<F extends Function>
|
||||
= F extends (...args: infer A) => unknown ? A : never;
|
||||
@@ -93,9 +93,7 @@ export const halfUnit = (value: unknown): string | undefined => {
|
||||
const isColorCode = (str: unknown) => !isColorClass(str);
|
||||
|
||||
const isColorClass = (str: unknown): boolean => {
|
||||
if (typeof str === 'string') {
|
||||
return CSS_COLORS.includes(str);
|
||||
}
|
||||
return typeof str === "string" && CSS_COLORS.includes(str);
|
||||
};
|
||||
|
||||
const mapRawPropTo = attrName => (style, value) => {
|
||||
|
||||
@@ -78,7 +78,7 @@ export const computeFlexItemProps = (props: FlexItemProps) => {
|
||||
className: classes([
|
||||
'Flex__item',
|
||||
Byond.IS_LTE_IE10 && 'Flex__item--iefix',
|
||||
Byond.IS_LTE_IE10 && grow > 0 && 'Flex__item--iefix--grow',
|
||||
Byond.IS_LTE_IE10 && (grow && grow > 0) && 'Flex__item--iefix--grow',
|
||||
className,
|
||||
]),
|
||||
style: {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { createPopper, OptionsGeneric } from "@popperjs/core";
|
||||
import { ArgumentsOf } from "common/types";
|
||||
import { Component, findDOMfromVNode, InfernoNode, render } from "inferno";
|
||||
|
||||
type PopperProps = {
|
||||
popperContent: InfernoNode;
|
||||
options?: Partial<OptionsGeneric<unknown>>;
|
||||
options?: ArgumentsOf<typeof createPopper>[2];
|
||||
additionalStyles?: CSSProperties,
|
||||
};
|
||||
|
||||
@@ -35,17 +36,22 @@ export class Popper extends Component<PopperProps> {
|
||||
this.renderPopperContent(() => {
|
||||
document.body.appendChild(this.renderedContent);
|
||||
|
||||
// HACK: We don't want to create a wrapper, as it could break the layout
|
||||
// of consumers, so we do the inferno equivalent of `findDOMNode(this)`.
|
||||
// This is usually bad as refs are usually better, but refs did
|
||||
// not work in this case, as they weren't propagating correctly.
|
||||
// A previous attempt was made as a render prop that passed an ID,
|
||||
// but this made consuming use too unwieldly.
|
||||
// This code is copied from `findDOMNode` in inferno-extras.
|
||||
// Because this component is written in TypeScript, we will know
|
||||
// immediately if this internal variable is removed.
|
||||
const domNode = findDOMfromVNode(this.$LI, true);
|
||||
if (!domNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.popperInstance = createPopper(
|
||||
// HACK: We don't want to create a wrapper, as it could break the layout
|
||||
// of consumers, so we do the inferno equivalent of `findDOMNode(this)`.
|
||||
// This is usually bad as refs are usually better, but refs did
|
||||
// not work in this case, as they weren't propagating correctly.
|
||||
// A previous attempt was made as a render prop that passed an ID,
|
||||
// but this made consuming use too unwieldly.
|
||||
// This code is copied from `findDOMNode` in inferno-extras.
|
||||
// Because this component is written in TypeScript, we will know
|
||||
// immediately if this internal variable is removed.
|
||||
findDOMfromVNode(this.$LI, true),
|
||||
domNode,
|
||||
this.renderedContent,
|
||||
options,
|
||||
);
|
||||
@@ -59,7 +65,6 @@ export class Popper extends Component<PopperProps> {
|
||||
componentWillUnmount() {
|
||||
this.popperInstance?.destroy();
|
||||
this.renderedContent.remove();
|
||||
this.renderedContent = null;
|
||||
}
|
||||
|
||||
renderPopperContent(callback: () => void) {
|
||||
|
||||
@@ -16,13 +16,9 @@ type TooltipState = {
|
||||
};
|
||||
|
||||
export class Tooltip extends Component<TooltipProps, TooltipState> {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
hovered: false,
|
||||
};
|
||||
}
|
||||
state = {
|
||||
hovered: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
// HACK: We don't want to create a wrapper, as it could break the layout
|
||||
@@ -35,6 +31,10 @@ export class Tooltip extends Component<TooltipProps, TooltipState> {
|
||||
// immediately if this internal variable is removed.
|
||||
const domNode = findDOMfromVNode(this.$LI, true);
|
||||
|
||||
if (!domNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
domNode.addEventListener("mouseenter", () => {
|
||||
this.setState({
|
||||
hovered: true,
|
||||
|
||||
@@ -28,6 +28,11 @@ const AdventureEntry = (props, context) => {
|
||||
const { data, act } = useBackend<AdventureBrowserData>(context);
|
||||
const { entry_ref, close }: { entry_ref: string, close: () => void } = props;
|
||||
const entry = data.adventures.find(x => x.ref === entry_ref);
|
||||
|
||||
if (!entry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<LabeledList>
|
||||
@@ -62,7 +67,7 @@ const AdventureList = (props, context) => {
|
||||
const [
|
||||
openAdventure,
|
||||
setOpenAdventure,
|
||||
] = useLocalState(context, 'openAdventure', null);
|
||||
] = useLocalState<string | null>(context, 'openAdventure', null);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -78,13 +83,13 @@ const AdventureList = (props, context) => {
|
||||
<Table.Cell color="label">Title</Table.Cell>
|
||||
<Table.Cell color="label">Edit</Table.Cell>
|
||||
</Table.Row>
|
||||
{data.adventures.map(p => (
|
||||
{data.adventures.map(adventure => (
|
||||
<Table.Row
|
||||
key={p.ref}
|
||||
key={adventure.ref}
|
||||
className="candystripe">
|
||||
<Table.Cell>{p.id}</Table.Cell>
|
||||
<Table.Cell>{p.name}</Table.Cell>
|
||||
<Table.Cell><Button icon="edit" onClick={() => setOpenAdventure(p.ref)} /></Table.Cell>
|
||||
<Table.Cell>{adventure.id}</Table.Cell>
|
||||
<Table.Cell>{adventure.name}</Table.Cell>
|
||||
<Table.Cell><Button icon="edit" onClick={() => setOpenAdventure(adventure.ref)} /></Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
<Table.Row>
|
||||
@@ -104,7 +109,14 @@ const DebugPlayer = (props, context) => {
|
||||
buttons={<Button onClick={() => act("end_play")}>End Playtest</Button>}>
|
||||
{data.delay_time > 0
|
||||
? <Box>DELAY {formatTime(data.delay_time)} / {data.delay_message}</Box>
|
||||
: <AdventureScreen hide_status />}
|
||||
: (
|
||||
<AdventureScreen
|
||||
adventure_data={data.adventure_data}
|
||||
drone_integrity={100}
|
||||
drone_max_integrity={100}
|
||||
hide_status
|
||||
/>
|
||||
)}
|
||||
</Section>);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { toArray } from 'common/collections';
|
||||
import { useBackend, useSharedState } from '../backend';
|
||||
import { AnimatedNumber, Box, Button, Flex, LabeledList, Section, Table, Tabs } from '../components';
|
||||
import { formatMoney } from '../format';
|
||||
@@ -135,7 +134,7 @@ export const CargoCatalog = (props, context) => {
|
||||
self_paid,
|
||||
app_cost,
|
||||
} = data;
|
||||
const supplies = toArray(data.supplies);
|
||||
const supplies = Object.values(data.supplies);
|
||||
const [
|
||||
activeSupplyName,
|
||||
setActiveSupplyName,
|
||||
|
||||
@@ -75,35 +75,72 @@ type DroneBasicData = {
|
||||
}
|
||||
|
||||
export type AdventureDataProvider = {
|
||||
adventure_data?: AdventureData;
|
||||
adventure_data: AdventureData,
|
||||
}
|
||||
|
||||
type ExodroneConsoleData = AdventureDataProvider & {
|
||||
signal_lost: boolean,
|
||||
drone: boolean,
|
||||
all_drones?: Array<DroneBasicData>
|
||||
drone_status?: DroneStatusEnum,
|
||||
drone_name?: string,
|
||||
drone_integrity?: number,
|
||||
drone_max_integrity?: number,
|
||||
drone_travel_coefficent?: number,
|
||||
drone_log?: Array<string>,
|
||||
configurable?: boolean,
|
||||
cargo?: Array<CargoData>,
|
||||
can_travel?: boolean,
|
||||
type DroneAdventure = AdventureDataProvider & {
|
||||
drone_status: DroneStatusEnum.Adventure,
|
||||
};
|
||||
|
||||
type DroneData = {
|
||||
drone_name: string,
|
||||
drone_integrity: number,
|
||||
drone_max_integrity: number,
|
||||
drone_travel_coefficent: number,
|
||||
drone_log: Array<string>,
|
||||
configurable: boolean,
|
||||
cargo: Array<CargoData>,
|
||||
can_travel: boolean,
|
||||
travel_error: string,
|
||||
sites?: Array<SiteData>,
|
||||
site?: SiteData,
|
||||
travel_time?: number,
|
||||
travel_time_left?: number,
|
||||
wait_time_left?: number,
|
||||
wait_message?: string,
|
||||
};
|
||||
|
||||
type DroneBusy = {
|
||||
drone_status: DroneStatusEnum.Busy,
|
||||
wait_time_left: number,
|
||||
wait_message: string,
|
||||
};
|
||||
|
||||
type DroneExploration = {
|
||||
drone_status: DroneStatusEnum.Exploration,
|
||||
sites: Array<SiteData>,
|
||||
site: SiteData,
|
||||
event?: FullEventData,
|
||||
adventure_data?: AdventureData,
|
||||
};
|
||||
|
||||
type DroneIdle = {
|
||||
drone_status: DroneStatusEnum.Idle,
|
||||
sites: Array<SiteData>,
|
||||
site: null,
|
||||
};
|
||||
|
||||
type DroneTravel = {
|
||||
drone_status: DroneStatusEnum.Travel,
|
||||
travel_time: number,
|
||||
travel_time_left: number,
|
||||
};
|
||||
|
||||
type ActiveDrone = DroneAdventure
|
||||
| DroneBusy
|
||||
| DroneExploration
|
||||
| DroneIdle
|
||||
| DroneTravel;
|
||||
|
||||
type ExodroneConsoleData = {
|
||||
signal_lost: boolean;
|
||||
|
||||
// ui_static_data
|
||||
all_tools: Record<string, ToolData>,
|
||||
all_bands: Record<string, string>
|
||||
}
|
||||
all_tools: Record<string, ToolData>;
|
||||
all_bands: Record<string, string>;
|
||||
} & (
|
||||
| (({
|
||||
drone: true;
|
||||
} & DroneData) &
|
||||
ActiveDrone)
|
||||
| {
|
||||
all_drones: Array<DroneBasicData>;
|
||||
drone: undefined;
|
||||
}
|
||||
);
|
||||
|
||||
type ToolData = {
|
||||
description: string,
|
||||
@@ -170,11 +207,11 @@ const SignalLostModal = (props, context) => {
|
||||
);
|
||||
};
|
||||
|
||||
const DroneSelectionSection = (props, context) => {
|
||||
const { act, data } = useBackend<ExodroneConsoleData>(context);
|
||||
const {
|
||||
all_drones,
|
||||
} = data;
|
||||
const DroneSelectionSection = (props: {
|
||||
all_drones: Array<DroneBasicData>,
|
||||
}, context) => {
|
||||
const { act } = useBackend<ExodroneConsoleData>(context);
|
||||
const { all_drones } = props;
|
||||
|
||||
return (
|
||||
<Section scrollable fill title="Exploration Drone Listing">
|
||||
@@ -266,12 +303,13 @@ const ToolSelectionModal = (props, context) => {
|
||||
);
|
||||
};
|
||||
|
||||
const EquipmentBox = (props, context) => {
|
||||
const EquipmentBox = (props: {
|
||||
cargo: CargoData,
|
||||
drone: DroneData,
|
||||
}, context) => {
|
||||
const { act, data } = useBackend<ExodroneConsoleData>(context);
|
||||
const {
|
||||
configurable,
|
||||
all_tools = {},
|
||||
} = data;
|
||||
const { all_tools = {} } = data;
|
||||
const { configurable } = props.drone;
|
||||
const cargo = props.cargo;
|
||||
const boxContents = cargo => {
|
||||
switch (cargo.type) {
|
||||
@@ -349,12 +387,14 @@ const EquipmentBox = (props, context) => {
|
||||
);
|
||||
};
|
||||
|
||||
const EquipmentGrid = (props, context) => {
|
||||
const { act, data } = useBackend<ExodroneConsoleData>(context);
|
||||
const EquipmentGrid = (props: {
|
||||
drone: ActiveDrone & DroneData,
|
||||
}, context) => {
|
||||
const { act } = useBackend<ExodroneConsoleData>(context);
|
||||
const {
|
||||
cargo,
|
||||
configurable,
|
||||
} = data;
|
||||
} = props.drone;
|
||||
const [
|
||||
choosingTools,
|
||||
setChoosingTools,
|
||||
@@ -399,6 +439,7 @@ const EquipmentGrid = (props, context) => {
|
||||
<Stack wrap="wrap" width={10}>
|
||||
{cargo.map(cargo_element => (
|
||||
<EquipmentBox
|
||||
drone={props.drone}
|
||||
key={cargo_element.name}
|
||||
cargo={cargo_element} />
|
||||
))}
|
||||
@@ -410,12 +451,14 @@ const EquipmentGrid = (props, context) => {
|
||||
);
|
||||
};
|
||||
|
||||
const DroneStatus = (props, context) => {
|
||||
const { act, data } = useBackend<ExodroneConsoleData>(context);
|
||||
const DroneStatus = (props: {
|
||||
drone_integrity: number,
|
||||
drone_max_integrity: number,
|
||||
}, context) => {
|
||||
const {
|
||||
drone_integrity,
|
||||
drone_max_integrity,
|
||||
} = data;
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Stack ml={-40}>
|
||||
@@ -459,18 +502,22 @@ const NoSiteDimmer = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const TravelTargetSelectionScreen = (props, context) => {
|
||||
const TravelTargetSelectionScreen = (props: {
|
||||
drone: (DroneExploration | DroneIdle | DroneTravel) & DroneData,
|
||||
showCancelButton?: boolean,
|
||||
}, context) => {
|
||||
// List of sites and eta travel times to each
|
||||
const { act, data } = useBackend<ExodroneConsoleData>(context);
|
||||
const { drone } = props;
|
||||
const { all_bands } = data;
|
||||
const {
|
||||
sites,
|
||||
site,
|
||||
can_travel,
|
||||
travel_error,
|
||||
drone_travel_coefficent,
|
||||
all_bands,
|
||||
drone_status,
|
||||
} = data;
|
||||
} = drone;
|
||||
|
||||
const site = ("site" in drone) ? drone.site : null;
|
||||
const sites = ("sites" in drone) ? drone.sites : null;
|
||||
|
||||
const travel_cost = target_site => {
|
||||
if (site) {
|
||||
@@ -500,12 +547,12 @@ const TravelTargetSelectionScreen = (props, context) => {
|
||||
&& dest.band_info[s] !== 0;
|
||||
return Object.keys(all_bands).filter(band_check);
|
||||
};
|
||||
const valid_destinations = !!sites && sites.filter(destination => (
|
||||
const valid_destinations = sites && sites.filter(destination => (
|
||||
!site || destination.ref !== site.ref
|
||||
));
|
||||
return (
|
||||
drone_status === "travel" && (
|
||||
<TravelDimmer />
|
||||
drone.drone_status === DroneStatusEnum.Travel && (
|
||||
<TravelDimmer drone={drone} />
|
||||
) || (
|
||||
<Section
|
||||
title="Travel Destinations"
|
||||
@@ -521,7 +568,10 @@ const TravelTargetSelectionScreen = (props, context) => {
|
||||
onClick={() => setTravelDimmerShown(false)} />
|
||||
)}
|
||||
<Box mt={props.showCancelButton && -3.5}>
|
||||
<DroneStatus />
|
||||
<DroneStatus
|
||||
drone_integrity={drone.drone_integrity}
|
||||
drone_max_integrity={drone.drone_max_integrity}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
}>
|
||||
@@ -544,7 +594,7 @@ const TravelTargetSelectionScreen = (props, context) => {
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{valid_destinations.map(destination => (
|
||||
{valid_destinations?.map(destination => (
|
||||
<Section
|
||||
key={destination.ref}
|
||||
title={destination.name}
|
||||
@@ -581,12 +631,10 @@ const TravelTargetSelectionScreen = (props, context) => {
|
||||
);
|
||||
};
|
||||
|
||||
const TravelDimmer = (props, context) => {
|
||||
const { act, data } = useBackend<ExodroneConsoleData>(context);
|
||||
const {
|
||||
travel_time,
|
||||
travel_time_left,
|
||||
} = data;
|
||||
const TravelDimmer = (props: {
|
||||
drone: DroneTravel,
|
||||
}, context) => {
|
||||
const { travel_time_left } = props.drone;
|
||||
return (
|
||||
<Section fill>
|
||||
<Dimmer>
|
||||
@@ -607,12 +655,14 @@ const TravelDimmer = (props, context) => {
|
||||
);
|
||||
};
|
||||
|
||||
const TimeoutScreen = (props, context) => {
|
||||
const { act, data } = useBackend<ExodroneConsoleData>(context);
|
||||
const TimeoutScreen = (props: {
|
||||
drone: DroneBusy,
|
||||
}) => {
|
||||
const {
|
||||
wait_time_left,
|
||||
wait_message,
|
||||
} = data;
|
||||
} = props.drone;
|
||||
|
||||
return (
|
||||
<Section fill>
|
||||
<Dimmer>
|
||||
@@ -633,13 +683,12 @@ const TimeoutScreen = (props, context) => {
|
||||
);
|
||||
};
|
||||
|
||||
const ExplorationScreen = (props, context) => {
|
||||
const { act, data } = useBackend<ExodroneConsoleData>(context);
|
||||
const {
|
||||
site,
|
||||
event,
|
||||
sites,
|
||||
} = data;
|
||||
const ExplorationScreen = (props: {
|
||||
drone: DroneExploration & DroneData,
|
||||
}, context) => {
|
||||
const { act } = useBackend(context);
|
||||
const { drone } = props;
|
||||
const { site } = drone;
|
||||
|
||||
const [
|
||||
TravelDimmerShown,
|
||||
@@ -647,14 +696,20 @@ const ExplorationScreen = (props, context) => {
|
||||
] = useLocalState(context, 'TravelDimmerShown', false);
|
||||
|
||||
if (TravelDimmerShown) {
|
||||
return (<TravelTargetSelectionScreen showCancelButton />);
|
||||
return (<TravelTargetSelectionScreen
|
||||
drone={drone}
|
||||
showCancelButton
|
||||
/>);
|
||||
}
|
||||
return (
|
||||
<Section
|
||||
fill
|
||||
title="Exploration"
|
||||
buttons={
|
||||
<DroneStatus />
|
||||
<DroneStatus
|
||||
drone_integrity={drone.drone_integrity}
|
||||
drone_max_integrity={drone.drone_max_integrity}
|
||||
/>
|
||||
}>
|
||||
<Stack vertical fill>
|
||||
<Stack.Item grow>
|
||||
@@ -688,22 +743,23 @@ const ExplorationScreen = (props, context) => {
|
||||
);
|
||||
};
|
||||
|
||||
const EventScreen = (props, context) => {
|
||||
const { act, data } = useBackend<ExodroneConsoleData>(context);
|
||||
const {
|
||||
drone_status,
|
||||
event,
|
||||
} = data;
|
||||
const EventScreen = (props: {
|
||||
drone: DroneData,
|
||||
event: FullEventData,
|
||||
}, context) => {
|
||||
const { act } = useBackend(context);
|
||||
const { drone, event } = props;
|
||||
|
||||
return (
|
||||
<Section
|
||||
fill
|
||||
title="Exploration"
|
||||
buttons={
|
||||
<DroneStatus />
|
||||
<DroneStatus
|
||||
drone_integrity={drone.drone_integrity}
|
||||
drone_max_integrity={drone.drone_max_integrity}
|
||||
/>
|
||||
}>
|
||||
{(drone_status && drone_status === "busy") && (
|
||||
<TimeoutScreen />
|
||||
)}
|
||||
<Stack vertical fill textAlign="center">
|
||||
<Stack.Item>
|
||||
<Stack fill>
|
||||
@@ -747,23 +803,28 @@ const EventScreen = (props, context) => {
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
type AdventureScreenProps = {
|
||||
hide_status?: boolean
|
||||
}
|
||||
|
||||
export const AdventureScreen = (props: AdventureScreenProps, context) => {
|
||||
const { act, data } = useBackend<AdventureDataProvider>(context);
|
||||
export const AdventureScreen = (props: {
|
||||
adventure_data: AdventureData,
|
||||
drone_integrity: number,
|
||||
drone_max_integrity: number,
|
||||
hide_status?: boolean,
|
||||
}, context) => {
|
||||
const { act } = useBackend(context);
|
||||
const {
|
||||
adventure_data,
|
||||
} = data;
|
||||
drone_integrity,
|
||||
drone_max_integrity,
|
||||
} = props;
|
||||
const rawData = adventure_data.raw_image;
|
||||
const imgSource = rawData ? rawData : resolveAsset(adventure_data.image);
|
||||
return (
|
||||
<Section
|
||||
fill
|
||||
title="Exploration"
|
||||
buttons={!props.hide_status && <DroneStatus />}>
|
||||
buttons={!props.hide_status && <DroneStatus
|
||||
drone_integrity={drone_integrity}
|
||||
drone_max_integrity={drone_max_integrity}
|
||||
/>}>
|
||||
<Stack>
|
||||
<Stack.Item>
|
||||
<BlockQuote preserveWhitespace>
|
||||
@@ -799,42 +860,42 @@ export const AdventureScreen = (props: AdventureScreenProps, context) => {
|
||||
);
|
||||
};
|
||||
|
||||
const DroneScreen = (props, context) => {
|
||||
const { act, data } = useBackend<ExodroneConsoleData>(context);
|
||||
const {
|
||||
drone_status,
|
||||
event,
|
||||
} = data;
|
||||
switch (drone_status) {
|
||||
case "busy":
|
||||
return <TimeoutScreen />;
|
||||
case "idle":
|
||||
case "travel":
|
||||
return <TravelTargetSelectionScreen />;
|
||||
case "adventure":
|
||||
return <AdventureScreen />;
|
||||
case "exploration":
|
||||
if (event) {
|
||||
return <EventScreen />;
|
||||
const DroneScreen = (props: {
|
||||
drone: ActiveDrone & DroneData,
|
||||
}) => {
|
||||
const { drone } = props;
|
||||
|
||||
switch (drone.drone_status) {
|
||||
case DroneStatusEnum.Busy:
|
||||
return <TimeoutScreen drone={drone} />;
|
||||
case DroneStatusEnum.Idle:
|
||||
case DroneStatusEnum.Travel:
|
||||
return <TravelTargetSelectionScreen drone={drone} />;
|
||||
case DroneStatusEnum.Adventure:
|
||||
return (<AdventureScreen
|
||||
adventure_data={drone.adventure_data}
|
||||
drone_integrity={drone.drone_integrity}
|
||||
drone_max_integrity={drone.drone_max_integrity}
|
||||
/>);
|
||||
case DroneStatusEnum.Exploration:
|
||||
if (drone.event) {
|
||||
return <EventScreen drone={drone} event={drone.event} />;
|
||||
}
|
||||
else {
|
||||
return <ExplorationScreen />;
|
||||
return <ExplorationScreen drone={drone} />;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const ExodroneConsoleContent = (props, context) => {
|
||||
const { act, data } = useBackend<ExodroneConsoleData>(context);
|
||||
const {
|
||||
drone,
|
||||
drone_name,
|
||||
drone_log,
|
||||
} = data;
|
||||
const { data } = useBackend<ExodroneConsoleData>(context);
|
||||
|
||||
if (!drone) {
|
||||
return <DroneSelectionSection />;
|
||||
if (!data.drone) {
|
||||
return <DroneSelectionSection all_drones={data.all_drones} />;
|
||||
}
|
||||
|
||||
const { drone_log } = data;
|
||||
|
||||
return (
|
||||
<Stack fill vertical>
|
||||
<Stack.Item grow>
|
||||
@@ -842,10 +903,10 @@ const ExodroneConsoleContent = (props, context) => {
|
||||
<Stack.Item grow>
|
||||
<Stack fill>
|
||||
<Stack.Item>
|
||||
<EquipmentGrid />
|
||||
<EquipmentGrid drone={data} />
|
||||
</Stack.Item>
|
||||
<Stack.Item grow basis={0}>
|
||||
<DroneScreen />
|
||||
<DroneScreen drone={data} />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
|
||||
@@ -290,7 +290,9 @@ export const StripMenu = (props, context) => {
|
||||
if (item === null) {
|
||||
tooltip = slot.displayName;
|
||||
} else if ("name" in item) {
|
||||
alternateAction = ALTERNATE_ACTIONS[item.alternate];
|
||||
if (item.alternate) {
|
||||
alternateAction = ALTERNATE_ACTIONS[item.alternate];
|
||||
}
|
||||
|
||||
content = (
|
||||
<Box
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"strictNullChecks": true,
|
||||
"target": "ES3"
|
||||
},
|
||||
"include": [
|
||||
|
||||
Reference in New Issue
Block a user