diff --git a/frontend/__test_support__/helpers.ts b/frontend/__test_support__/helpers.ts index cf33beb5c..9b2859de1 100644 --- a/frontend/__test_support__/helpers.ts +++ b/frontend/__test_support__/helpers.ts @@ -7,11 +7,11 @@ export function clickButton( position: number, text: string, options?: { partial_match?: boolean, button_tag?: string }) { - const btnTag = options && options.button_tag ? options.button_tag : "button"; + const btnTag = options?.button_tag ? options.button_tag : "button"; const button = wrapper.find(btnTag).at(position); const expectedText = text.toLowerCase(); const actualText = button.text().toLowerCase(); - options && options.partial_match + options?.partial_match ? expect(actualText).toContain(expectedText) : expect(actualText).toEqual(expectedText); button.simulate("click"); diff --git a/frontend/__tests__/resource_index_builder_test.ts b/frontend/__tests__/resource_index_builder_test.ts index 06f047639..e953c7296 100644 --- a/frontend/__tests__/resource_index_builder_test.ts +++ b/frontend/__tests__/resource_index_builder_test.ts @@ -32,7 +32,7 @@ test("buildResourceIndex - add a FarmEvent", () => { const key = Object.keys(db.index.byKind.FarmEvent)[0]; const fe = db.index.references[key]; expect(fe).toBeTruthy(); - if (fe && fe.kind === "FarmEvent") { + if (fe?.kind === "FarmEvent") { const { body } = fe; expect(body).toEqual(STUB_RESOURCE.body); } else { diff --git a/frontend/controls/axis_input_box.tsx b/frontend/controls/axis_input_box.tsx index 663b6c463..da59dfab6 100644 --- a/frontend/controls/axis_input_box.tsx +++ b/frontend/controls/axis_input_box.tsx @@ -1,12 +1,11 @@ import * as React from "react"; import { AxisInputBoxProps } from "./interfaces"; import { Col, BlurableInput } from "../ui/index"; -import { isUndefined } from "lodash"; export const AxisInputBox = ({ onChange, value, axis }: AxisInputBoxProps) => { return { diff --git a/frontend/devices/components/boolean_mcu_input_group.tsx b/frontend/devices/components/boolean_mcu_input_group.tsx index d7a7d6450..cd223864c 100644 --- a/frontend/devices/components/boolean_mcu_input_group.tsx +++ b/frontend/devices/components/boolean_mcu_input_group.tsx @@ -36,8 +36,8 @@ export function BooleanMCUInputGroup(props: BooleanMCUInputGroupProps) { @@ -45,8 +45,8 @@ export function BooleanMCUInputGroup(props: BooleanMCUInputGroupProps) { @@ -54,8 +54,8 @@ export function BooleanMCUInputGroup(props: BooleanMCUInputGroupProps) { diff --git a/frontend/devices/pin_bindings/__tests__/pin_binding_input_group_test.tsx b/frontend/devices/pin_bindings/__tests__/pin_binding_input_group_test.tsx index bdb9e7805..2b7cb1fa8 100644 --- a/frontend/devices/pin_bindings/__tests__/pin_binding_input_group_test.tsx +++ b/frontend/devices/pin_bindings/__tests__/pin_binding_input_group_test.tsx @@ -115,7 +115,7 @@ describe("", () => { const p = fakeProps(); const key = Object.keys(p.resources.byKind.Sequence)[0]; const s = p.resources.references[key]; - const id = s && s.body.id; + const id = s?.body.id; const wrapper = mount(); expect(wrapper.instance().state.sequenceIdInput).toEqual(undefined); wrapper.instance().setSequenceIdInput({ label: "label", value: "" + id }); diff --git a/frontend/devices/update_interceptor.ts b/frontend/devices/update_interceptor.ts index 603562a45..eef0ab024 100644 --- a/frontend/devices/update_interceptor.ts +++ b/frontend/devices/update_interceptor.ts @@ -79,8 +79,8 @@ export const mcuParamValidator = (ok: () => void, no?: (message: string) => void): void => { const validator = edgeCases[key]; const result = validator && validator(key, val, state); - if (result && result.outcome === "NO") { - return (no && no(result.errorMessage)); + if (result?.outcome === "NO") { + return (no?.(result.errorMessage)); } else { return ok(); } diff --git a/frontend/draggable/__tests__/reducer_test.ts b/frontend/draggable/__tests__/reducer_test.ts index 008237085..92b0f89b7 100644 --- a/frontend/draggable/__tests__/reducer_test.ts +++ b/frontend/draggable/__tests__/reducer_test.ts @@ -26,7 +26,7 @@ describe("draggableReducer", () => { const dt = nextState.dataTransfer; expect(Object.keys(dt)).toContain(payload.uuid); const entry = dt[payload.uuid]; - expect(entry && entry.uuid).toEqual(payload.uuid); + expect(entry?.uuid).toEqual(payload.uuid); }); it("drops a step", () => { diff --git a/frontend/farm_designer/designer_panel.tsx b/frontend/farm_designer/designer_panel.tsx index a6d4bbafd..fcfbcf651 100644 --- a/frontend/farm_designer/designer_panel.tsx +++ b/frontend/farm_designer/designer_panel.tsx @@ -56,7 +56,7 @@ export const DesignerPanelHeader = (props: DesignerPanelHeaderProps) => { title={t("go back") + backToText(props.backTo)} onClick={() => { props.backTo ? routeHistory.push(props.backTo) : history.back(); - props.onBack && props.onBack(); + props.onBack?.(); }} /> {props.title && diff --git a/frontend/farm_designer/farm_events/calendar/occurrence.ts b/frontend/farm_designer/farm_events/calendar/occurrence.ts index f3ef5ca97..b5e2b599d 100644 --- a/frontend/farm_designer/farm_events/calendar/occurrence.ts +++ b/frontend/farm_designer/farm_events/calendar/occurrence.ts @@ -16,10 +16,10 @@ export function occurrence( CalendarOccurrence { const normalHeading = fe.executable.name || fe.executable_type; const heading = () => { - if (modifiers && modifiers.empty) { + if (modifiers?.empty) { return "*Empty*"; } - if (modifiers && modifiers.numHidden) { + if (modifiers?.numHidden) { return `+ ${modifiers.numHidden} more: ` + normalHeading; } return normalHeading; diff --git a/frontend/farm_designer/map/actions.ts b/frontend/farm_designer/map/actions.ts index 4468704e7..0bf78a728 100644 --- a/frontend/farm_designer/map/actions.ts +++ b/frontend/farm_designer/map/actions.ts @@ -36,7 +36,7 @@ const addOrRemoveFromGroup = const group = fetchGroupFromUrl(resources); const point = resources.references[clickedPlantUuid] as TaggedPoint | undefined; - if (group && point && point.body.id) { + if (group && point?.body.id) { type Body = (typeof group)["body"]; const nextGroup: Body = ({ ...group.body, @@ -54,7 +54,7 @@ const addOrRemoveFromSelection = (clickedPlantUuid: UUID, selectedPlants: UUID[] | undefined) => { const nextSelected = (selectedPlants || []).filter(uuid => uuid !== clickedPlantUuid); - if (!(selectedPlants && selectedPlants.includes(clickedPlantUuid))) { + if (!(selectedPlants?.includes(clickedPlantUuid))) { nextSelected.push(clickedPlantUuid); } return selectPlant(nextSelected); diff --git a/frontend/farm_designer/map/layers/farmbot/bot_trail.tsx b/frontend/farm_designer/map/layers/farmbot/bot_trail.tsx index ec2c5379b..967ca81a9 100644 --- a/frontend/farm_designer/map/layers/farmbot/bot_trail.tsx +++ b/frontend/farm_designer/map/layers/farmbot/bot_trail.tsx @@ -21,7 +21,7 @@ function getNewTrailArray(update: TrailRecord, watering: boolean): TrailRecord[] const arr: TrailRecord[] = JSON.parse(get(sessionStorage, key, "[]")); if (arr.length > (trailLength - 1)) { arr.shift(); } // max length reached const last = arr[arr.length - 1]; // most recent item in array - if (update && update.coord && + if (update?.coord && (!last || !isEqual(last.coord, update.coord))) { // coordinate comparison arr.push(update); // unique addition } else { // nothing new to add, increase water circle size if watering diff --git a/frontend/farm_designer/plants/plant_info.tsx b/frontend/farm_designer/plants/plant_info.tsx index 378f1295c..91bf93cda 100644 --- a/frontend/farm_designer/plants/plant_info.tsx +++ b/frontend/farm_designer/plants/plant_info.tsx @@ -7,7 +7,7 @@ import { TaggedPlant } from "../map/interfaces"; import { DesignerPanel, DesignerPanelHeader } from "../designer_panel"; import { t } from "../../i18next_wrapper"; import { EditPlantInfoProps, PlantOptions } from "../interfaces"; -import { isString, isUndefined } from "lodash"; +import { isString } from "lodash"; import { history, getPathArray } from "../../history"; import { destroy, edit, save } from "../../api/crud"; import { BooleanSetting } from "../../session_keys"; @@ -20,7 +20,7 @@ export class RawPlantInfo extends React.Component { get confirmDelete() { const confirmSetting = this.props.getConfigValue( BooleanSetting.confirm_plant_deletion); - return isUndefined(confirmSetting) ? true : confirmSetting; + return confirmSetting ?? true; } destroy = (plantUUID: string) => { diff --git a/frontend/farm_designer/point_groups/__tests__/point_group_sort_selector_test.tsx b/frontend/farm_designer/point_groups/__tests__/point_group_sort_selector_test.tsx index a89de6439..49b25b5d0 100644 --- a/frontend/farm_designer/point_groups/__tests__/point_group_sort_selector_test.tsx +++ b/frontend/farm_designer/point_groups/__tests__/point_group_sort_selector_test.tsx @@ -51,7 +51,7 @@ describe("", () => { const sort = (sortType: PointGroupSortType): string[] => { const array = SORT_OPTIONS[sortType](plants as TaggedPlant[]); - return array.map(x => x && x.body && (x.body.name || "NA")); + return array.map(x => x?.body?.name || "NA"); }; it("sorts randomly", () => { diff --git a/frontend/farm_designer/saved_gardens/garden_edit.tsx b/frontend/farm_designer/saved_gardens/garden_edit.tsx index 26f301bcc..d95373502 100644 --- a/frontend/farm_designer/saved_gardens/garden_edit.tsx +++ b/frontend/farm_designer/saved_gardens/garden_edit.tsx @@ -66,7 +66,7 @@ export const mapStateToProps = (props: Everything): EditGardenProps => { const savedGarden = findSavedGardenByUrl(props.resources.index); return { savedGarden, - gardenIsOpen: !!(savedGarden && savedGarden.uuid === openedSavedGarden), + gardenIsOpen: !!(savedGarden?.uuid === openedSavedGarden), dispatch: props.dispatch, plantPointerCount: selectAllPlantPointers(props.resources.index).length, }; diff --git a/frontend/farm_designer/settings.tsx b/frontend/farm_designer/settings.tsx index 2f499a870..16b187f26 100644 --- a/frontend/farm_designer/settings.tsx +++ b/frontend/farm_designer/settings.tsx @@ -71,7 +71,7 @@ const Setting = (props: SettingProps) => { toggleValue={props.invert ? !value : value} toggleAction={() => { props.dispatch(setWebAppConfigValue(setting, !value)); - callback && callback(); + callback?.(); }} title={`${t("toggle")} ${title}`} customText={{ textFalse: t("off"), textTrue: t("on") }} />} diff --git a/frontend/farm_designer/tools/add_tool_slot.tsx b/frontend/farm_designer/tools/add_tool_slot.tsx index 697ae22f8..23de61eb3 100644 --- a/frontend/farm_designer/tools/add_tool_slot.tsx +++ b/frontend/farm_designer/tools/add_tool_slot.tsx @@ -100,7 +100,7 @@ export class RawAddToolSlot : "initializing"} - ; + ; } } diff --git a/frontend/farmware/farmware_config_menu.tsx b/frontend/farmware/farmware_config_menu.tsx index 0954e1422..bcffab66e 100644 --- a/frontend/farmware/farmware_config_menu.tsx +++ b/frontend/farmware/farmware_config_menu.tsx @@ -24,7 +24,7 @@ export function FarmwareConfigMenu(props: FarmwareConfigMenuProps) { className="fb-button gray fa fa-download" onClick={() => { const p = getDevice().installFirstPartyFarmware(); - p && p.catch(commandErr("Farmware installation")); + p?.catch(commandErr("Farmware installation")); }} disabled={props.firstPartyFwsInstalled} /> diff --git a/frontend/farmware/farmware_forms.tsx b/frontend/farmware/farmware_forms.tsx index af067ec6d..7774d2683 100644 --- a/frontend/farmware/farmware_forms.tsx +++ b/frontend/farmware/farmware_forms.tsx @@ -108,6 +108,6 @@ export function FarmwareForm(props: FarmwareFormProps): JSX.Element { /** Determine if a Farmware has requested inputs. */ export function needsFarmwareForm(farmware: FarmwareManifestInfo): Boolean { - const needsWidget = farmware.config && farmware.config.length > 0; + const needsWidget = farmware.config?.length > 0; return needsWidget; } diff --git a/frontend/farmware/farmware_info.tsx b/frontend/farmware/farmware_info.tsx index 53b42052f..6b0a3351c 100644 --- a/frontend/farmware/farmware_info.tsx +++ b/frontend/farmware/farmware_info.tsx @@ -57,7 +57,7 @@ const PendingInstallNameError = }): JSX.Element => { const installation: TaggedFarmwareInstallation | undefined = installations.filter(x => x.body.url === url)[0]; - const packageError = installation && installation.body.package_error; + const packageError = installation?.body.package_error; return (url && installation && packageError) ?
diff --git a/frontend/farmware/images/photos.tsx b/frontend/farmware/images/photos.tsx index e88c56cd0..6436973a2 100644 --- a/frontend/farmware/images/photos.tsx +++ b/frontend/farmware/images/photos.tsx @@ -116,7 +116,7 @@ export class Photos extends React.Component { deletePhoto = () => { const img = this.props.currentImage || this.props.images[0]; - if (img && img.uuid) { + if (img?.uuid) { this.props.dispatch(destroy(img.uuid)) .then(() => success(t("Image Deleted."))) .catch(() => error(t("Could not delete image."))); diff --git a/frontend/farmware/weed_detector/image_workspace.tsx b/frontend/farmware/weed_detector/image_workspace.tsx index 45b36de37..73196112e 100644 --- a/frontend/farmware/weed_detector/image_workspace.tsx +++ b/frontend/farmware/weed_detector/image_workspace.tsx @@ -61,7 +61,7 @@ export class ImageWorkspace extends React.Component { maybeProcessPhoto = () => { const img = this.props.currentImage || this.props.images[0]; - if (img && img.body.id) { + if (img?.body.id) { this.props.onProcessPhoto(img.body.id); } }; diff --git a/frontend/folders/component.tsx b/frontend/folders/component.tsx index def2ba081..52736d29a 100644 --- a/frontend/folders/component.tsx +++ b/frontend/folders/component.tsx @@ -80,7 +80,7 @@ export const FolderListItem = (props: FolderItemProps) => { onMouseUp={() => props.toggleSequenceMove(sequence.uuid)} />
- ; + ; }; const ToggleFolderBtn = (props: ToggleFolderBtnProps) => { diff --git a/frontend/folders/search_folder_tree.ts b/frontend/folders/search_folder_tree.ts index f1c7c89fa..d090a4466 100644 --- a/frontend/folders/search_folder_tree.ts +++ b/frontend/folders/search_folder_tree.ts @@ -1,5 +1,4 @@ import { TaggedResource, TaggedSequence } from "farmbot"; - import { RootFolderNode, FolderUnion } from "./constants"; export interface FolderSearchProps { diff --git a/frontend/link.tsx b/frontend/link.tsx index ac1296cbc..241621c96 100644 --- a/frontend/link.tsx +++ b/frontend/link.tsx @@ -18,7 +18,7 @@ export const clickHandler = /** BEGIN LEGACY SHIMS */ const { onClick, to } = props; navigate(maybeStripLegacyUrl(to)); - onClick && onClick(e); + onClick?.(e); }; export class Link extends React.Component { diff --git a/frontend/messages/cards.tsx b/frontend/messages/cards.tsx index e958759f6..0dcf9cce9 100644 --- a/frontend/messages/cards.tsx +++ b/frontend/messages/cards.tsx @@ -180,7 +180,7 @@ const FirmwareChoiceTable = () => export const changeFirmwareHardware = (dispatch: Function | undefined) => (ddi: DropDownItem) => { if (isFwHardwareValue(ddi.value)) { - dispatch && dispatch(updateConfig({ firmware_hardware: ddi.value })); + dispatch?.(updateConfig({ firmware_hardware: ddi.value })); } }; diff --git a/frontend/nav/compute_editor_url_from_state.ts b/frontend/nav/compute_editor_url_from_state.ts index fc87db08d..d068d0804 100644 --- a/frontend/nav/compute_editor_url_from_state.ts +++ b/frontend/nav/compute_editor_url_from_state.ts @@ -11,7 +11,7 @@ export const computeEditorUrlFromState = : resources.consumers.regimens.currentRegimen; const r = resources.index.references[current || ""]; const base = `/app/${resource === "Sequence" ? "sequences" : "regimens"}/`; - if (r && r.kind == resource) { + if (r?.kind == resource) { return base + urlFriendly(r.body.name); } else { return base; diff --git a/frontend/nav/index.tsx b/frontend/nav/index.tsx index 25c08c315..ba2ba0a6a 100644 --- a/frontend/nav/index.tsx +++ b/frontend/nav/index.tsx @@ -59,7 +59,7 @@ export class NavBar extends React.Component> { BooleanSetting.disable_emergency_unlock_confirmation)} /> AccountMenu = () => { - const hasName = this.props.user && this.props.user.body.name; + const hasName = this.props.user?.body.name; const firstName = hasName ? `${hasName.split(" ")[0].slice(0, 9)} ▾` : `${t("Menu")} ▾`; return
diff --git a/frontend/open_farm/cached_crop.ts b/frontend/open_farm/cached_crop.ts index 790e5a22d..019ca787b 100644 --- a/frontend/open_farm/cached_crop.ts +++ b/frontend/open_farm/cached_crop.ts @@ -43,10 +43,7 @@ const promiseCache: Dictionary>> = {}; const cacheTheIcon = (slug: string) => (resp: AxiosResponse): OFIcon => { - if (resp - && resp.data - && resp.data.data - && resp.data.data.attributes) { + if (resp?.data?.data?.attributes) { const icon = { slug: resp.data.data.attributes.slug, spread: resp.data.data.attributes.spread, diff --git a/frontend/redux/middlewares.ts b/frontend/redux/middlewares.ts index aeb2d7ee1..8e1332e4c 100644 --- a/frontend/redux/middlewares.ts +++ b/frontend/redux/middlewares.ts @@ -35,7 +35,7 @@ export function getMiddleware(env: EnvName) { .map((mwc) => mwc.fn); // tslint:disable-next-line:no-any const wow = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__; - const dtCompose = wow && wow({ + const dtCompose = wow?.({ actionsBlacklist: [ Actions.NETWORK_EDGE_CHANGE, Actions.PING_NO, diff --git a/frontend/redux/refilter_logs_middleware.ts b/frontend/redux/refilter_logs_middleware.ts index b61d1d1c7..8e26fae44 100644 --- a/frontend/redux/refilter_logs_middleware.ts +++ b/frontend/redux/refilter_logs_middleware.ts @@ -12,10 +12,8 @@ const WEB_APP_CONFIG: ResourceName = "WebAppConfig"; * resources, downloading the filtered log list as required from the API. */ // tslint:disable-next-line:no-any export const fn: Middleware = () => (dispatch) => (action: any) => { - const needsRefresh = action - && action.payload - && action.type === Actions.SAVE_RESOURCE_OK - && action.payload.kind === WEB_APP_CONFIG; + const needsRefresh = action?.payload?.kind === WEB_APP_CONFIG + && action.type === Actions.SAVE_RESOURCE_OK; needsRefresh && throttledLogRefresh(dispatch); diff --git a/frontend/redux/revert_to_english_middleware.ts b/frontend/redux/revert_to_english_middleware.ts index 8b5278e94..f0ca9a252 100644 --- a/frontend/redux/revert_to_english_middleware.ts +++ b/frontend/redux/revert_to_english_middleware.ts @@ -23,16 +23,11 @@ const WEB_APP_CONFIG: ResourceName = "WebAppConfig"; // tslint:disable-next-line:no-any const fn: Middleware = () => (dispatch) => (action: any) => { const x: DeepPartial> = action; - if (x - && x.type === Actions.RESOURCE_READY - && x.payload - && x.payload.body + if (x?.type === Actions.RESOURCE_READY + && x.payload?.body && x.payload.kind === WEB_APP_CONFIG) { const conf = arrayUnwrap(x.payload.body); - conf - && conf.body - && conf.body.disable_i18n - && revertToEnglish(); + conf?.body?.disable_i18n && revertToEnglish(); } return dispatch(action); diff --git a/frontend/redux/subscribers.ts b/frontend/redux/subscribers.ts index bee3aad58..5f867e461 100644 --- a/frontend/redux/subscribers.ts +++ b/frontend/redux/subscribers.ts @@ -12,10 +12,9 @@ export function dontStopThem() { } const shouldStop = (allResources: TaggedResource[], config: TaggedWebAppConfig | undefined) => { const loggedIn = !!localStorage.getItem("session"); - const discardUnsaved = config && config.body.discard_unsaved; + const discardUnsaved = config?.body.discard_unsaved; const sequenceResources = allResources.filter(r => r.kind === "Sequence"); - const discardUnsavedSequences = - config && config.body.discard_unsaved_sequences; + const discardUnsavedSequences = config?.body.discard_unsaved_sequences; /** * For the unsaved notification to show, a user must: @@ -59,6 +58,6 @@ export function registerSubscribers(store: Store) { subscriptions.forEach(function (s) { ENV_LIST.includes && ENV_LIST.includes(s.env) && - store.subscribe(() => s.fn && s.fn(store.getState())); + store.subscribe(() => s.fn?.(store.getState())); }); } diff --git a/frontend/redux/version_tracker_middleware.ts b/frontend/redux/version_tracker_middleware.ts index bdfda100f..4af0b8aed 100644 --- a/frontend/redux/version_tracker_middleware.ts +++ b/frontend/redux/version_tracker_middleware.ts @@ -23,7 +23,7 @@ const fn: MW = // tslint:disable-next-line:no-any (action: any) => { const fbos = getVersionFromState(store.getState()); - window.Rollbar && window.Rollbar.configure({ payload: { fbos } }); + window.Rollbar?.configure({ payload: { fbos } }); return dispatch(action); }; diff --git a/frontend/regimens/bulk_scheduler/index.tsx b/frontend/regimens/bulk_scheduler/index.tsx index 8d211a214..4faa4a630 100644 --- a/frontend/regimens/bulk_scheduler/index.tsx +++ b/frontend/regimens/bulk_scheduler/index.tsx @@ -17,7 +17,7 @@ const BAD_UUID = "WARNING: Not a sequence UUID."; export class BulkScheduler extends React.Component { selected = (): DropDownItem => { const s = this.props.selectedSequence; - return (s && s.body.id) + return (s?.body.id) ? { label: s.body.name, value: s.uuid } : NULL_CHOICE; }; @@ -66,7 +66,7 @@ export class BulkScheduler extends React.Component { render() { const { dispatch, weeks, sequences } = this.props; - const active = !!(sequences && sequences.length); + const active = !!(sequences?.length); return
", () => { p.dispatch = jest.fn(() => Promise.resolve()); const wrapper = mount(); clickButton(wrapper, 2, "delete"); - const expectedUuid = p.current && p.current.uuid; + const expectedUuid = p.current?.uuid; expect(destroy).toHaveBeenCalledWith(expectedUuid); }); @@ -74,7 +74,7 @@ describe("", () => { const p = fakeProps(); const wrapper = mount(); clickButton(wrapper, 0, "save", { partial_match: true }); - const expectedUuid = p.current && p.current.uuid; + const expectedUuid = p.current?.uuid; expect(save).toHaveBeenCalledWith(expectedUuid); }); }); diff --git a/frontend/resources/__tests__/selectors_test.ts b/frontend/resources/__tests__/selectors_test.ts index 84accec40..49f4a5678 100644 --- a/frontend/resources/__tests__/selectors_test.ts +++ b/frontend/resources/__tests__/selectors_test.ts @@ -177,7 +177,7 @@ describe("maybeGetSequence", () => { const i = buildResourceIndex([s]); const result = Selector.maybeGetSequence(i.index, s.uuid); expect(result).toBeTruthy(); - result && expect(result.uuid).toBe(s.uuid); + expect(result?.uuid).toBe(s.uuid); }); }); diff --git a/frontend/resources/reducer_support.ts b/frontend/resources/reducer_support.ts index ebc0385e5..9b43e5b08 100644 --- a/frontend/resources/reducer_support.ts +++ b/frontend/resources/reducer_support.ts @@ -188,7 +188,7 @@ const reindexAllSequences = (i: ResourceIndex) => { const mapper = reindexSequences(i); betterCompact(Object.keys(i.byKind["Sequence"]).map(uuid => { const resource = i.references[uuid]; - return (resource && resource.kind == "Sequence") ? resource : undefined; + return (resource?.kind == "Sequence") ? resource : undefined; })).map(mapper); }; @@ -283,7 +283,7 @@ const AFTER_HOOKS: IndexerHook = { FbosConfig: (i) => { const conf = getFbosConfig(i); - if (conf && conf.body.boot_sequence_id) { + if (conf?.body.boot_sequence_id) { const { boot_sequence_id } = conf.body; const tracker = i.inUse["Sequence.FbosConfig"]; const uuid = i.byKindAndId[joinKindAndId("Sequence", boot_sequence_id)]; @@ -345,7 +345,7 @@ export const indexUpsert: IndexUpsert = (db, resources, strategy) => { const { kind } = arrayUnwrap(resources); // Clean up indexes (if needed) const before = BEFORE_HOOKS[kind]; - before && before(db, strategy); + before?.(db, strategy); // Run indexers ups.map(callback => { @@ -354,14 +354,14 @@ export const indexUpsert: IndexUpsert = (db, resources, strategy) => { // Finalize indexing (if needed) const after = AFTER_HOOKS[kind]; - after && after(db, strategy); + after?.(db, strategy); }; export function indexRemove(db: ResourceIndex, resource: TaggedResource) { downs.map(callback => arrayWrap(resource).map(r => callback(r, db))); // Finalize indexing (if needed) const after = AFTER_HOOKS[resource.kind]; - after && after(db, "ongoing"); + after?.(db, "ongoing"); } export const beforeEach = (state: RestResources, diff --git a/frontend/resources/selectors.ts b/frontend/resources/selectors.ts index 3f7044b5c..2085525ce 100644 --- a/frontend/resources/selectors.ts +++ b/frontend/resources/selectors.ts @@ -77,7 +77,7 @@ export function findPointerByTypeAndId(index: ResourceIndex, const uuid = "" + index.byKindAndId[pni]; const resource = index.references[uuid]; - if (resource && resource.kind === "Point") { + if (resource?.kind === "Point") { return resource; } else { // We might have a sequence dependency leak if this exception is ever @@ -204,7 +204,7 @@ export function maybeGetTimeSettings(index: ResourceIndex): TimeSettings { export function maybeGetDevice(index: ResourceIndex): TaggedDevice | undefined { const dev = index.references[Object.keys(index.byKind.Device)[0] || "nope"]; - return (dev && dev.kind === "Device") ? + return (dev?.kind === "Device") ? dev : undefined; } @@ -227,7 +227,7 @@ export function maybeFetchUser(index: ResourceIndex): if (user && sanityCheck(user) && list.length > 1) { throw new Error("PROBLEM: Expected 1 user. Got: " + list.length); } - if ((list.length === 1) && user && user.kind === "User") { + if ((list.length === 1) && user?.kind === "User") { return user; } else { return undefined; diff --git a/frontend/resources/sequence_meta.ts b/frontend/resources/sequence_meta.ts index 2a58274de..be03fff20 100644 --- a/frontend/resources/sequence_meta.ts +++ b/frontend/resources/sequence_meta.ts @@ -50,7 +50,7 @@ export const determineVector = return ts ? ts.body : undefined; case "identifier": const variable = maybeFindVariable(node.args.label, resources, uuid); - return variable && variable.vector; + return variable?.vector; } return undefined; }; diff --git a/frontend/sequences/__tests__/sequence_editor_middle_active_test.tsx b/frontend/sequences/__tests__/sequence_editor_middle_active_test.tsx index b6d641419..d51394e5b 100644 --- a/frontend/sequences/__tests__/sequence_editor_middle_active_test.tsx +++ b/frontend/sequences/__tests__/sequence_editor_middle_active_test.tsx @@ -127,7 +127,7 @@ describe("", () => { p.dispatch = dispatch; const wrapper = mount(); const props = wrapper.find("DropArea").props() as DropAreaProps; - props.callback && props.callback("key"); + props.callback?.("key"); dispatch.mock.calls[0][0](() => ({ value: 1, intent: "step_splice", draggerId: 2 })); expect(splice).toHaveBeenCalledWith(expect.objectContaining({ diff --git a/frontend/sequences/sequence_editor_middle_active.tsx b/frontend/sequences/sequence_editor_middle_active.tsx index 8a8c18886..0f1436d79 100644 --- a/frontend/sequences/sequence_editor_middle_active.tsx +++ b/frontend/sequences/sequence_editor_middle_active.tsx @@ -148,7 +148,7 @@ const SequenceBtnGroup = ({ onClick={() => { const confirm = getWebAppConfigValue( BooleanSetting.confirm_sequence_deletion); - const force = isUndefined(confirm) ? false : !confirm; + const force = !(confirm ?? true); dispatch(destroy(sequence.uuid, force)) .then(() => push("/app/sequences/")); }}> diff --git a/frontend/sequences/state_to_props.ts b/frontend/sequences/state_to_props.ts index 7a4902cf3..b7377fe41 100644 --- a/frontend/sequences/state_to_props.ts +++ b/frontend/sequences/state_to_props.ts @@ -19,7 +19,7 @@ import { export function mapStateToProps(props: Everything): Props { const uuid = props.resources.consumers.sequences.current; const sequence = uuid ? findSequence(props.resources.index, uuid) : undefined; - sequence && (sequence.body.body || []).map(x => getStepTag(x)); + (sequence?.body.body || []).map(x => getStepTag(x)); const fwConfig = validFwConfig(getFirmwareConfig(props.resources.index)); const { mcu_params } = props.bot.hardware; diff --git a/frontend/sequences/step_tiles/__tests__/tile_execute_script_test.tsx b/frontend/sequences/step_tiles/__tests__/tile_execute_script_test.tsx index e94b49fc7..a9980223e 100644 --- a/frontend/sequences/step_tiles/__tests__/tile_execute_script_test.tsx +++ b/frontend/sequences/step_tiles/__tests__/tile_execute_script_test.tsx @@ -83,7 +83,7 @@ describe("", () => { it("shows special 1st-party Farmware name", () => { const p = fakeProps(); (p.currentStep as ExecuteScript).args.label = "plant-detection"; - p.farmwareData && p.farmwareData.farmwareNames.push("plant-detection"); + p.farmwareData?.farmwareNames.push("plant-detection"); const wrapper = mount(); expect(wrapper.find("label").length).toEqual(1); expect(wrapper.text()).toContain("Weed Detector"); diff --git a/frontend/sequences/step_tiles/tile_execute_script.tsx b/frontend/sequences/step_tiles/tile_execute_script.tsx index 5d764b23f..27a6187bd 100644 --- a/frontend/sequences/step_tiles/tile_execute_script.tsx +++ b/frontend/sequences/step_tiles/tile_execute_script.tsx @@ -54,7 +54,7 @@ export function TileExecuteScript(props: StepParams) { /** Configs (inputs) from Farmware manifest for . */ const currentFarmwareConfigDefaults = (fwName: string): FarmwareConfig[] => { - return farmwareData && farmwareData.farmwareConfigs[fwName] + return farmwareData?.farmwareConfigs[fwName] ? farmwareData.farmwareConfigs[fwName] : []; }; diff --git a/frontend/sequences/step_tiles/tile_if/__tests__/index_test.tsx b/frontend/sequences/step_tiles/tile_if/__tests__/index_test.tsx index b9b5f9215..12c7f39a9 100644 --- a/frontend/sequences/step_tiles/tile_if/__tests__/index_test.tsx +++ b/frontend/sequences/step_tiles/tile_if/__tests__/index_test.tsx @@ -24,8 +24,8 @@ import { const fakeResourceIndex = buildResourceIndex(FAKE_RESOURCES).index; const fakeTaggedSequence = fakeResourceIndex .references[Object.keys(fakeResourceIndex.byKind.Sequence)[0]] as TaggedSequence; -const fakeId = fakeTaggedSequence && fakeTaggedSequence.body.id || 0; -const fakeName = fakeTaggedSequence && fakeTaggedSequence.body.name || ""; +const fakeId = fakeTaggedSequence.body.id || 0; +const fakeName = fakeTaggedSequence.body.name || ""; const expectedItem = { label: fakeName, value: fakeId }; function fakeProps(): IfParams { diff --git a/frontend/sync/actions.ts b/frontend/sync/actions.ts index edb942a24..3aa64e4df 100644 --- a/frontend/sync/actions.ts +++ b/frontend/sync/actions.ts @@ -30,7 +30,7 @@ export const newTaggedResource = (kind: T["kind"], return { kind: kind as TaggedResource["kind"], body: body as TaggedResource["body"], - uuid: generateUuid(body && body.id ? body.id : undefined, kind), + uuid: generateUuid(body?.id, kind), specialStatus } as T; }); diff --git a/frontend/tools/components/toolbay_list.tsx b/frontend/tools/components/toolbay_list.tsx index 7549d684e..76c722556 100644 --- a/frontend/tools/components/toolbay_list.tsx +++ b/frontend/tools/components/toolbay_list.tsx @@ -11,7 +11,7 @@ export class ToolBayList extends React.Component { ToolSlotListItem = (slot: TaggedToolSlotPointer, index: number) => { const { getToolByToolSlotUUID } = this.props; const tool = getToolByToolSlotUUID(slot.uuid); - const name = (tool && tool.body.name) || t("None"); + const name = (tool?.body.name) || t("None"); return {slot.body.gantry_mounted ? t("Gantry") : slot.body.x} diff --git a/frontend/ui/back_arrow.tsx b/frontend/ui/back_arrow.tsx index b8e9d5b44..f317111bf 100644 --- a/frontend/ui/back_arrow.tsx +++ b/frontend/ui/back_arrow.tsx @@ -6,7 +6,7 @@ interface BackArrowProps { export function BackArrow(props: BackArrowProps) { const onClick = () => { history.back(); - props.onClick && props.onClick(); + props.onClick?.(); }; return diff --git a/frontend/ui/blurable_input.tsx b/frontend/ui/blurable_input.tsx index 3b89ff3cb..e2d72a659 100644 --- a/frontend/ui/blurable_input.tsx +++ b/frontend/ui/blurable_input.tsx @@ -47,7 +47,7 @@ export class BlurableInput extends React.Component> { withinLimits = (options?: { toasts?: boolean }): boolean => { const onError = (msg: string) => { this.setState({ error: msg }); - options && options.toasts && error(msg); + options?.toasts && error(msg); }; if (this.props.type === "number") { diff --git a/frontend/util/__tests__/util_test.ts b/frontend/util/__tests__/util_test.ts index cf1e89ea4..fcd4f2632 100644 --- a/frontend/util/__tests__/util_test.ts +++ b/frontend/util/__tests__/util_test.ts @@ -131,18 +131,6 @@ describe("util", () => { }); }); - describe("isUndefined()", () => { - it("undefined", () => { - const result = Util.isUndefined(undefined); - expect(result).toBeTruthy(); - }); - - it("defined", () => { - const result = Util.isUndefined({}); - expect(result).toBeFalsy(); - }); - }); - describe("randomColor()", () => { it("only picks valid colors", () => { times(Util.colors.length * 1.5, () => diff --git a/frontend/util/errors.ts b/frontend/util/errors.ts index eec02cc82..0e7a43488 100644 --- a/frontend/util/errors.ts +++ b/frontend/util/errors.ts @@ -28,7 +28,7 @@ export function prettyPrintApiErrors(err: AxiosErrorResponse) { function safelyFetchErrors(err: AxiosErrorResponse): Dictionary { // In case the interpreter gives us an oddball error message. - if (err && err.response && err.response.data) { + if (err.response?.data) { return err.response.data; } else { return { @@ -42,7 +42,7 @@ export function bail(message: string): never { } export const catchErrors = (error: Error) => { - if (window.Rollbar && window.Rollbar.error) { + if (window.Rollbar?.error) { window.Rollbar.error(error); } else { throw error; diff --git a/frontend/util/util.ts b/frontend/util/util.ts index 45ae09d9f..517c79924 100644 --- a/frontend/util/util.ts +++ b/frontend/util/util.ts @@ -14,7 +14,6 @@ import { sortBy, merge, isNumber, - isUndefined as lodashIsUndefined } from "lodash"; import { t } from "../i18next_wrapper"; @@ -88,10 +87,6 @@ export type CowardlyDictionary = Dictionary; */ export const NOT_SAVED = -1; -export function isUndefined(x: object | undefined): x is undefined { - return lodashIsUndefined(x); -} - /** Better than Array.proto.filter and compact() because the type checker * knows what's going on. */ @@ -190,7 +185,7 @@ export function validBotLocationData( */ export function validFwConfig(config: TaggedFirmwareConfig | undefined): TaggedFirmwareConfig["body"] | undefined { - return (config && config.body.api_migrated) + return (config?.body.api_migrated) ? config.body : undefined; } @@ -200,7 +195,7 @@ export function validFwConfig(config: TaggedFirmwareConfig | undefined): */ export function validFbosConfig( config: TaggedFbosConfig | undefined): TaggedFbosConfig["body"] | undefined { - return (config && config.body.api_migrated) + return (config?.body.api_migrated) ? config.body : undefined; } @@ -232,4 +227,4 @@ export const parseIntInput = (input: string): number => { export const timeFormatString = (timeSettings: TimeSettings | undefined): string => - (timeSettings && timeSettings.hour24) ? "H:mm" : "h:mma"; + (timeSettings?.hour24) ? "H:mm" : "h:mma";