diff --git a/lib/gui/app/app.ts b/lib/gui/app/app.ts index 5a4e3bec..1119ae34 100644 --- a/lib/gui/app/app.ts +++ b/lib/gui/app/app.ts @@ -264,7 +264,8 @@ function updateDriveProgress( // @ts-ignore const driveInMap = drives[drive.device]; if (driveInMap) { - driveInMap.progress = progress; + // @ts-ignore + drives[drive.device] = { ...driveInMap, progress }; setDrives(drives); } } diff --git a/lib/gui/app/components/drive-selector/DriveSelectorModal.tsx b/lib/gui/app/components/drive-selector/DriveSelectorModal.tsx deleted file mode 100644 index 477fb439..00000000 --- a/lib/gui/app/components/drive-selector/DriveSelectorModal.tsx +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright 2019 balena.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Drive as DrivelistDrive } from 'drivelist'; -import * as _ from 'lodash'; -import * as React from 'react'; -import { Modal } from 'rendition'; - -import { - COMPATIBILITY_STATUS_TYPES, - getDriveImageCompatibilityStatuses, - hasListDriveImageCompatibilityStatus, - isDriveValid, -} from '../../../../shared/drive-constraints'; -import { bytesToClosestUnit } from '../../../../shared/units'; -import { getDrives, hasAvailableDrives } from '../../models/available-drives'; -import * as selectionState from '../../models/selection-state'; -import { store } from '../../models/store'; -import * as analytics from '../../modules/analytics'; -import { open as openExternal } from '../../os/open-external/services/open-external'; - -import RaspberrypiSvg from '../../../assets/raspberrypi.svg'; - -/** - * @summary Determine if we can change a drive's selection state - */ -function shouldChangeDriveSelectionState(drive: DrivelistDrive) { - return isDriveValid(drive, selectionState.getImage()); -} - -/** - * @summary Toggle a drive selection - */ -function toggleDrive(drive: DrivelistDrive) { - const canChangeDriveSelectionState = shouldChangeDriveSelectionState(drive); - - if (canChangeDriveSelectionState) { - analytics.logEvent('Toggle drive', { - drive, - previouslySelected: selectionState.isDriveSelected(drive.device), - }); - - selectionState.toggleDrive(drive.device); - } -} - -/** - * @summary Get a drive's compatibility status object(s) - * - * @description - * Given a drive, return its compatibility status with the selected image, - * containing the status type (ERROR, WARNING), and accompanying - * status message. - */ -function getDriveStatuses( - drive: DrivelistDrive, -): Array<{ type: number; message: string }> { - return getDriveImageCompatibilityStatuses(drive, selectionState.getImage()); -} - -function keyboardToggleDrive( - drive: DrivelistDrive, - event: React.KeyboardEvent, -) { - const ENTER = 13; - const SPACE = 32; - if (_.includes([ENTER, SPACE], event.keyCode)) { - toggleDrive(drive); - } -} - -interface DriverlessDrive { - link: string; - linkTitle: string; - linkMessage: string; -} - -export function DriveSelectorModal({ close }: { close: () => void }) { - const defaultMissingDriversModalState: { drive?: DriverlessDrive } = {}; - const [missingDriversModal, setMissingDriversModal] = React.useState( - defaultMissingDriversModalState, - ); - const [drives, setDrives] = React.useState(getDrives()); - - React.useEffect(() => { - const unsubscribe = store.subscribe(() => { - setDrives(getDrives()); - }); - return unsubscribe; - }); - - /** - * @summary Prompt the user to install missing usbboot drivers - */ - function installMissingDrivers(drive: { - link: string; - linkTitle: string; - linkMessage: string; - }) { - if (drive.link) { - analytics.logEvent('Open driver link modal', { - url: drive.link, - }); - setMissingDriversModal({ drive }); - } - } - - /** - * @summary Select a drive and close the modal - */ - async function selectDriveAndClose(drive: DrivelistDrive) { - const canChangeDriveSelectionState = await shouldChangeDriveSelectionState( - drive, - ); - - if (canChangeDriveSelectionState) { - selectionState.selectDrive(drive.device); - - analytics.logEvent('Drive selected (double click)'); - - close(); - } - } - - const hasStatus = hasListDriveImageCompatibilityStatus( - selectionState.getSelectedDrives(), - selectionState.getImage(), - ); - - return ( - - - - {missingDriversModal.drive !== undefined && ( - setMissingDriversModal({})} - done={() => { - try { - if (missingDriversModal.drive !== undefined) { - openExternal(missingDriversModal.drive.link); - } - } catch (error) { - analytics.logException(error); - } finally { - setMissingDriversModal({}); - } - }} - action={'Yes, continue'} - cancelButtonProps={{ - children: 'Cancel', - }} - children={ - missingDriversModal.drive.linkMessage || - `Etcher will open ${missingDriversModal.drive.link} in your browser` - } - > - )} - - ); -} diff --git a/lib/gui/app/components/drive-selector/styles/_drive-selector.scss b/lib/gui/app/components/drive-selector/styles/_drive-selector.scss deleted file mode 100644 index 809f693f..00000000 --- a/lib/gui/app/components/drive-selector/styles/_drive-selector.scss +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2016 balena.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -.modal-drive-selector-modal .modal-content { - width: 315px; - height: 320px; -} - -.modal-drive-selector-modal .modal-body { - padding-top: 0; - padding-bottom: 0; -} - -.modal-drive-selector-modal .list-group-item[disabled] { - cursor: not-allowed; -} - -.modal-drive-selector-modal { - - .list-group-item-footer:has(span) { - margin-top: 8px; - } - - .list-group-item-heading, - .list-group-item-text { - word-break: break-all; - } - - .list-group { - margin-bottom: 0; - } - - .list-group-item { - display: flex; - align-items: center; - border-left: 0; - border-right: 0; - border-radius: 0; - border-color: darken($palette-theme-light-background, 7%); - padding: 12px 0; - - .list-group-item-section-expanded { - flex-grow: 1; - margin-left: 15px; - } - - .list-group-item-section + .list-group-item-section { - margin-left: 10px; - display: inline-block; - vertical-align: middle; - } - - > .tick { - font-size: 11px; - } - - &:first-child { - border-top: 0; - } - - &[disabled] .list-group-item-heading { - color: $palette-theme-light-soft-foreground; - } - - .drive-init-progress { - appearance: none; - width: 100%; - height: 2.5px; - border: none; - border-radius: 50% 50%; - } - - .drive-init-progress::-webkit-progress-bar { - background-color: $palette-theme-default-background; - border: none; - outline: none; - } - - .drive-init-progress::-webkit-progress-value { - border-bottom: 1px solid darken($palette-theme-primary-background, 15); - background-color: $palette-theme-primary-background; - } - - } - - .list-group-item-heading { - font-size: 13px; - } - - .list-group-item-text { - line-height: 1; - font-size: 11px; - color: $palette-theme-light-soft-foreground; - } - - .word-keep { - word-break: keep-all; - } -} - diff --git a/lib/gui/app/components/flash-results/flash-results.tsx b/lib/gui/app/components/flash-results/flash-results.tsx index a171d6bf..4c7000a5 100644 --- a/lib/gui/app/components/flash-results/flash-results.tsx +++ b/lib/gui/app/components/flash-results/flash-results.tsx @@ -14,10 +14,12 @@ * limitations under the License. */ +import { faCheckCircle } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import * as _ from 'lodash'; import outdent from 'outdent'; import * as React from 'react'; -import { Txt } from 'rendition'; +import { Txt, Flex } from 'rendition'; import styled from 'styled-components'; import { left, position, space, top } from 'styled-system'; @@ -57,14 +59,20 @@ export function FlashResults({ ); return (
-
- -

Flash Complete!

-
+ + + + Flash Complete! + +
{_.map(results.devices, (quantity, type) => { return quantity ? ( diff --git a/lib/gui/app/components/progress-button/progress-button.tsx b/lib/gui/app/components/progress-button/progress-button.tsx index 541e98f3..62fcff92 100644 --- a/lib/gui/app/components/progress-button/progress-button.tsx +++ b/lib/gui/app/components/progress-button/progress-button.tsx @@ -35,6 +35,7 @@ const FlashProgressBar = styled(ProgressBar)` width: 220px; height: 12px; + margin-bottom: 6px; border-radius: 14px; font-size: 16px; line-height: 48px; @@ -50,6 +51,7 @@ interface ProgressButtonProps { disabled: boolean; cancel: () => void; callback: () => void; + warning?: boolean; } const colors = { @@ -80,8 +82,16 @@ export class ProgressButton extends React.PureComponent { }); if (this.props.active) { return ( -
- + <> + {status}  {position} @@ -92,12 +102,13 @@ export class ProgressButton extends React.PureComponent { background={colors[this.props.type]} value={this.props.percentage} /> -
+ ); } return ( diff --git a/lib/gui/app/components/settings/settings.tsx b/lib/gui/app/components/settings/settings.tsx index 1811d0e5..9b1bf1a2 100644 --- a/lib/gui/app/components/settings/settings.tsx +++ b/lib/gui/app/components/settings/settings.tsx @@ -19,7 +19,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import * as _ from 'lodash'; import * as os from 'os'; import * as React from 'react'; -import { Badge, Checkbox, Modal } from 'rendition'; +import { Checkbox, Modal } from 'rendition'; import { version } from '../../../../../package.json'; import * as settings from '../../models/settings'; @@ -92,23 +92,6 @@ async function getSettingsList(): Promise { name: 'updatesEnabled', label: 'Auto-updates enabled', }, - { - name: 'unsafeMode', - label: ( - - Unsafe mode{' '} - - Dangerous - - - ), - options: { - description: `Are you sure you want to turn this on? - You will be able to overwrite your system drives if you're not careful.`, - confirmLabel: 'Enable unsafe mode', - }, - hide: await settings.get('disableUnsafeMode'), - }, ]; } diff --git a/lib/gui/app/components/source-selector/source-selector.tsx b/lib/gui/app/components/source-selector/source-selector.tsx index f9d89177..0ec6b82d 100644 --- a/lib/gui/app/components/source-selector/source-selector.tsx +++ b/lib/gui/app/components/source-selector/source-selector.tsx @@ -473,7 +473,7 @@ export class SourceSelector extends React.Component<
} + fallback={} />
diff --git a/lib/gui/app/components/drive-selector/target-selector.tsx b/lib/gui/app/components/target-selector/target-selector-button.tsx similarity index 100% rename from lib/gui/app/components/drive-selector/target-selector.tsx rename to lib/gui/app/components/target-selector/target-selector-button.tsx diff --git a/lib/gui/app/components/target-selector/target-selector-modal.tsx b/lib/gui/app/components/target-selector/target-selector-modal.tsx new file mode 100644 index 00000000..ccff228c --- /dev/null +++ b/lib/gui/app/components/target-selector/target-selector-modal.tsx @@ -0,0 +1,489 @@ +/* + * Copyright 2019 balena.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + faChevronDown, + faExclamationTriangle, +} from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { scanner, sourceDestination } from 'etcher-sdk'; +import * as React from 'react'; +import { + Badge, + Table, + Txt, + Flex, + Link, + TableColumn, + ModalProps, +} from 'rendition'; +import styled from 'styled-components'; + +import { + getDriveImageCompatibilityStatuses, + hasListDriveImageCompatibilityStatus, + isDriveValid, + TargetStatus, + Image, +} from '../../../../shared/drive-constraints'; +import { compatibility } from '../../../../shared/messages'; +import { bytesToClosestUnit } from '../../../../shared/units'; +import { getDrives, hasAvailableDrives } from '../../models/available-drives'; +import { + getImage, + getSelectedDrives, + isDriveSelected, +} from '../../models/selection-state'; +import { store } from '../../models/store'; +import { logEvent, logException } from '../../modules/analytics'; +import { open as openExternal } from '../../os/open-external/services/open-external'; +import { Modal } from '../../styled-components'; + +import TargetSVGIcon from '../../../assets/tgt.svg'; + +interface UsbbootDrive extends sourceDestination.UsbbootDrive { + progress: number; +} + +interface DriverlessDrive { + displayName: string; // added in app.ts + description: string; + link: string; + linkTitle: string; + linkMessage: string; + linkCTA: string; +} + +type Target = scanner.adapters.DrivelistDrive | DriverlessDrive | UsbbootDrive; + +function isUsbbootDrive(drive: Target): drive is UsbbootDrive { + return (drive as UsbbootDrive).progress !== undefined; +} + +function isDriverlessDrive(drive: Target): drive is DriverlessDrive { + return (drive as DriverlessDrive).link !== undefined; +} + +function isDrivelistDrive( + drive: Target, +): drive is scanner.adapters.DrivelistDrive { + return typeof (drive as scanner.adapters.DrivelistDrive).size === 'number'; +} + +const ScrollableFlex = styled(Flex)` + overflow: auto; + + ::-webkit-scrollbar { + display: none; + } + + > div > div { + /* This is required for the sticky table header in TargetsTable */ + overflow-x: visible; + } +`; + +const TargetsTable = styled(({ refFn, ...props }) => { + return ( +
+ ref={refFn} {...props} /> +
+ ); +})` + [data-display='table-head'] [data-display='table-cell'] { + position: sticky; + top: 0; + background-color: ${(props) => props.theme.colors.quartenary.light}; + } + + [data-display='table-cell']:first-child { + padding-left: 15px; + } + + [data-display='table-cell']:last-child { + width: 150px; + } + + && [data-display='table-row'] > [data-display='table-cell'] { + padding: 6px 8px; + color: #2a506f; + } +`; + +function badgeShadeFromStatus(status: string) { + switch (status) { + case compatibility.containsImage(): + return 16; + case compatibility.system(): + return 5; + default: + return 14; + } +} + +const InitProgress = styled( + ({ + value, + ...props + }: { + value: number; + props?: React.ProgressHTMLAttributes; + }) => { + return ; + }, +)` + /* Reset the default appearance */ + appearance: none; + + ::-webkit-progress-bar { + width: 130px; + height: 4px; + background-color: #dde1f0; + border-radius: 14px; + } + + ::-webkit-progress-value { + background-color: #1496e1; + border-radius: 14px; + } +`; + +interface TargetSelectorModalProps extends Omit { + done: (targets: scanner.adapters.DrivelistDrive[]) => void; +} + +interface TargetSelectorModalState { + drives: Target[]; + image: Image; + missingDriversModal: { drive?: DriverlessDrive }; + selectedList: scanner.adapters.DrivelistDrive[]; + showSystemDrives: boolean; +} + +export class TargetSelectorModal extends React.Component< + TargetSelectorModalProps, + TargetSelectorModalState +> { + unsubscribe: () => void; + tableColumns: Array>; + + constructor(props: TargetSelectorModalProps) { + super(props); + + const defaultMissingDriversModalState: { drive?: DriverlessDrive } = {}; + const selectedList = getSelectedDrives(); + + this.state = { + drives: getDrives(), + image: getImage(), + missingDriversModal: defaultMissingDriversModalState, + selectedList, + showSystemDrives: false, + }; + + this.tableColumns = [ + { + field: 'description', + label: 'Name', + render: (description: string, drive: Target) => { + return isDrivelistDrive(drive) && drive.isSystem ? ( + + + {description} + + ) : ( + {description} + ); + }, + }, + { + field: 'description', + key: 'size', + label: 'Size', + render: (_description: string, drive: Target) => { + if (isDrivelistDrive(drive) && drive.size !== null) { + return bytesToClosestUnit(drive.size); + } + }, + }, + { + field: 'description', + key: 'link', + label: 'Location', + render: (_description: string, drive: Target) => { + return ( + + {drive.displayName} + {isDriverlessDrive(drive) && ( + <> + {' '} + -{' '} + + this.installMissingDrivers(drive)}> + {drive.linkCTA} + + + + )} + + ); + }, + }, + { + field: 'description', + key: 'extra', + // Space as empty string would use the field name as label + label: ' ', + render: (_description: string, drive: Target) => { + if (isUsbbootDrive(drive)) { + return this.renderProgress(drive.progress); + } else if (isDrivelistDrive(drive)) { + return this.renderStatuses( + getDriveImageCompatibilityStatuses(drive, this.state.image), + ); + } + }, + }, + ]; + } + + private driveShouldBeDisabled(drive: Target, image: any) { + return ( + isUsbbootDrive(drive) || + isDriverlessDrive(drive) || + !isDriveValid(drive, image) + ); + } + + private getDisplayedTargets(targets: Target[]): Target[] { + return targets.filter((drive) => { + return ( + isUsbbootDrive(drive) || + isDriverlessDrive(drive) || + isDriveSelected(drive.device) || + this.state.showSystemDrives || + !drive.isSystem + ); + }); + } + + private getDisabledTargets(drives: Target[], image: any): string[] { + return drives + .filter((drive) => this.driveShouldBeDisabled(drive, image)) + .map((drive) => drive.displayName); + } + + private renderProgress(progress: number) { + return ( + + Initializing device + + + ); + } + + private renderStatuses(statuses: TargetStatus[]) { + return ( + // the column render fn expects a single Element + <> + {statuses.map((status) => { + const badgeShade = badgeShadeFromStatus(status.message); + return ( + + {status.message} + + ); + })} + + ); + } + + private installMissingDrivers(drive: DriverlessDrive) { + if (drive.link) { + logEvent('Open driver link modal', { + url: drive.link, + }); + this.setState({ missingDriversModal: { drive } }); + } + } + + componentDidMount() { + this.unsubscribe = store.subscribe(() => { + const drives = getDrives(); + const image = getImage(); + this.setState({ + drives, + image, + selectedList: getSelectedDrives(), + }); + }); + } + + componentWillUnmount() { + this.unsubscribe(); + } + + render() { + const { cancel, done, ...props } = this.props; + const { selectedList, drives, image, missingDriversModal } = this.state; + + const displayedTargets = this.getDisplayedTargets(drives); + const disabledTargets = this.getDisabledTargets(drives, image); + const numberOfSystemDrives = drives.filter( + (drive) => isDrivelistDrive(drive) && drive.isSystem, + ).length; + const numberOfDisplayedSystemDrives = displayedTargets.filter( + (drive) => isDrivelistDrive(drive) && drive.isSystem, + ).length; + const numberOfHiddenSystemDrives = + numberOfSystemDrives - numberOfDisplayedSystemDrives; + const hasStatus = hasListDriveImageCompatibilityStatus(selectedList, image); + + return ( + + + Select target + + + {drives.length} found + + + } + titleDetails={{getDrives().length} found} + cancel={cancel} + done={() => done(selectedList)} + action={`Select (${selectedList.length})`} + style={{ + width: '780px', + height: '420px', + }} + primaryButtonProps={{ + primary: !hasStatus, + warning: hasStatus, + disabled: !hasAvailableDrives(), + }} + {...props} + > + + {!hasAvailableDrives() ? ( + + + Plug a target drive + + ) : ( + + ) => { + if (t !== null) { + t.setRowSelection(selectedList); + } + }} + columns={this.tableColumns} + data={displayedTargets} + disabledRows={disabledTargets} + rowKey="displayName" + onCheck={(rows: Target[]) => { + this.setState({ + selectedList: rows.filter(isDrivelistDrive), + }); + }} + onRowClick={(row: Target) => { + if ( + !isDrivelistDrive(row) || + this.driveShouldBeDisabled(row, image) + ) { + return; + } + const newList = [...selectedList]; + const selectedIndex = selectedList.findIndex( + (target) => target.device === row.device, + ); + if (selectedIndex === -1) { + newList.push(row); + } else { + // Deselect if selected + newList.splice(selectedIndex, 1); + } + this.setState({ + selectedList: newList, + }); + }} + /> + {numberOfHiddenSystemDrives > 0 && ( + this.setState({ showSystemDrives: true })} + > + + + Show {numberOfHiddenSystemDrives} hidden + + + )} + + )} + + + {missingDriversModal.drive !== undefined && ( + this.setState({ missingDriversModal: {} })} + done={() => { + try { + if (missingDriversModal.drive !== undefined) { + openExternal(missingDriversModal.drive.link); + } + } catch (error) { + logException(error); + } finally { + this.setState({ missingDriversModal: {} }); + } + }} + action="Yes, continue" + cancelButtonProps={{ + children: 'Cancel', + }} + children={ + missingDriversModal.drive.linkMessage || + `Etcher will open ${missingDriversModal.drive.link} in your browser` + } + /> + )} + + ); + } +} diff --git a/lib/gui/app/models/settings.ts b/lib/gui/app/models/settings.ts index a4b00eee..0d433dae 100644 --- a/lib/gui/app/models/settings.ts +++ b/lib/gui/app/models/settings.ts @@ -73,7 +73,6 @@ export async function writeConfigFile( } const DEFAULT_SETTINGS: _.Dictionary = { - unsafeMode: false, errorReporting: true, unmountOnSuccess: true, validateWriteOnSuccess: true, diff --git a/lib/gui/app/models/store.ts b/lib/gui/app/models/store.ts index d66e9a73..fe1b3fff 100644 --- a/lib/gui/app/models/store.ts +++ b/lib/gui/app/models/store.ts @@ -134,6 +134,8 @@ function storeReducer( } drives = _.sortBy(drives, [ + // System drives last + (d) => !!d.isSystem, // Devices with no devicePath first (usbboot) (d) => !!d.devicePath, // Then sort by devicePath (only available on Linux with udev) or device diff --git a/lib/gui/app/modules/drive-scanner.ts b/lib/gui/app/modules/drive-scanner.ts index bc550433..a76c2939 100644 --- a/lib/gui/app/modules/drive-scanner.ts +++ b/lib/gui/app/modules/drive-scanner.ts @@ -17,19 +17,10 @@ import * as sdk from 'etcher-sdk'; import { geteuid, platform } from 'process'; -import * as settings from '../models/settings'; - -/** - * @summary returns true if system drives should be shown - */ -function includeSystemDrives() { - return ( - settings.getSync('unsafeMode') && !settings.getSync('disableUnsafeMode') - ); -} - const adapters: sdk.scanner.adapters.Adapter[] = [ - new sdk.scanner.adapters.BlockDeviceAdapter({ includeSystemDrives }), + new sdk.scanner.adapters.BlockDeviceAdapter({ + includeSystemDrives: () => true, + }), ]; // Can't use permissions.isElevated() here as it returns a promise and we need to set diff --git a/lib/gui/app/os/dialog.ts b/lib/gui/app/os/dialog.ts index e3233cf7..aea6b821 100644 --- a/lib/gui/app/os/dialog.ts +++ b/lib/gui/app/os/dialog.ts @@ -18,7 +18,7 @@ import * as electron from 'electron'; import * as _ from 'lodash'; import * as errors from '../../../shared/errors'; -import { getAllExtensions } from '../../../shared/supported-formats'; +import { SUPPORTED_EXTENSIONS } from '../../../shared/supported-formats'; /** * @summary Open an image selection dialog @@ -40,7 +40,11 @@ export async function selectImage(): Promise { filters: [ { name: 'OS Images', - extensions: [...getAllExtensions()].sort(), + extensions: SUPPORTED_EXTENSIONS, + }, + { + name: 'All', + extensions: ['*'], }, ], }; diff --git a/lib/gui/app/pages/main/DriveSelector.tsx b/lib/gui/app/pages/main/DriveSelector.tsx index 1cac4f82..20c1a6bd 100644 --- a/lib/gui/app/pages/main/DriveSelector.tsx +++ b/lib/gui/app/pages/main/DriveSelector.tsx @@ -14,12 +14,19 @@ * limitations under the License. */ -import * as _ from 'lodash'; +import { scanner } from 'etcher-sdk'; import * as React from 'react'; import styled from 'styled-components'; -import { DriveSelectorModal } from '../../components/drive-selector/DriveSelectorModal'; -import { TargetSelector } from '../../components/drive-selector/target-selector'; -import { getImage, getSelectedDrives } from '../../models/selection-state'; + +import { TargetSelector } from '../../components/target-selector/target-selector-button'; +import { TargetSelectorModal } from '../../components/target-selector/target-selector-modal'; +import { + isDriveSelected, + getImage, + getSelectedDrives, + deselectDrive, + selectDrive, +} from '../../models/selection-state'; import * as settings from '../../models/settings'; import { observe } from '../../models/store'; import * as analytics from '../../modules/analytics'; @@ -45,12 +52,11 @@ const StepBorder = styled.div<{ `; const getDriveListLabel = () => { - return _.join( - _.map(getSelectedDrives(), (drive: any) => { + return getSelectedDrives() + .map((drive: any) => { return `${drive.description} (${drive.displayName})`; - }), - '\n', - ); + }) + .join('\n'); }; const shouldShowDrivesButton = () => { @@ -64,6 +70,35 @@ const getDriveSelectionStateSlice = () => ({ image: getImage(), }); +export const selectAllTargets = ( + modalTargets: scanner.adapters.DrivelistDrive[], +) => { + const selectedDrivesFromState = getSelectedDrives(); + const deselected = selectedDrivesFromState.filter( + (drive) => + !modalTargets.find((modalTarget) => modalTarget.device === drive.device), + ); + // deselect drives + deselected.forEach((drive) => { + analytics.logEvent('Toggle drive', { + drive, + previouslySelected: true, + }); + deselectDrive(drive.device); + }); + // select drives + modalTargets.forEach((drive) => { + // Don't send events for drives that were already selected + if (!isDriveSelected(drive.device)) { + analytics.logEvent('Toggle drive', { + drive, + previouslySelected: false, + }); + } + selectDrive(drive.device); + }); +}; + interface DriveSelectorProps { webviewShowing: boolean; disabled: boolean; @@ -84,7 +119,7 @@ export const DriveSelector = ({ { showDrivesButton, driveListLabel, targets, image }, setStateSlice, ] = React.useState(getDriveSelectionStateSlice()); - const [showDriveSelectorModal, setShowDriveSelectorModal] = React.useState( + const [showTargetSelectorModal, setShowTargetSelectorModal] = React.useState( false, ); @@ -115,11 +150,11 @@ export const DriveSelector = ({ show={!hasDrive && showDrivesButton} tooltip={driveListLabel} openDriveSelector={() => { - setShowDriveSelectorModal(true); + setShowTargetSelectorModal(true); }} reselectDrive={() => { analytics.logEvent('Reselect drive'); - setShowDriveSelectorModal(true); + setShowTargetSelectorModal(true); }} flashing={flashing} targets={targets} @@ -127,10 +162,14 @@ export const DriveSelector = ({ />
- {showDriveSelectorModal && ( - setShowDriveSelectorModal(false)} - > + {showTargetSelectorModal && ( + setShowTargetSelectorModal(false)} + done={(modalTargets) => { + selectAllTargets(modalTargets); + setShowTargetSelectorModal(false); + }} + > )}
); diff --git a/lib/gui/app/pages/main/Flash.tsx b/lib/gui/app/pages/main/Flash.tsx index 42d7743e..4eeac6af 100644 --- a/lib/gui/app/pages/main/Flash.tsx +++ b/lib/gui/app/pages/main/Flash.tsx @@ -21,9 +21,9 @@ import { Flex, Modal, Txt } from 'rendition'; import * as constraints from '../../../../shared/drive-constraints'; import * as messages from '../../../../shared/messages'; -import { DriveSelectorModal } from '../../components/drive-selector/DriveSelectorModal'; import { ProgressButton } from '../../components/progress-button/progress-button'; import { SourceOptions } from '../../components/source-selector/source-selector'; +import { TargetSelectorModal } from '../../components/target-selector/target-selector-modal'; import * as availableDrives from '../../models/available-drives'; import * as flashState from '../../models/flash-state'; import * as selection from '../../models/selection-state'; @@ -31,6 +31,7 @@ import * as analytics from '../../modules/analytics'; import { scanner as driveScanner } from '../../modules/drive-scanner'; import * as imageWriter from '../../modules/image-writer'; import * as notification from '../../os/notification'; +import { selectAllTargets } from './DriveSelector'; import FlashSvg from '../../../assets/flash.svg'; @@ -197,6 +198,13 @@ export class FlashStep extends React.PureComponent< } } + private hasListWarnings(drives: any[], image: any) { + if (drives.length === 0 || flashState.isFlashing()) { + return; + } + return constraints.hasListDriveImageCompatibilityStatus(drives, image); + } + private async tryFlash() { const devices = selection.getSelectedDevices(); const image = selection.getImage(); @@ -209,10 +217,7 @@ export class FlashStep extends React.PureComponent< if (drives.length === 0 || this.props.isFlashing) { return; } - const hasDangerStatus = constraints.hasListDriveImageCompatibilityStatus( - drives, - image, - ); + const hasDangerStatus = this.hasListWarnings(drives, image); if (hasDangerStatus) { this.setState({ warningMessages: getWarningMessages(drives, image) }); return; @@ -245,6 +250,10 @@ export class FlashStep extends React.PureComponent< position={this.props.position} disabled={this.props.shouldFlashStepBeDisabled} cancel={imageWriter.cancel} + warning={this.hasListWarnings( + selection.getSelectedDrives(), + selection.getImage(), + )} callback={() => { this.tryFlash(); }} @@ -317,11 +326,14 @@ export class FlashStep extends React.PureComponent< )} - {this.state.showDriveSelectorModal && ( - this.setState({ showDriveSelectorModal: false })} - /> + this.setState({ showDriveSelectorModal: false })} + done={(modalTargets) => { + selectAllTargets(modalTargets); + this.setState({ showDriveSelectorModal: false }); + }} + > )} ); diff --git a/lib/gui/app/scss/components/_label.scss b/lib/gui/app/scss/components/_label.scss deleted file mode 100644 index 19f76a83..00000000 --- a/lib/gui/app/scss/components/_label.scss +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2016 balena.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -.label { - font-size: 9px; - margin-right: 4.5px; -} - -.label-big { - font-size: 11px; - padding: 8px 25px; -} - -.label-inset { - background-color: darken($palette-theme-dark-background, 10%); - color: darken($palette-theme-dark-foreground, 43%); -} - -.label-danger { - background-color: $palette-theme-danger-background; - color: $palette-theme-danger-foreground; -} diff --git a/lib/gui/app/scss/components/_tick.scss b/lib/gui/app/scss/components/_tick.scss deleted file mode 100644 index 9a9519ec..00000000 --- a/lib/gui/app/scss/components/_tick.scss +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2016 balena.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -.tick { - @extend .glyphicon; - - display: inline-block; - border-radius: 50%; - padding: 3px; - font-size: 18px; - border: 2px solid; - - &[disabled] { - color: $palette-theme-dark-soft-foreground; - border-color: $palette-theme-dark-soft-foreground; - background-color: transparent; - } -} - -.tick--success { - @extend .glyphicon-ok; - - color: $palette-theme-success-foreground; - background-color: $palette-theme-success-background; - border-color: $palette-theme-success-background; -} - -.tick--error { - @extend .glyphicon-remove; - - color: $palette-theme-danger-foreground; - background-color: $palette-theme-danger-background; - border-color: $palette-theme-danger-background; -} diff --git a/lib/gui/app/scss/main.scss b/lib/gui/app/scss/main.scss index 972a34f6..ee934aa3 100644 --- a/lib/gui/app/scss/main.scss +++ b/lib/gui/app/scss/main.scss @@ -25,17 +25,13 @@ $disabled-opacity: 0.2; @import "../../../../node_modules/flexboxgrid/dist/flexboxgrid.css"; @import "../../../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap"; @import "./modules/theme"; -@import "./modules/bootstrap"; @import "./modules/space"; -@import "./components/label"; -@import "./components/tick"; -@import "../components/drive-selector/styles/drive-selector"; @import "../pages/main/styles/main"; @import "../pages/finish/styles/finish"; @import "./desktop"; @font-face { - font-family: "SourceSansPro"; + font-family: "Source Sans Pro"; src: url("./fonts/SourceSansPro-Regular.ttf") format("truetype"); font-weight: 500; font-style: normal; @@ -43,14 +39,20 @@ $disabled-opacity: 0.2; } @font-face { - font-family: "SourceSansPro"; + font-family: "Source Sans Pro"; src: url("./fonts/SourceSansPro-SemiBold.ttf") format("truetype"); font-weight: 600; font-style: normal; font-display: block; } +// Prevent white flash when running application +html { + background-color: $palette-theme-dark-background; +} + body { + background-color: $palette-theme-dark-background; letter-spacing: 0.1px; display: flex; flex-direction: column; diff --git a/lib/gui/app/scss/modules/_bootstrap.scss b/lib/gui/app/scss/modules/_bootstrap.scss deleted file mode 100644 index b49adddb..00000000 --- a/lib/gui/app/scss/modules/_bootstrap.scss +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2016 balena.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// This file is meant to hold Bootstrap modifications -// that don't qualify as separate UI components. - -// Prevent white flash when running application -html { - background-color: $palette-theme-dark-background; -} - -body { - background-color: $palette-theme-dark-background; -} diff --git a/lib/gui/app/styled-components.tsx b/lib/gui/app/styled-components.tsx index fd7ddd09..f2260398 100644 --- a/lib/gui/app/styled-components.tsx +++ b/lib/gui/app/styled-components.tsx @@ -15,56 +15,26 @@ */ import * as React from 'react'; -import { Button, ButtonProps, Provider, Txt } from 'rendition'; +import { + Button, + ButtonProps, + Modal as ModalBase, + Provider, + Txt, +} from 'rendition'; import styled from 'styled-components'; import { space } from 'styled-system'; -import { colors } from './theme'; - -const font = 'SourceSansPro'; -const theme = { - font, - titleFont: font, - global: { - font: { - family: font, - }, - }, - colors, - button: { - border: { - width: '0', - radius: '24px', - }, - disabled: { - opacity: 1, - }, - extend: () => ` - && { - width: 200px; - height: 48px; - - &:disabled { - background-color: ${colors.dark.disabled.background}; - color: ${colors.dark.disabled.foreground}; - opacity: 1; - - &:hover { - background-color: ${colors.dark.disabled.background}; - color: ${colors.dark.disabled.foreground}; - } - } - } - `, - }, -}; +import { colors, theme } from './theme'; export const ThemedProvider = (props: any) => ( ); export const BaseButton = styled(Button)` + width: 200px; height: 48px; + font-size: 16px; `; export const IconButton = styled((props) => + ))` color: #ffffff; margin: auto; @@ -105,10 +75,9 @@ export const ChangeButton = styled(Button)` ${space} } `; -export const StepNameButton = styled(Button)` - border-radius: 24px; - margin: auto; - display: flex; + +export const StepNameButton = styled(BaseButton)` + display: inline-flex; justify-content: center; align-items: center; width: 100%; @@ -123,16 +92,53 @@ export const StepNameButton = styled(Button)` } } `; + export const Footer = styled(Txt)` margin-top: 10px; color: ${colors.dark.disabled.foreground}; font-size: 10px; `; + export const Underline = styled(Txt.span)` border-bottom: 1px dotted; padding-bottom: 2px; `; + export const DetailsText = styled(Txt.p)` color: ${colors.dark.disabled.foreground}; margin-bottom: 0; `; + +export const Modal = styled((props) => { + return ( + + ); +})` + > div { + padding: 30px; + height: calc(100% - 80px); + + > h3 { + margin: 0; + } + + > div:last-child { + height: 80px; + background-color: #fff; + justify-content: center; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + box-shadow: 0 -2px 10px 0 rgba(221, 225, 240, 0.5), 0 -1px 0 0 #dde1f0; + } + } +`; diff --git a/lib/gui/app/theme.ts b/lib/gui/app/theme.ts index c12d22b4..46e744f9 100644 --- a/lib/gui/app/theme.ts +++ b/lib/gui/app/theme.ts @@ -64,3 +64,33 @@ export const colors = { background: '#5fb835', }, }; + +export const theme = { + colors, + button: { + border: { + width: '0', + radius: '24px', + }, + disabled: { + opacity: 1, + }, + extend: () => ` + && { + width: 200px; + height: 48px; + + :disabled { + background-color: ${colors.dark.disabled.background}; + color: ${colors.dark.disabled.foreground}; + opacity: 1; + + :hover { + background-color: ${colors.dark.disabled.background}; + color: ${colors.dark.disabled.foreground}; + } + } + } + `, + }, +}; diff --git a/lib/gui/assets/tgt.svg b/lib/gui/assets/tgt.svg new file mode 100644 index 00000000..3193c769 --- /dev/null +++ b/lib/gui/assets/tgt.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/lib/shared/drive-constraints.ts b/lib/shared/drive-constraints.ts index b534933f..f68d27ed 100644 --- a/lib/shared/drive-constraints.ts +++ b/lib/shared/drive-constraints.ts @@ -172,12 +172,7 @@ export function getDriveImageCompatibilityStatuses( const statusList = []; // Mind the order of the if-statements if you modify. - if (isSourceDrive(drive, image)) { - statusList.push({ - type: COMPATIBILITY_STATUS_TYPES.ERROR, - message: messages.compatibility.containsImage(), - }); - } else if (isDriveLocked(drive)) { + if (isDriveLocked(drive)) { statusList.push({ type: COMPATIBILITY_STATUS_TYPES.ERROR, message: messages.compatibility.locked(), @@ -196,6 +191,13 @@ export function getDriveImageCompatibilityStatuses( message: messages.compatibility.tooSmall(prettyBytes(relativeBytes)), }); } else { + if (isSourceDrive(drive, image)) { + statusList.push({ + type: COMPATIBILITY_STATUS_TYPES.ERROR, + message: messages.compatibility.containsImage(), + }); + } + if (isSystemDrive(drive)) { statusList.push({ type: COMPATIBILITY_STATUS_TYPES.WARNING, @@ -273,3 +275,8 @@ export function hasListDriveImageCompatibilityStatus( ) { return Boolean(getListDriveImageCompatibilityStatuses(drives, image).length); } + +export interface TargetStatus { + message: string; + type: number; +} diff --git a/lib/shared/messages.ts b/lib/shared/messages.ts index 3e23d34d..ff32c937 100644 --- a/lib/shared/messages.ts +++ b/lib/shared/messages.ts @@ -65,16 +65,16 @@ export const compatibility = { }, system: () => { - return 'System Drive'; + return 'System drive'; }, containsImage: () => { - return 'Drive Mountpoint Contains Image'; + return 'Source drive'; }, // The drive is large and therefore likely not a medium you want to write to. largeDrive: () => { - return 'Large Drive'; + return 'Large drive'; }, } as const; diff --git a/lib/shared/supported-formats.ts b/lib/shared/supported-formats.ts index 4dee3ba5..c2b0c6dc 100644 --- a/lib/shared/supported-formats.ts +++ b/lib/shared/supported-formats.ts @@ -14,44 +14,28 @@ * limitations under the License. */ -import * as sdk from 'etcher-sdk'; -import * as mime from 'mime-types'; -import * as path from 'path'; +import { basename } from 'path'; -export function getCompressedExtensions(): string[] { - const result = []; - for (const [ - mimetype, - cls, - // @ts-ignore (mimetypes is private) - ] of sdk.sourceDestination.SourceDestination.mimetypes.entries()) { - if (cls.prototype instanceof sdk.sourceDestination.CompressedSource) { - const extension = mime.extension(mimetype); - if (extension) { - result.push(extension); - } - } - } - return result; -} - -export function getNonCompressedExtensions(): string[] { - return sdk.sourceDestination.SourceDestination.imageExtensions; -} - -export function getArchiveExtensions(): string[] { - return ['zip', 'etch']; -} - -export function getAllExtensions(): string[] { - return [ - ...getArchiveExtensions(), - ...getNonCompressedExtensions(), - ...getCompressedExtensions(), - ]; -} +export const SUPPORTED_EXTENSIONS = [ + 'bin', + 'bz2', + 'dmg', + 'dsk', + 'etch', + 'gz', + 'hddimg', + 'img', + 'iso', + 'raw', + 'rpi-sdimg', + 'sdcard', + 'vhd', + 'wic', + 'xz', + 'zip', +]; export function looksLikeWindowsImage(imagePath: string): boolean { const regex = /windows|win7|win8|win10|winxp/i; - return regex.test(path.basename(imagePath)); + return regex.test(basename(imagePath)); } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 836b7154..bd5fdbf6 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -218,12 +218,31 @@ } }, "@babel/helper-annotate-as-pure": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", - "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.1.tgz", + "integrity": "sha512-ewp3rvJEwLaHgyWGe4wQssC2vjks3E80WiUe2BpMb0KhreTjMROCbxXcEovTrbeGVdQct5VjQfrv9EgC+xMzCw==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.1" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", + "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==", + "dev": true + }, + "@babel/types": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.2.tgz", + "integrity": "sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.1", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } } }, "@babel/helper-builder-binary-assignment-operator-visitor": { @@ -794,12 +813,31 @@ } }, "@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz", + "integrity": "sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.1" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", + "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==", + "dev": true + }, + "@babel/types": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.2.tgz", + "integrity": "sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.1", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } } }, "@babel/helper-module-transforms": { @@ -2592,21 +2630,13 @@ } }, "@babel/runtime-corejs2": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.10.0.tgz", - "integrity": "sha512-sH02MTpZ4/f9glo0UNGXWQhl23e9gEB2MzjAqHZnKrztmY6eGxxPFJOniEHDOQisJwWZJlPAcBywi45lYc4h2w==", + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.10.2.tgz", + "integrity": "sha512-ZLwsFnNm3WpIARU1aLFtufjMHsmEnc8TjtrfAjmbgMbeoyR+LuQoyESoNdTfeDhL6IdY12SpeycXMgSgl8XGXA==", "dev": true, "requires": { "core-js": "^2.6.5", "regenerator-runtime": "^0.13.4" - }, - "dependencies": { - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", - "dev": true - } } }, "@babel/template": { @@ -2842,12 +2872,20 @@ } }, "@fortawesome/free-regular-svg-icons": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.13.0.tgz", - "integrity": "sha512-70FAyiS5j+ANYD4dh9NGowTorNDnyvQHHpCM7FpnF7GxtDjBUCKdrFqCPzesEIpNDFNd+La3vex+jDk4nnUfpA==", + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.13.1.tgz", + "integrity": "sha512-sSeaqqmv2ovA5LKcrbh3VnEDZHVhaxijWKm4R0AdT0eG21pgxNsJbStD8lW9z6bgSuWXRNHhbhOmARuRCLS8tw==", "dev": true, "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.28" + "@fortawesome/fontawesome-common-types": "^0.2.29" + }, + "dependencies": { + "@fortawesome/fontawesome-common-types": { + "version": "0.2.29", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.29.tgz", + "integrity": "sha512-cY+QfDTbZ7XVxzx7jxbC98Oxr/zc7R2QpTLqTxqlfyXDrAJjzi/xUIqAUsygELB62JIrbsWxtSRhayKFkGI7MA==", + "dev": true + } } }, "@fortawesome/free-solid-svg-icons": { @@ -2894,6 +2932,45 @@ "fastq": "^1.6.0" } }, + "@react-google-maps/api": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-1.9.7.tgz", + "integrity": "sha512-o51/1RaN67R9I9atfwAbshh5xs1hxzvrTiysNWi8rnkUSF29Kip+1YMQ1wys+fWUmjAPCjfqzYvaJuBIkgu8mw==", + "dev": true, + "requires": { + "@react-google-maps/infobox": "1.9.6", + "@react-google-maps/marker-clusterer": "1.9.6", + "acorn": "7.3.1", + "acorn-jsx": "^5.2.0", + "invariant": "2.2.4" + }, + "dependencies": { + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + } + } + }, + "@react-google-maps/infobox": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-1.9.6.tgz", + "integrity": "sha512-fR+zAC673BijGd9IJuV8sD0zEy1cLddxgyrT/SHFlNRPsnyprbH5Ox4zmfNU6UTkrQ7ULedFK7hUrgLVYKpc+Q==", + "dev": true + }, + "@react-google-maps/marker-clusterer": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-1.9.6.tgz", + "integrity": "sha512-bliu3CfMWLxMJaW8UK7+6Q1iPQoXbP9BK14yvid2pZL+OgqKgmnY6O9pazIi5Ca9NVTviNcN8WCz1TicUiABQw==", + "dev": true + }, "@ronomon/direct-io": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@ronomon/direct-io/-/direct-io-3.0.1.tgz", @@ -3437,9 +3514,9 @@ } }, "@types/json-schema": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", - "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", + "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", "dev": true }, "@types/lodash": { @@ -3545,6 +3622,15 @@ "@types/react": "*" } }, + "@types/react-helmet": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.0.0.tgz", + "integrity": "sha512-NBMPAxgjpaMooXa51cU1BTgrX6T+hQbMiLm77JhBbfOzPQea3RB5rNpPOD5xGWHIVpGXHd59cltEzIq0qglGcQ==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/react-jsonschema-form": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@types/react-jsonschema-form/-/react-jsonschema-form-1.7.3.tgz", @@ -3556,9 +3642,9 @@ } }, "@types/react-native": { - "version": "0.62.12", - "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.62.12.tgz", - "integrity": "sha512-EuM2QOx0LGwY3mKQ313+QcTYOwJhw5eggmE42GO4ElIKIfNK+zxxM6Pe9dT1Eq8eCJXY0oG327L7gUBWniwNNA==", + "version": "0.62.13", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.62.13.tgz", + "integrity": "sha512-hs4/tSABhcJx+J8pZhVoXHrOQD89WFmbs8QiDLNSA9zNrD46pityAuBWuwk1aMjPk9I3vC5ewkJroVRHgRIfdg==", "dev": true, "requires": { "@types/react": "*" @@ -3599,9 +3685,9 @@ } }, "@types/sanitize-html": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-1.23.0.tgz", - "integrity": "sha512-yl0HvhWOZWzmkGEBQLKYf+BtotvbGLFhCejuBvwyM1Q23RliLXNfviuOMLQ/nCRFWR2yI0LRRiaH/Oz7CIpCGw==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-1.23.2.tgz", + "integrity": "sha512-s00omSXGjemcNGXChE44grxYLPCkxdp/rdxGCb4FcGyH0aOjFOacrnP0P394Ktp+IKeBk1Q7VsGJ+cOa2GZ5hw==", "dev": true, "requires": { "htmlparser2": "^4.1.0" @@ -4350,6 +4436,18 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "array.prototype.map": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", + "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.4" + } + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -6102,9 +6200,9 @@ } }, "core-js": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", "dev": true }, "core-js-compat": { @@ -6582,9 +6680,9 @@ "dev": true }, "d3-fetch": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.1.2.tgz", - "integrity": "sha512-S2loaQCV/ZeyTyIF2oP8D1K9Z4QizUzW7cWeAOAS4U88qOt3Ucf6GsmgthuYSdyB2HyEm4CeGvkQxWsmInsIVA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.2.0.tgz", + "integrity": "sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==", "dev": true, "requires": { "d3-dsv": "1" @@ -7260,9 +7358,9 @@ } }, "electron": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/electron/-/electron-9.0.3.tgz", - "integrity": "sha512-rY59wy50z0oWp/q69zq0UIzvtcM5j2BJbLAwEoLfVNS3DLt9wDZqRqSIBvLEBl+xWbafCnRA9haEqi7ssM94GA==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/electron/-/electron-9.0.4.tgz", + "integrity": "sha512-QzkeZNAiNB7KxcdoQKSoaiVT/GQdB4Vt0/ZZOuU8tIKABAsni2I7ztiAbUzxcsnQsqEBSfChuPuDQ5A4VbbzPg==", "dev": true, "requires": { "@electron/get": "^1.0.1", @@ -7402,6 +7500,200 @@ "yargs": "^15.3.1" }, "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "mocha": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", + "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==", + "dev": true, + "requires": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "chokidar": "3.3.0", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "3.0.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.5", + "ms": "2.1.1", + "node-environment-flags": "1.0.6", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.0" + }, + "dependencies": { + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7410,44 +7702,51 @@ "requires": { "isexe": "^2.0.0" } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, "electron-notarize": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-0.3.0.tgz", - "integrity": "sha512-tuDw8H0gcDOalNLv6RM2CwGvUXU60MPGZRDEmd0ppX+yP5XqL8Ec2DuXyz9J7WQSA3aRCfzIgH8C5CAivDYWMw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-1.0.0.tgz", + "integrity": "sha512-dsib1IAquMn0onCrNMJ6gtEIZn/azG8hZMCYOuZIMVMUeRMgBYHK1s5TK9P8xAcrAjh/2aN5WYHzgVSWX314og==", "dev": true, "requires": { "debug": "^4.1.1", - "fs-extra": "^8.1.0" + "fs-extra": "^9.0.1" }, "dependencies": { "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", "dev": true, "requires": { + "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true } } }, @@ -7858,9 +8157,9 @@ } }, "entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.2.tgz", - "integrity": "sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", "dev": true }, "env-paths": { @@ -7914,6 +8213,35 @@ "string.prototype.trimright": "^2.1.1" } }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "es-get-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", + "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "dev": true, + "requires": { + "es-abstract": "^1.17.4", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -8209,9 +8537,9 @@ "dev": true }, "etcher-sdk": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-4.1.13.tgz", - "integrity": "sha512-ITzBgoy8AGI6/DHyS43wix2TNa9kVeILsr/vQl75Hbbt5gM4C2ygwqABG7V+MGTaNphWc2OstSP9k6xQpGfttw==", + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-4.1.15.tgz", + "integrity": "sha512-PZ/OLbAmtQXQlla33FVwvyLL5rzpYG5gQLpPiz1zCzNDWykaVIM9lXQ8m0DxTp7/m11tM3lw1gT/IPCUKtKfdA==", "dev": true, "requires": { "@ronomon/direct-io": "^3.0.1", @@ -8612,6 +8940,14 @@ "promise": "^7.1.1", "setimmediate": "^1.0.5", "ua-parser-js": "^0.7.18" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=", + "dev": true + } } }, "fd-slicer": { @@ -9484,43 +9820,17 @@ } }, "grommet": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/grommet/-/grommet-2.13.0.tgz", - "integrity": "sha512-C2asZEacDjr9stnVw2XoYVezPdbLM3qxQrxdLfnfdlj1pTb/JNfwqpQZatMW5G8NU3S596juVAnTPefmMXcwyQ==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/grommet/-/grommet-2.14.0.tgz", + "integrity": "sha512-G/LTkr+uFri4NUNQGJMx8TtWWe9+KSpIHCXC9WgRaICI73R3+BvhLSNSzWMQ6YIQC+7PJFtruebeWjdUqR3Ykw==", "dev": true, "requires": { - "css": "^2.2.3", "grommet-icons": "^4.2.0", "hoist-non-react-statics": "^3.2.0", - "markdown-to-jsx": "^6.9.1", + "markdown-to-jsx": "^6.11.4", "polished": "^3.4.1", "prop-types": "^15.7.2", - "react-desc": "^4.1.2", - "recompose": "^0.30.0" - }, - "dependencies": { - "recompose": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz", - "integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==", - "dev": true, - "requires": { - "@babel/runtime": "^7.0.0", - "change-emitter": "^0.1.2", - "fbjs": "^0.8.1", - "hoist-non-react-statics": "^2.3.1", - "react-lifecycles-compat": "^3.0.2", - "symbol-observable": "^1.0.4" - }, - "dependencies": { - "hoist-non-react-statics": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", - "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==", - "dev": true - } - } - } + "react-desc": "^4.1.2" } }, "grommet-icons": { @@ -9707,9 +10017,9 @@ "dev": true }, "highlight.js": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.0.3.tgz", - "integrity": "sha512-9FG7SSzv9yOY5CGGxfI6NDm7xLYtMOjKtPBxw7Zff3t5UcRcUNTGEeS8lNjhceL34KeetLMoGMFTGoaa83HwyQ==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.1.tgz", + "integrity": "sha512-b4L09127uVa+9vkMgPpdUQP78ickGbHEQTWeBrQFTJZ4/n2aihWOGS0ZoUqAwjVmfjhq/C76HRzkqwZhK4sBbg==", "dev": true }, "hmac-drbg": { @@ -10262,6 +10572,12 @@ } } }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, "is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", @@ -10407,6 +10723,12 @@ "is-path-inside": "^3.0.1" } }, + "is-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", + "dev": true + }, "is-my-ip-valid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", @@ -10498,12 +10820,24 @@ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, + "is-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", + "dev": true + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, "is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", @@ -10577,6 +10911,22 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, + "iterate-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", + "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", + "dev": true + }, + "iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", + "dev": true, + "requires": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + } + }, "jake": { "version": "10.7.1", "resolved": "https://registry.npmjs.org/jake/-/jake-10.7.1.tgz", @@ -11177,18 +11527,6 @@ "integrity": "sha1-+CbJtOKoUR2E46yinbBeGk87cqk=", "dev": true }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=", - "dev": true - }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -11201,30 +11539,12 @@ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", "dev": true }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", - "dev": true - }, "lodash.kebabcase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", "dev": true }, - "lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true - }, "log-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", @@ -11983,49 +12303,59 @@ "dev": true }, "mocha": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz", - "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.0.1.tgz", + "integrity": "sha512-vefaXfdYI8+Yo8nPZQQi0QO2o+5q9UIMX1jZ1XMmK3+4+CQjc7+B0hPdUeglXiTlr8IHMVRo63IhO9Mzt6fxOg==", "dev": true, "requires": { - "ansi-colors": "3.2.3", + "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.3.0", + "chokidar": "3.3.1", "debug": "3.2.6", - "diff": "3.5.0", + "diff": "4.0.2", "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", + "find-up": "4.1.0", + "glob": "7.1.6", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "3.13.1", "log-symbols": "3.0.0", "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", + "ms": "2.1.2", "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", + "promise.allsettled": "1.0.2", + "serialize-javascript": "3.0.0", + "strip-json-comments": "3.0.1", + "supports-color": "7.1.0", + "which": "2.0.2", "wide-align": "1.1.3", + "workerpool": "6.0.0", "yargs": "13.3.2", "yargs-parser": "13.1.2", "yargs-unparser": "1.6.0" }, "dependencies": { - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true - }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, + "chokidar": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", + "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.3.0" + } + }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -12046,40 +12376,17 @@ "ms": "^2.1.1" } }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", @@ -12097,12 +12404,6 @@ "path-exists": "^3.0.0" } }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, "p-locate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", @@ -12118,6 +12419,21 @@ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, + "readdirp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", + "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", + "dev": true, + "requires": { + "picomatch": "^2.0.7" + } + }, + "serialize-javascript": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.0.0.tgz", + "integrity": "sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==", + "dev": true + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -12138,13 +12454,28 @@ "ansi-regex": "^4.1.0" } }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" } }, "wrap-ansi": { @@ -12174,6 +12505,17 @@ "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^13.1.2" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + } } }, "yargs-parser": { @@ -13333,9 +13675,9 @@ "dev": true }, "partitioninfo": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/partitioninfo/-/partitioninfo-5.3.4.tgz", - "integrity": "sha512-cmf+S4LvtrgPlC8LbY2qI96Zf1z6j1v9rFBik23AxoE3R1EH9QYWdrCHJf5uwEp8iJUdRhZjQIaRvfLH5hPJPA==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/partitioninfo/-/partitioninfo-5.3.5.tgz", + "integrity": "sha512-lZIt5D9wsOVnixePPmVyHQs1m1eDICgDecBmV00W5tpDoOwHdkoseW0p+YjBlWd4LIbUqJXJIrfjPq/12uAVSg==", "dev": true, "requires": { "bluebird": "^3.5.3", @@ -13556,9 +13898,9 @@ "dev": true }, "polished": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/polished/-/polished-3.6.3.tgz", - "integrity": "sha512-QJ0q0b6gX1+0OJtPMfgVJxV0vg5XTa4im+Rca989dAtmsd/fEky3X+0A+V+OUXq1nyiDGplJwqD853dTS0gkPg==", + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/polished/-/polished-3.6.5.tgz", + "integrity": "sha512-VwhC9MlhW7O5dg/z7k32dabcAFW1VI2+7fSe8cE/kXcfL7mVdoa5UxciYGW2sJU78ldDLT6+ROEKIZKFNTnUXQ==", "dev": true, "requires": { "@babel/runtime": "^7.9.2" @@ -13736,6 +14078,19 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, + "promise.allsettled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", + "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", + "dev": true, + "requires": { + "array.prototype.map": "^1.0.1", + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "iterate-value": "^1.0.0" + } + }, "prop-types": { "version": "15.7.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", @@ -13973,16 +14328,34 @@ "scheduler": "^0.19.1" } }, + "react-fast-compare": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==", + "dev": true + }, "react-google-recaptcha": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-2.0.1.tgz", - "integrity": "sha512-4Y8awVnarn7+gdVpu8uvSmRJzzlMMoXqdhLoyToTOfVK6oM+NaChNI8NShnu75Q2YGHLvR1IA1FWZesuYHwn5w==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-2.1.0.tgz", + "integrity": "sha512-K9jr7e0CWFigi8KxC3WPvNqZZ47df2RrMAta6KmRoE4RUi7Ys6NmNjytpXpg4HI/svmQJLKR+PncEPaNJ98DqQ==", "dev": true, "requires": { "prop-types": "^15.5.0", "react-async-script": "^1.1.1" } }, + "react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -14003,14 +14376,6 @@ "react-is": "^16.8.4", "react-lifecycles-compat": "^3.0.4", "shortid": "^2.2.14" - }, - "dependencies": { - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", - "dev": true - } } }, "react-lifecycles-compat": { @@ -14028,10 +14393,16 @@ "prop-types": "^15.6.2" } }, + "react-side-effect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.0.tgz", + "integrity": "sha512-IgmcegOSi5SNX+2Snh1vqmF0Vg/CbkycU9XZbOHJlZ6kMzTmi3yc254oB1WCkgA7OQtIAoLmcSFuHTc/tlcqXg==", + "dev": true + }, "react-simplemde-editor": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/react-simplemde-editor/-/react-simplemde-editor-4.1.1.tgz", - "integrity": "sha512-Mbe7xHmD154aLL7xES3GnJ+JUep1BoeWA9oEiHy8Fz9upgRnvGLbkHX9elqlEyyZYX8+o+GrXUSvZFjJ7sz1/Q==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/react-simplemde-editor/-/react-simplemde-editor-4.1.2.tgz", + "integrity": "sha512-0eEPhrOTRNNCxnuJMmUuPm/DHIijsm9H2aGrY9+nmMksccNHi7mfBM9ajoIzo9XzWZrXSvjBu+svzRdt48MjLw==", "dev": true, "requires": { "@types/codemirror": "^0.0.88", @@ -14232,21 +14603,23 @@ "dev": true }, "rendition": { - "version": "14.13.0", - "resolved": "https://registry.npmjs.org/rendition/-/rendition-14.13.0.tgz", - "integrity": "sha512-MBKuUZnBsOkZApmVrDYycMWhD7KypJZqa6ZSD+y2uIoYEtFBSQ+kr4iJLh3lsgBuFTkouBrjYWJeN+X2FWb5bQ==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/rendition/-/rendition-15.2.1.tgz", + "integrity": "sha512-dwSiy43c3EEw+eZDz7qNNPQZ1zH7iTlAYd5AdHevmYwZdvnFNFQen0EdhAr9Z8o4ra6TYZpNJod4Vp9eZ1hufQ==", "dev": true, "requires": { "@fortawesome/fontawesome-svg-core": "^1.2.25", "@fortawesome/free-regular-svg-icons": "^5.11.2", "@fortawesome/free-solid-svg-icons": "^5.11.2", "@fortawesome/react-fontawesome": "^0.1.5", + "@react-google-maps/api": "^1.9.7", "@types/color": "^3.0.0", "@types/json-schema": "^7.0.3", "@types/lodash": "^4.14.77", "@types/marked": "^0.7.2", "@types/node": "^13.13.4", "@types/prop-types": "^15.7.0", + "@types/react-helmet": "^6.0.0", "@types/react-jsonschema-form": "^1.3.2", "@types/recompose": "^0.26.2", "@types/sanitize-html": "^1.18.3", @@ -14259,7 +14632,7 @@ "color": "^3.1.2", "color-hash": "^1.0.3", "copy-to-clipboard": "^3.0.8", - "grommet": "^2.13.0", + "grommet": "^2.14.0", "highlight.js": "^10.0.3", "jellyschema": "^0.11.9", "lodash": "^4.17.11", @@ -14267,6 +14640,7 @@ "mermaid": "8.4.0", "prop-types": "^15.7.2", "react-google-recaptcha": "^2.0.0-rc.1", + "react-helmet": "^6.0.0", "react-jsonschema-form": "^1.3.0", "react-notifications-component": "^2.2.3", "react-simplemde-editor": "^4.1.1", @@ -14281,9 +14655,9 @@ }, "dependencies": { "@types/node": { - "version": "13.13.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.9.tgz", - "integrity": "sha512-EPZBIGed5gNnfWCiwEIwTE2Jdg4813odnG8iNPMQGrqVxrI+wL68SPtPeCX+ZxGBaA6pKAVc6jaKgP/Q0QzfdQ==", + "version": "13.13.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.12.tgz", + "integrity": "sha512-zWz/8NEPxoXNT9YyF2osqyA9WjssZukYpgI4UYZpOjcyqwIUqWGkcCionaEb9Ki+FULyPyvNFpg/329Kd2/pbw==", "dev": true }, "styled-system": { @@ -14690,18 +15064,14 @@ } }, "sanitize-html": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.24.0.tgz", - "integrity": "sha512-TAIFx39V/y06jDd4YUz7ntCdMUXN5Z28pSG7sTP2BCLXwHA9+ermacDpQs35Evo4p6YSgmaPdSbGiX4Fgptuuw==", + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.27.0.tgz", + "integrity": "sha512-U1btucGeYVpg0GoK43jPpe/bDCV4cBOGuxzv5NBd0bOjyZdMKY0n98S/vNlO1wVwre0VCj8H3hbzE7gD2+RjKA==", "dev": true, "requires": { "chalk": "^2.4.1", "htmlparser2": "^4.1.0", - "lodash.clonedeep": "^4.5.0", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.mergewith": "^4.6.2", + "lodash": "^4.17.15", "postcss": "^7.0.27", "srcset": "^2.0.1", "xtend": "^4.0.1" @@ -15785,9 +16155,9 @@ "dev": true }, "styled-components": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.1.0.tgz", - "integrity": "sha512-0Qs2wEkFBXHFlysz6CV831VG6HedcrFUwChjnWylNivsx14MtmqQsohi21rMHZxzuTba063dEyoe/SR6VGJI7Q==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.1.1.tgz", + "integrity": "sha512-1ps8ZAYu2Husx+Vz8D+MvXwEwvMwFv+hqqUwhNlDN5ybg6A+3xyW1ECrAgywhvXapNfXiz79jJyU0x22z0FFTg==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", @@ -17858,6 +18228,12 @@ "errno": "~0.1.7" } }, + "workerpool": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", + "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", + "dev": true + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -18263,4 +18639,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index a1b81893..382e420e 100644 --- a/package.json +++ b/package.json @@ -72,13 +72,13 @@ "css-loader": "^3.5.3", "d3": "^4.13.0", "debug": "^4.2.0", - "electron": "9.0.3", + "electron": "9.0.4", "electron-builder": "^22.7.0", "electron-mocha": "^8.2.0", - "electron-notarize": "^0.3.0", + "electron-notarize": "^1.0.0", "electron-rebuild": "^1.11.0", "electron-updater": "^4.3.2", - "etcher-sdk": "^4.1.13", + "etcher-sdk": "^4.1.15", "file-loader": "^6.0.0", "flexboxgrid": "^6.3.0", "husky": "^4.2.5", @@ -86,9 +86,8 @@ "inactivity-timer": "^1.0.0", "lint-staged": "^10.2.2", "lodash": "^4.17.10", - "mime-types": "^2.1.18", "mini-css-extract-plugin": "^0.9.0", - "mocha": "^7.0.1", + "mocha": "^8.0.1", "nan": "^2.14.0", "native-addon-loader": "^2.0.1", "node-gyp": "^7.0.0", @@ -99,7 +98,7 @@ "react": "^16.8.5", "react-dom": "^16.8.5", "redux": "^4.0.5", - "rendition": "^14.13.0", + "rendition": "^15.2.1", "request": "^2.81.0", "resin-corvus": "^2.0.5", "roboto-fontface": "^0.10.0", diff --git a/tests/shared/drive-constraints.spec.ts b/tests/shared/drive-constraints.spec.ts index e22ca18f..e5ecf1de 100644 --- a/tests/shared/drive-constraints.spec.ts +++ b/tests/shared/drive-constraints.spec.ts @@ -1126,7 +1126,7 @@ describe('Shared: DriveConstraints', function () { }); describe('given the drive contains the image and the drive is locked', () => { - it('should return the contains-image drive error by precedence', function () { + it('should return the locked error by precedence', function () { this.drive.isReadOnly = true; this.image.path = path.join(this.mountpoint, 'rpi.img'); @@ -1135,7 +1135,7 @@ describe('Shared: DriveConstraints', function () { this.image, ); // @ts-ignore - const expectedTuples = [['ERROR', 'containsImage']]; + const expectedTuples = [['ERROR', 'locked']]; // @ts-ignore expectStatusTypesAndMessagesToBe(result, expectedTuples); @@ -1303,7 +1303,7 @@ describe('Shared: DriveConstraints', function () { ), ).to.deep.equal([ { - message: 'Drive Mountpoint Contains Image', + message: 'Source drive', type: 2, }, ]); @@ -1345,7 +1345,7 @@ describe('Shared: DriveConstraints', function () { ), ).to.deep.equal([ { - message: 'System Drive', + message: 'System drive', type: 1, }, ]); @@ -1359,7 +1359,7 @@ describe('Shared: DriveConstraints', function () { ), ).to.deep.equal([ { - message: 'Large Drive', + message: 'Large drive', type: 1, }, ]); @@ -1386,7 +1386,7 @@ describe('Shared: DriveConstraints', function () { constraints.getListDriveImageCompatibilityStatuses(drives, image), ).to.deep.equal([ { - message: 'Drive Mountpoint Contains Image', + message: 'Source drive', type: 2, }, { @@ -1398,11 +1398,11 @@ describe('Shared: DriveConstraints', function () { type: 2, }, { - message: 'System Drive', + message: 'System drive', type: 1, }, { - message: 'Large Drive', + message: 'Large drive', type: 1, }, { diff --git a/tests/shared/supported-formats.spec.ts b/tests/shared/supported-formats.spec.ts index 368321b1..17bb856a 100644 --- a/tests/shared/supported-formats.spec.ts +++ b/tests/shared/supported-formats.spec.ts @@ -20,53 +20,6 @@ import * as _ from 'lodash'; import * as supportedFormats from '../../lib/shared/supported-formats'; describe('Shared: SupportedFormats', function () { - describe('.getCompressedExtensions()', function () { - it('should return the supported compressed extensions', function () { - const extensions = supportedFormats.getCompressedExtensions().sort(); - expect(extensions).to.deep.equal(['bz2', 'gz', 'xz'].sort()); - }); - }); - - describe('.getNonCompressedExtensions()', function () { - it('should return the supported non compressed extensions', function () { - const extensions = supportedFormats.getNonCompressedExtensions(); - expect(extensions).to.deep.equal([ - 'img', - 'iso', - 'bin', - 'dsk', - 'hddimg', - 'raw', - 'dmg', - 'sdcard', - 'rpi-sdimg', - 'wic', - ]); - }); - }); - - describe('.getArchiveExtensions()', function () { - it('should return the supported archive extensions', function () { - const extensions = supportedFormats.getArchiveExtensions(); - expect(extensions).to.deep.equal(['zip', 'etch']); - }); - }); - - describe('.getAllExtensions()', function () { - it('should return the union of all compressed, uncompressed, and archive extensions', function () { - const archiveExtensions = supportedFormats.getArchiveExtensions(); - const compressedExtensions = supportedFormats.getCompressedExtensions(); - const nonCompressedExtensions = supportedFormats.getNonCompressedExtensions(); - const expected = _.union( - archiveExtensions, - compressedExtensions, - nonCompressedExtensions, - ).sort(); - const extensions = supportedFormats.getAllExtensions(); - expect(extensions.sort()).to.deep.equal(expected); - }); - }); - describe('.looksLikeWindowsImage()', function () { _.each( [