mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-25 12:16:37 +00:00
patch: bump pretty_bytes to 6.1.1
This commit is contained in:
parent
90ec37c194
commit
79e072e1a0
@ -14,12 +14,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg';
|
import ExclamationTriangleSvg from "@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg";
|
||||||
import ChevronDownSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-down.svg';
|
import ChevronDownSvg from "@fortawesome/fontawesome-free/svgs/solid/chevron-down.svg";
|
||||||
import * as sourceDestination from 'etcher-sdk/build/source-destination/';
|
import * as sourceDestination from "etcher-sdk/build/source-destination/";
|
||||||
import * as React from 'react';
|
import * as React from "react";
|
||||||
import { Flex, ModalProps, Txt, Badge, Link, TableColumn } from 'rendition';
|
import { Flex, ModalProps, Txt, Badge, Link, TableColumn } from "rendition";
|
||||||
import styled from 'styled-components';
|
import styled from "styled-components";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getDriveImageCompatibilityStatuses,
|
getDriveImageCompatibilityStatuses,
|
||||||
@ -27,24 +27,24 @@ import {
|
|||||||
DriveStatus,
|
DriveStatus,
|
||||||
DrivelistDrive,
|
DrivelistDrive,
|
||||||
isDriveSizeLarge,
|
isDriveSizeLarge,
|
||||||
} from '../../../../shared/drive-constraints';
|
} from "../../../../shared/drive-constraints";
|
||||||
import { compatibility, warning } from '../../../../shared/messages';
|
import { compatibility, warning } from "../../../../shared/messages";
|
||||||
import * as prettyBytes from 'pretty-bytes';
|
import prettyBytes from "pretty-bytes";
|
||||||
import { getDrives, hasAvailableDrives } from '../../models/available-drives';
|
import { getDrives, hasAvailableDrives } from "../../models/available-drives";
|
||||||
import { getImage, isDriveSelected } from '../../models/selection-state';
|
import { getImage, isDriveSelected } from "../../models/selection-state";
|
||||||
import { store } from '../../models/store';
|
import { store } from "../../models/store";
|
||||||
import { logEvent, logException } from '../../modules/analytics';
|
import { logEvent, logException } from "../../modules/analytics";
|
||||||
import { open as openExternal } from '../../os/open-external/services/open-external';
|
import { open as openExternal } from "../../os/open-external/services/open-external";
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
GenericTableProps,
|
GenericTableProps,
|
||||||
Modal,
|
Modal,
|
||||||
Table,
|
Table,
|
||||||
} from '../../styled-components';
|
} from "../../styled-components";
|
||||||
|
|
||||||
import { SourceMetadata } from '../source-selector/source-selector';
|
import { SourceMetadata } from "../source-selector/source-selector";
|
||||||
import { middleEllipsis } from '../../utils/middle-ellipsis';
|
import { middleEllipsis } from "../../utils/middle-ellipsis";
|
||||||
import * as i18next from 'i18next';
|
import * as i18next from "i18next";
|
||||||
|
|
||||||
interface UsbbootDrive extends sourceDestination.UsbbootDrive {
|
interface UsbbootDrive extends sourceDestination.UsbbootDrive {
|
||||||
progress: number;
|
progress: number;
|
||||||
@ -70,15 +70,15 @@ function isDriverlessDrive(drive: Drive): drive is DriverlessDrive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isDrivelistDrive(drive: Drive): drive is DrivelistDrive {
|
function isDrivelistDrive(drive: Drive): drive is DrivelistDrive {
|
||||||
return typeof (drive as DrivelistDrive).size === 'number';
|
return typeof (drive as DrivelistDrive).size === "number";
|
||||||
}
|
}
|
||||||
|
|
||||||
const DrivesTable = styled((props: GenericTableProps<Drive>) => (
|
const DrivesTable = styled((props: GenericTableProps<Drive>) => (
|
||||||
<Table<Drive> {...props} />
|
<Table<Drive> {...props} />
|
||||||
))`
|
))`
|
||||||
[data-display='table-head'],
|
[data-display="table-head"],
|
||||||
[data-display='table-body'] {
|
[data-display="table-body"] {
|
||||||
> [data-display='table-row'] > [data-display='table-cell'] {
|
> [data-display="table-row"] > [data-display="table-cell"] {
|
||||||
&:nth-child(2) {
|
&:nth-child(2) {
|
||||||
width: 32%;
|
width: 32%;
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ const InitProgress = styled(
|
|||||||
props?: React.ProgressHTMLAttributes<Element>;
|
props?: React.ProgressHTMLAttributes<Element>;
|
||||||
}) => {
|
}) => {
|
||||||
return <progress max="100" value={value} {...props} />;
|
return <progress max="100" value={value} {...props} />;
|
||||||
},
|
}
|
||||||
)`
|
)`
|
||||||
/* Reset the default appearance */
|
/* Reset the default appearance */
|
||||||
appearance: none;
|
appearance: none;
|
||||||
@ -138,7 +138,7 @@ const InitProgress = styled(
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export interface DriveSelectorProps
|
export interface DriveSelectorProps
|
||||||
extends Omit<ModalProps, 'done' | 'cancel' | 'onSelect'> {
|
extends Omit<ModalProps, "done" | "cancel" | "onSelect"> {
|
||||||
write: boolean;
|
write: boolean;
|
||||||
multipleSelection: boolean;
|
multipleSelection: boolean;
|
||||||
showWarnings?: boolean;
|
showWarnings?: boolean;
|
||||||
@ -189,8 +189,8 @@ export class DriveSelector extends React.Component<
|
|||||||
|
|
||||||
this.tableColumns = [
|
this.tableColumns = [
|
||||||
{
|
{
|
||||||
field: 'description',
|
field: "description",
|
||||||
label: i18next.t('drives.name'),
|
label: i18next.t("drives.name"),
|
||||||
render: (description: string, drive: Drive) => {
|
render: (description: string, drive: Drive) => {
|
||||||
if (isDrivelistDrive(drive)) {
|
if (isDrivelistDrive(drive)) {
|
||||||
const isLargeDrive = isDriveSizeLarge(drive);
|
const isLargeDrive = isDriveSizeLarge(drive);
|
||||||
@ -201,7 +201,7 @@ export class DriveSelector extends React.Component<
|
|||||||
{hasWarnings && (
|
{hasWarnings && (
|
||||||
<ExclamationTriangleSvg
|
<ExclamationTriangleSvg
|
||||||
height="1em"
|
height="1em"
|
||||||
fill={drive.isSystem ? '#fca321' : '#8f9297'}
|
fill={drive.isSystem ? "#fca321" : "#8f9297"}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Txt ml={(hasWarnings && 8) || 0}>
|
<Txt ml={(hasWarnings && 8) || 0}>
|
||||||
@ -214,9 +214,9 @@ export class DriveSelector extends React.Component<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'description',
|
field: "description",
|
||||||
key: 'size',
|
key: "size",
|
||||||
label: i18next.t('drives.size'),
|
label: i18next.t("drives.size"),
|
||||||
render: (_description: string, drive: Drive) => {
|
render: (_description: string, drive: Drive) => {
|
||||||
if (isDrivelistDrive(drive) && drive.size !== null) {
|
if (isDrivelistDrive(drive) && drive.size !== null) {
|
||||||
return prettyBytes(drive.size);
|
return prettyBytes(drive.size);
|
||||||
@ -224,17 +224,17 @@ export class DriveSelector extends React.Component<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'description',
|
field: "description",
|
||||||
key: 'link',
|
key: "link",
|
||||||
label: i18next.t('drives.location'),
|
label: i18next.t("drives.location"),
|
||||||
render: (_description: string, drive: Drive) => {
|
render: (_description: string, drive: Drive) => {
|
||||||
return (
|
return (
|
||||||
<Txt>
|
<Txt>
|
||||||
{drive.displayName}
|
{drive.displayName}
|
||||||
{isDriverlessDrive(drive) && (
|
{isDriverlessDrive(drive) && (
|
||||||
<>
|
<>
|
||||||
{' '}
|
{" "}
|
||||||
-{' '}
|
-{" "}
|
||||||
<b>
|
<b>
|
||||||
<a onClick={() => this.installMissingDrivers(drive)}>
|
<a onClick={() => this.installMissingDrivers(drive)}>
|
||||||
{drive.linkCTA}
|
{drive.linkCTA}
|
||||||
@ -247,8 +247,8 @@ export class DriveSelector extends React.Component<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'description',
|
field: "description",
|
||||||
key: 'extra',
|
key: "extra",
|
||||||
// We use an empty React fragment otherwise it uses the field name as label
|
// We use an empty React fragment otherwise it uses the field name as label
|
||||||
label: <></>,
|
label: <></>,
|
||||||
render: (_description: string, drive: Drive) => {
|
render: (_description: string, drive: Drive) => {
|
||||||
@ -300,7 +300,7 @@ export class DriveSelector extends React.Component<
|
|||||||
|
|
||||||
private warningFromStatus(
|
private warningFromStatus(
|
||||||
status: string,
|
status: string,
|
||||||
drive: { device: string; size: number },
|
drive: { device: string; size: number }
|
||||||
) {
|
) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case compatibility.containsImage():
|
case compatibility.containsImage():
|
||||||
@ -320,7 +320,7 @@ export class DriveSelector extends React.Component<
|
|||||||
const statuses: DriveStatus[] = getDriveImageCompatibilityStatuses(
|
const statuses: DriveStatus[] = getDriveImageCompatibilityStatuses(
|
||||||
drive,
|
drive,
|
||||||
this.state.image,
|
this.state.image,
|
||||||
this.props.write,
|
this.props.write
|
||||||
).slice(0, 2);
|
).slice(0, 2);
|
||||||
return (
|
return (
|
||||||
// the column render fn expects a single Element
|
// the column render fn expects a single Element
|
||||||
@ -336,7 +336,7 @@ export class DriveSelector extends React.Component<
|
|||||||
key={status.message}
|
key={status.message}
|
||||||
shade={badgeShade}
|
shade={badgeShade}
|
||||||
mr="8px"
|
mr="8px"
|
||||||
tooltip={this.props.showWarnings ? warningMessage : ''}
|
tooltip={this.props.showWarnings ? warningMessage : ""}
|
||||||
>
|
>
|
||||||
{status.message}
|
{status.message}
|
||||||
</Badge>
|
</Badge>
|
||||||
@ -348,7 +348,7 @@ export class DriveSelector extends React.Component<
|
|||||||
|
|
||||||
private installMissingDrivers(drive: DriverlessDrive) {
|
private installMissingDrivers(drive: DriverlessDrive) {
|
||||||
if (drive.link) {
|
if (drive.link) {
|
||||||
logEvent('Open driver link modal', {
|
logEvent("Open driver link modal", {
|
||||||
url: drive.link,
|
url: drive.link,
|
||||||
});
|
});
|
||||||
this.setState({ missingDriversModal: { drive } });
|
this.setState({ missingDriversModal: { drive } });
|
||||||
@ -400,14 +400,14 @@ export class DriveSelector extends React.Component<
|
|||||||
color="#5b82a7"
|
color="#5b82a7"
|
||||||
style={{ fontWeight: 600 }}
|
style={{ fontWeight: 600 }}
|
||||||
>
|
>
|
||||||
{i18next.t('drives.find', { length: drives.length })}
|
{i18next.t("drives.find", { length: drives.length })}
|
||||||
</Txt>
|
</Txt>
|
||||||
</Flex>
|
</Flex>
|
||||||
}
|
}
|
||||||
titleDetails={<Txt fontSize={11}>{getDrives().length} found</Txt>}
|
titleDetails={<Txt fontSize={11}>{getDrives().length} found</Txt>}
|
||||||
cancel={() => cancel(this.originalList)}
|
cancel={() => cancel(this.originalList)}
|
||||||
done={() => done(selectedList)}
|
done={() => done(selectedList)}
|
||||||
action={i18next.t('drives.select', { select: selectedList.length })}
|
action={i18next.t("drives.select", { select: selectedList.length })}
|
||||||
primaryButtonProps={{
|
primaryButtonProps={{
|
||||||
primary: !showWarnings,
|
primary: !showWarnings,
|
||||||
warning: showWarnings,
|
warning: showWarnings,
|
||||||
@ -439,7 +439,7 @@ export class DriveSelector extends React.Component<
|
|||||||
data={displayedDrives}
|
data={displayedDrives}
|
||||||
disabledRows={disabledDrives}
|
disabledRows={disabledDrives}
|
||||||
getRowClass={(row: Drive) =>
|
getRowClass={(row: Drive) =>
|
||||||
isDrivelistDrive(row) && row.isSystem ? ['system'] : []
|
isDrivelistDrive(row) && row.isSystem ? ["system"] : []
|
||||||
}
|
}
|
||||||
rowKey="displayName"
|
rowKey="displayName"
|
||||||
onCheck={(rows: Drive[]) => {
|
onCheck={(rows: Drive[]) => {
|
||||||
@ -451,14 +451,14 @@ export class DriveSelector extends React.Component<
|
|||||||
const deselecting = selectedList.filter(
|
const deselecting = selectedList.filter(
|
||||||
(selected) =>
|
(selected) =>
|
||||||
newSelection.filter(
|
newSelection.filter(
|
||||||
(row) => row.device === selected.device,
|
(row) => row.device === selected.device
|
||||||
).length === 0,
|
).length === 0
|
||||||
);
|
);
|
||||||
const selecting = newSelection.filter(
|
const selecting = newSelection.filter(
|
||||||
(row) =>
|
(row) =>
|
||||||
selectedList.filter(
|
selectedList.filter(
|
||||||
(selected) => row.device === selected.device,
|
(selected) => row.device === selected.device
|
||||||
).length === 0,
|
).length === 0
|
||||||
);
|
);
|
||||||
deselecting.concat(selecting).forEach((row) => {
|
deselecting.concat(selecting).forEach((row) => {
|
||||||
if (this.props.onSelect) {
|
if (this.props.onSelect) {
|
||||||
@ -488,7 +488,7 @@ export class DriveSelector extends React.Component<
|
|||||||
this.props.onSelect(row);
|
this.props.onSelect(row);
|
||||||
}
|
}
|
||||||
const index = selectedList.findIndex(
|
const index = selectedList.findIndex(
|
||||||
(d) => d.device === row.device,
|
(d) => d.device === row.device
|
||||||
);
|
);
|
||||||
const newList = this.props.multipleSelection
|
const newList = this.props.multipleSelection
|
||||||
? [...selectedList]
|
? [...selectedList]
|
||||||
@ -514,7 +514,7 @@ export class DriveSelector extends React.Component<
|
|||||||
<Flex alignItems="center">
|
<Flex alignItems="center">
|
||||||
<ChevronDownSvg height="1em" fill="currentColor" />
|
<ChevronDownSvg height="1em" fill="currentColor" />
|
||||||
<Txt ml={8}>
|
<Txt ml={8}>
|
||||||
{i18next.t('drives.showHidden', {
|
{i18next.t("drives.showHidden", {
|
||||||
num: numberOfHiddenSystemDrives,
|
num: numberOfHiddenSystemDrives,
|
||||||
})}
|
})}
|
||||||
</Txt>
|
</Txt>
|
||||||
@ -524,8 +524,8 @@ export class DriveSelector extends React.Component<
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{this.props.showWarnings && hasSystemDrives ? (
|
{this.props.showWarnings && hasSystemDrives ? (
|
||||||
<Alert className="system-drive-alert" style={{ width: '67%' }}>
|
<Alert className="system-drive-alert" style={{ width: "67%" }}>
|
||||||
{i18next.t('drives.systemDriveDanger')}
|
{i18next.t("drives.systemDriveDanger")}
|
||||||
</Alert>
|
</Alert>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
@ -545,13 +545,13 @@ export class DriveSelector extends React.Component<
|
|||||||
this.setState({ missingDriversModal: {} });
|
this.setState({ missingDriversModal: {} });
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
action={i18next.t('yesContinue')}
|
action={i18next.t("yesContinue")}
|
||||||
cancelButtonProps={{
|
cancelButtonProps={{
|
||||||
children: i18next.t('cancel'),
|
children: i18next.t("cancel"),
|
||||||
}}
|
}}
|
||||||
children={
|
children={
|
||||||
missingDriversModal.drive.linkMessage ||
|
missingDriversModal.drive.linkMessage ||
|
||||||
i18next.t('drives.openInBrowser', {
|
i18next.t("drives.openInBrowser", {
|
||||||
link: missingDriversModal.drive.link,
|
link: missingDriversModal.drive.link,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg';
|
import ExclamationTriangleSvg from "@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg";
|
||||||
import * as _ from 'lodash';
|
import * as _ from "lodash";
|
||||||
import * as React from 'react';
|
import * as React from "react";
|
||||||
import { Badge, Flex, Txt, ModalProps } from 'rendition';
|
import { Badge, Flex, Txt, ModalProps } from "rendition";
|
||||||
import { Modal, ScrollableFlex } from '../../styled-components';
|
import { Modal, ScrollableFlex } from "../../styled-components";
|
||||||
import { middleEllipsis } from '../../utils/middle-ellipsis';
|
import { middleEllipsis } from "../../utils/middle-ellipsis";
|
||||||
|
|
||||||
import * as prettyBytes from 'pretty-bytes';
|
import prettyBytes from "pretty-bytes";
|
||||||
import { DriveWithWarnings } from '../../pages/main/Flash';
|
import { DriveWithWarnings } from "../../pages/main/Flash";
|
||||||
import * as i18next from 'i18next';
|
import * as i18next from "i18next";
|
||||||
|
|
||||||
const DriveStatusWarningModal = ({
|
const DriveStatusWarningModal = ({
|
||||||
done,
|
done,
|
||||||
@ -18,12 +18,12 @@ const DriveStatusWarningModal = ({
|
|||||||
isSystem: boolean;
|
isSystem: boolean;
|
||||||
drivesWithWarnings: DriveWithWarnings[];
|
drivesWithWarnings: DriveWithWarnings[];
|
||||||
}) => {
|
}) => {
|
||||||
let warningSubtitle = i18next.t('drives.largeDriveWarning');
|
let warningSubtitle = i18next.t("drives.largeDriveWarning");
|
||||||
let warningCta = i18next.t('drives.largeDriveWarningMsg');
|
let warningCta = i18next.t("drives.largeDriveWarningMsg");
|
||||||
|
|
||||||
if (isSystem) {
|
if (isSystem) {
|
||||||
warningSubtitle = i18next.t('drives.systemDriveWarning');
|
warningSubtitle = i18next.t("drives.systemDriveWarning");
|
||||||
warningCta = i18next.t('drives.systemDriveWarningMsg');
|
warningCta = i18next.t("drives.systemDriveWarningMsg");
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@ -34,9 +34,9 @@ const DriveStatusWarningModal = ({
|
|||||||
cancelButtonProps={{
|
cancelButtonProps={{
|
||||||
primary: false,
|
primary: false,
|
||||||
warning: true,
|
warning: true,
|
||||||
children: i18next.t('drives.changeTarget'),
|
children: i18next.t("drives.changeTarget"),
|
||||||
}}
|
}}
|
||||||
action={i18next.t('sure')}
|
action={i18next.t("sure")}
|
||||||
primaryButtonProps={{
|
primaryButtonProps={{
|
||||||
primary: false,
|
primary: false,
|
||||||
outline: true,
|
outline: true,
|
||||||
@ -51,7 +51,7 @@ const DriveStatusWarningModal = ({
|
|||||||
<Flex flexDirection="column">
|
<Flex flexDirection="column">
|
||||||
<ExclamationTriangleSvg height="2em" fill="#fca321" />
|
<ExclamationTriangleSvg height="2em" fill="#fca321" />
|
||||||
<Txt fontSize="24px" color="#fca321">
|
<Txt fontSize="24px" color="#fca321">
|
||||||
{i18next.t('warning')}
|
{i18next.t("warning")}
|
||||||
</Txt>
|
</Txt>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Txt fontSize="24px">{warningSubtitle}</Txt>
|
<Txt fontSize="24px">{warningSubtitle}</Txt>
|
||||||
@ -66,11 +66,11 @@ const DriveStatusWarningModal = ({
|
|||||||
{drivesWithWarnings.map((drive, i, array) => (
|
{drivesWithWarnings.map((drive, i, array) => (
|
||||||
<>
|
<>
|
||||||
<Flex justifyContent="space-between" alignItems="baseline">
|
<Flex justifyContent="space-between" alignItems="baseline">
|
||||||
<strong>{middleEllipsis(drive.description, 28)}</strong>{' '}
|
<strong>{middleEllipsis(drive.description, 28)}</strong>{" "}
|
||||||
{drive.size && prettyBytes(drive.size) + ' '}
|
{drive.size && prettyBytes(drive.size) + " "}
|
||||||
<Badge shade={5}>{drive.statuses[0].message}</Badge>
|
<Badge shade={5}>{drive.statuses[0].message}</Badge>
|
||||||
</Flex>
|
</Flex>
|
||||||
{i !== array.length - 1 ? <hr style={{ width: '100%' }} /> : null}
|
{i !== array.length - 1 ? <hr style={{ width: "100%" }} /> : null}
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
</ScrollableFlex>
|
</ScrollableFlex>
|
||||||
|
@ -14,18 +14,18 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import CopySvg from '@fortawesome/fontawesome-free/svgs/solid/copy.svg';
|
import CopySvg from "@fortawesome/fontawesome-free/svgs/solid/copy.svg";
|
||||||
import FileSvg from '@fortawesome/fontawesome-free/svgs/solid/file.svg';
|
import FileSvg from "@fortawesome/fontawesome-free/svgs/solid/file.svg";
|
||||||
import LinkSvg from '@fortawesome/fontawesome-free/svgs/solid/link.svg';
|
import LinkSvg from "@fortawesome/fontawesome-free/svgs/solid/link.svg";
|
||||||
import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg';
|
import ExclamationTriangleSvg from "@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg";
|
||||||
import ChevronDownSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-down.svg';
|
import ChevronDownSvg from "@fortawesome/fontawesome-free/svgs/solid/chevron-down.svg";
|
||||||
import ChevronRightSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-right.svg';
|
import ChevronRightSvg from "@fortawesome/fontawesome-free/svgs/solid/chevron-right.svg";
|
||||||
import { ipcRenderer, IpcRendererEvent } from 'electron';
|
import { ipcRenderer, IpcRendererEvent } from "electron";
|
||||||
import { uniqBy, isNil } from 'lodash';
|
import { uniqBy, isNil } from "lodash";
|
||||||
import * as path from 'path';
|
import * as path from "path";
|
||||||
import * as prettyBytes from 'pretty-bytes';
|
import prettyBytes from "pretty-bytes";
|
||||||
import * as React from 'react';
|
import * as React from "react";
|
||||||
import { requestMetadata } from '../../app';
|
import { requestMetadata } from "../../app";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Flex,
|
Flex,
|
||||||
@ -36,17 +36,17 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
Spinner,
|
Spinner,
|
||||||
Link,
|
Link,
|
||||||
} from 'rendition';
|
} from "rendition";
|
||||||
import styled from 'styled-components';
|
import styled from "styled-components";
|
||||||
|
|
||||||
import * as errors from '../../../../shared/errors';
|
import * as errors from "../../../../shared/errors";
|
||||||
import * as messages from '../../../../shared/messages';
|
import * as messages from "../../../../shared/messages";
|
||||||
import * as supportedFormats from '../../../../shared/supported-formats';
|
import * as supportedFormats from "../../../../shared/supported-formats";
|
||||||
import * as selectionState from '../../models/selection-state';
|
import * as selectionState from "../../models/selection-state";
|
||||||
import { observe } from '../../models/store';
|
import { observe } from "../../models/store";
|
||||||
import * as analytics from '../../modules/analytics';
|
import * as analytics from "../../modules/analytics";
|
||||||
import * as exceptionReporter from '../../modules/exception-reporter';
|
import * as exceptionReporter from "../../modules/exception-reporter";
|
||||||
import * as osDialog from '../../os/dialog';
|
import * as osDialog from "../../os/dialog";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChangeButton,
|
ChangeButton,
|
||||||
@ -55,24 +55,24 @@ import {
|
|||||||
StepButton,
|
StepButton,
|
||||||
StepNameButton,
|
StepNameButton,
|
||||||
ScrollableFlex,
|
ScrollableFlex,
|
||||||
} from '../../styled-components';
|
} from "../../styled-components";
|
||||||
import { colors } from '../../theme';
|
import { colors } from "../../theme";
|
||||||
import { middleEllipsis } from '../../utils/middle-ellipsis';
|
import { middleEllipsis } from "../../utils/middle-ellipsis";
|
||||||
import { SVGIcon } from '../svg-icon/svg-icon';
|
import { SVGIcon } from "../svg-icon/svg-icon";
|
||||||
|
|
||||||
import ImageSvg from '../../../assets/image.svg';
|
import ImageSvg from "../../../assets/image.svg";
|
||||||
import SrcSvg from '../../../assets/src.svg';
|
import SrcSvg from "../../../assets/src.svg";
|
||||||
import { DriveSelector } from '../drive-selector/drive-selector';
|
import { DriveSelector } from "../drive-selector/drive-selector";
|
||||||
import { DrivelistDrive } from '../../../../shared/drive-constraints';
|
import { DrivelistDrive } from "../../../../shared/drive-constraints";
|
||||||
import { isJson } from '../../../../shared/utils';
|
import { isJson } from "../../../../shared/utils";
|
||||||
import {
|
import {
|
||||||
SourceMetadata,
|
SourceMetadata,
|
||||||
Authentication,
|
Authentication,
|
||||||
Source,
|
Source,
|
||||||
} from '../../../../shared/typings/source-selector';
|
} from "../../../../shared/typings/source-selector";
|
||||||
import * as i18next from 'i18next';
|
import * as i18next from "i18next";
|
||||||
|
|
||||||
const recentUrlImagesKey = 'recentUrlImages';
|
const recentUrlImagesKey = "recentUrlImages";
|
||||||
|
|
||||||
function normalizeRecentUrlImages(urls: any[]): URL[] {
|
function normalizeRecentUrlImages(urls: any[]): URL[] {
|
||||||
if (!Array.isArray(urls)) {
|
if (!Array.isArray(urls)) {
|
||||||
@ -94,7 +94,7 @@ function normalizeRecentUrlImages(urls: any[]): URL[] {
|
|||||||
function getRecentUrlImages(): URL[] {
|
function getRecentUrlImages(): URL[] {
|
||||||
let urls = [];
|
let urls = [];
|
||||||
try {
|
try {
|
||||||
urls = JSON.parse(localStorage.getItem(recentUrlImagesKey) || '[]');
|
urls = JSON.parse(localStorage.getItem(recentUrlImagesKey) || "[]");
|
||||||
} catch {
|
} catch {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
@ -107,7 +107,7 @@ function setRecentUrlImages(urls: URL[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isURL = (imagePath: string) =>
|
const isURL = (imagePath: string) =>
|
||||||
imagePath.startsWith('https://') || imagePath.startsWith('http://');
|
imagePath.startsWith("https://") || imagePath.startsWith("http://");
|
||||||
|
|
||||||
const Card = styled(BaseCard)`
|
const Card = styled(BaseCard)`
|
||||||
hr {
|
hr {
|
||||||
@ -136,7 +136,7 @@ function getState() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isString(value: any): value is string {
|
function isString(value: any): value is string {
|
||||||
return typeof value === 'string';
|
return typeof value === "string";
|
||||||
}
|
}
|
||||||
|
|
||||||
const URLSelector = ({
|
const URLSelector = ({
|
||||||
@ -146,12 +146,12 @@ const URLSelector = ({
|
|||||||
done: (imageURL: string, auth?: Authentication) => void;
|
done: (imageURL: string, auth?: Authentication) => void;
|
||||||
cancel: () => void;
|
cancel: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const [imageURL, setImageURL] = React.useState('');
|
const [imageURL, setImageURL] = React.useState("");
|
||||||
const [recentImages, setRecentImages] = React.useState<URL[]>([]);
|
const [recentImages, setRecentImages] = React.useState<URL[]>([]);
|
||||||
const [loading, setLoading] = React.useState(false);
|
const [loading, setLoading] = React.useState(false);
|
||||||
const [showBasicAuth, setShowBasicAuth] = React.useState(false);
|
const [showBasicAuth, setShowBasicAuth] = React.useState(false);
|
||||||
const [username, setUsername] = React.useState('');
|
const [username, setUsername] = React.useState("");
|
||||||
const [password, setPassword] = React.useState('');
|
const [password, setPassword] = React.useState("");
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const fetchRecentUrlImages = async () => {
|
const fetchRecentUrlImages = async () => {
|
||||||
const recentUrlImages: URL[] = await getRecentUrlImages();
|
const recentUrlImages: URL[] = await getRecentUrlImages();
|
||||||
@ -165,7 +165,7 @@ const URLSelector = ({
|
|||||||
primaryButtonProps={{
|
primaryButtonProps={{
|
||||||
disabled: loading || !imageURL,
|
disabled: loading || !imageURL,
|
||||||
}}
|
}}
|
||||||
action={loading ? <Spinner /> : i18next.t('ok')}
|
action={loading ? <Spinner /> : i18next.t("ok")}
|
||||||
done={async () => {
|
done={async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const urlStrings = recentImages.map((url: URL) => url.href);
|
const urlStrings = recentImages.map((url: URL) => url.href);
|
||||||
@ -179,13 +179,13 @@ const URLSelector = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex flexDirection="column">
|
<Flex flexDirection="column">
|
||||||
<Flex mb={15} style={{ width: '100%' }} flexDirection="column">
|
<Flex mb={15} style={{ width: "100%" }} flexDirection="column">
|
||||||
<Txt mb="10px" fontSize="24px">
|
<Txt mb="10px" fontSize="24px">
|
||||||
{i18next.t('source.useSourceURL')}
|
{i18next.t("source.useSourceURL")}
|
||||||
</Txt>
|
</Txt>
|
||||||
<Input
|
<Input
|
||||||
value={imageURL}
|
value={imageURL}
|
||||||
placeholder={i18next.t('source.enterValidURL')}
|
placeholder={i18next.t("source.enterValidURL")}
|
||||||
type="text"
|
type="text"
|
||||||
onChange={(evt: React.ChangeEvent<HTMLInputElement>) =>
|
onChange={(evt: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
setImageURL(evt.target.value)
|
setImageURL(evt.target.value)
|
||||||
@ -197,8 +197,8 @@ const URLSelector = ({
|
|||||||
fontSize="14px"
|
fontSize="14px"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (showBasicAuth) {
|
if (showBasicAuth) {
|
||||||
setUsername('');
|
setUsername("");
|
||||||
setPassword('');
|
setPassword("");
|
||||||
}
|
}
|
||||||
setShowBasicAuth(!showBasicAuth);
|
setShowBasicAuth(!showBasicAuth);
|
||||||
}}
|
}}
|
||||||
@ -210,7 +210,7 @@ const URLSelector = ({
|
|||||||
{!showBasicAuth && (
|
{!showBasicAuth && (
|
||||||
<ChevronRightSvg height="1em" fill="currentColor" />
|
<ChevronRightSvg height="1em" fill="currentColor" />
|
||||||
)}
|
)}
|
||||||
<Txt ml={8}>{i18next.t('source.auth')}</Txt>
|
<Txt ml={8}>{i18next.t("source.auth")}</Txt>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Link>
|
</Link>
|
||||||
{showBasicAuth && (
|
{showBasicAuth && (
|
||||||
@ -218,7 +218,7 @@ const URLSelector = ({
|
|||||||
<Input
|
<Input
|
||||||
mb={15}
|
mb={15}
|
||||||
value={username}
|
value={username}
|
||||||
placeholder={i18next.t('source.username')}
|
placeholder={i18next.t("source.username")}
|
||||||
type="text"
|
type="text"
|
||||||
onChange={(evt: React.ChangeEvent<HTMLInputElement>) =>
|
onChange={(evt: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
setUsername(evt.target.value)
|
setUsername(evt.target.value)
|
||||||
@ -226,7 +226,7 @@ const URLSelector = ({
|
|||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
value={password}
|
value={password}
|
||||||
placeholder={i18next.t('source.password')}
|
placeholder={i18next.t("source.password")}
|
||||||
type="password"
|
type="password"
|
||||||
onChange={(evt: React.ChangeEvent<HTMLInputElement>) =>
|
onChange={(evt: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
setPassword(evt.target.value)
|
setPassword(evt.target.value)
|
||||||
@ -249,10 +249,10 @@ const URLSelector = ({
|
|||||||
setImageURL(recent.href);
|
setImageURL(recent.href);
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
overflowWrap: 'break-word',
|
overflowWrap: "break-word",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{recent.pathname.split('/').pop()} - {recent.href}
|
{recent.pathname.split("/").pop()} - {recent.href}
|
||||||
</Txt>
|
</Txt>
|
||||||
))
|
))
|
||||||
.reverse()}
|
.reverse()}
|
||||||
@ -284,7 +284,7 @@ const FlowSelector = styled(
|
|||||||
>
|
>
|
||||||
{flow.label}
|
{flow.label}
|
||||||
</StepButton>
|
</StepButton>
|
||||||
),
|
)
|
||||||
)`
|
)`
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
color: rgba(255, 255, 255, 0.7);
|
color: rgba(255, 255, 255, 0.7);
|
||||||
@ -349,20 +349,20 @@ export class SourceSelector extends React.Component<
|
|||||||
this.unsubscribe = observe(() => {
|
this.unsubscribe = observe(() => {
|
||||||
this.setState(getState());
|
this.setState(getState());
|
||||||
});
|
});
|
||||||
ipcRenderer.on('select-image', this.onSelectImage);
|
ipcRenderer.on("select-image", this.onSelectImage);
|
||||||
ipcRenderer.send('source-selector-ready');
|
ipcRenderer.send("source-selector-ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
this.unsubscribe?.();
|
this.unsubscribe?.();
|
||||||
ipcRenderer.removeListener('select-image', this.onSelectImage);
|
ipcRenderer.removeListener("select-image", this.onSelectImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onSelectImage(_event: IpcRendererEvent, imagePath: string) {
|
private async onSelectImage(_event: IpcRendererEvent, imagePath: string) {
|
||||||
this.setState({ imageLoading: true });
|
this.setState({ imageLoading: true });
|
||||||
await this.selectSource(
|
await this.selectSource(
|
||||||
imagePath,
|
imagePath,
|
||||||
isURL(this.normalizeImagePath(imagePath)) ? 'Http' : 'File',
|
isURL(this.normalizeImagePath(imagePath)) ? "Http" : "File"
|
||||||
).promise;
|
).promise;
|
||||||
this.setState({ imageLoading: false });
|
this.setState({ imageLoading: false });
|
||||||
}
|
}
|
||||||
@ -376,7 +376,7 @@ export class SourceSelector extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private reselectSource() {
|
private reselectSource() {
|
||||||
analytics.logEvent('Reselect image', {
|
analytics.logEvent("Reselect image", {
|
||||||
previousImage: selectionState.getImage(),
|
previousImage: selectionState.getImage(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -386,7 +386,7 @@ export class SourceSelector extends React.Component<
|
|||||||
private selectSource(
|
private selectSource(
|
||||||
selected: string | DrivelistDrive,
|
selected: string | DrivelistDrive,
|
||||||
SourceType: Source,
|
SourceType: Source,
|
||||||
auth?: Authentication,
|
auth?: Authentication
|
||||||
): { promise: Promise<void>; cancel: () => void } {
|
): { promise: Promise<void>; cancel: () => void } {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
return {
|
return {
|
||||||
@ -398,23 +398,23 @@ export class SourceSelector extends React.Component<
|
|||||||
let metadata: SourceMetadata | undefined;
|
let metadata: SourceMetadata | undefined;
|
||||||
if (isString(selected)) {
|
if (isString(selected)) {
|
||||||
if (
|
if (
|
||||||
SourceType === 'Http' &&
|
SourceType === "Http" &&
|
||||||
!isURL(this.normalizeImagePath(selected))
|
!isURL(this.normalizeImagePath(selected))
|
||||||
) {
|
) {
|
||||||
this.handleError(
|
this.handleError(
|
||||||
i18next.t('source.unsupportedProtocol'),
|
i18next.t("source.unsupportedProtocol"),
|
||||||
selected,
|
selected,
|
||||||
messages.error.unsupportedProtocol(),
|
messages.error.unsupportedProtocol()
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supportedFormats.looksLikeWindowsImage(selected)) {
|
if (supportedFormats.looksLikeWindowsImage(selected)) {
|
||||||
analytics.logEvent('Possibly Windows image', { image: selected });
|
analytics.logEvent("Possibly Windows image", { image: selected });
|
||||||
this.setState({
|
this.setState({
|
||||||
warning: {
|
warning: {
|
||||||
message: messages.warning.looksLikeWindowsImage(),
|
message: messages.warning.looksLikeWindowsImage(),
|
||||||
title: i18next.t('source.windowsImage'),
|
title: i18next.t("source.windowsImage"),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -426,29 +426,29 @@ export class SourceSelector extends React.Component<
|
|||||||
metadata = await requestMetadata({ selected, SourceType, auth });
|
metadata = await requestMetadata({ selected, SourceType, auth });
|
||||||
|
|
||||||
if (!metadata?.hasMBR && this.state.warning === null) {
|
if (!metadata?.hasMBR && this.state.warning === null) {
|
||||||
analytics.logEvent('Missing partition table', { metadata });
|
analytics.logEvent("Missing partition table", { metadata });
|
||||||
this.setState({
|
this.setState({
|
||||||
warning: {
|
warning: {
|
||||||
message: messages.warning.missingPartitionTable(),
|
message: messages.warning.missingPartitionTable(),
|
||||||
title: i18next.t('source.partitionTable'),
|
title: i18next.t("source.partitionTable"),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.handleError(
|
this.handleError(
|
||||||
i18next.t('source.errorOpen'),
|
i18next.t("source.errorOpen"),
|
||||||
sourcePath,
|
sourcePath,
|
||||||
messages.error.openSource(sourcePath, error.message),
|
messages.error.openSource(sourcePath, error.message),
|
||||||
error,
|
error
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (selected.partitionTableType === null) {
|
if (selected.partitionTableType === null) {
|
||||||
analytics.logEvent('Missing partition table', { selected });
|
analytics.logEvent("Missing partition table", { selected });
|
||||||
this.setState({
|
this.setState({
|
||||||
warning: {
|
warning: {
|
||||||
message: messages.warning.driveMissingPartitionTable(),
|
message: messages.warning.driveMissingPartitionTable(),
|
||||||
title: i18next.t('source.partitionTable'),
|
title: i18next.t("source.partitionTable"),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -456,8 +456,8 @@ export class SourceSelector extends React.Component<
|
|||||||
path: selected.device,
|
path: selected.device,
|
||||||
displayName: selected.displayName,
|
displayName: selected.displayName,
|
||||||
description: selected.displayName,
|
description: selected.displayName,
|
||||||
size: selected.size as SourceMetadata['size'],
|
size: selected.size as SourceMetadata["size"],
|
||||||
SourceType: 'BlockDevice',
|
SourceType: "BlockDevice",
|
||||||
drive: selected,
|
drive: selected,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -466,7 +466,7 @@ export class SourceSelector extends React.Component<
|
|||||||
metadata.auth = auth;
|
metadata.auth = auth;
|
||||||
metadata.SourceType = SourceType;
|
metadata.SourceType = SourceType;
|
||||||
selectionState.selectSource(metadata);
|
selectionState.selectSource(metadata);
|
||||||
analytics.logEvent('Select image', {
|
analytics.logEvent("Select image", {
|
||||||
// An easy way so we can quickly identify if we're making use of
|
// An easy way so we can quickly identify if we're making use of
|
||||||
// certain features without printing pages of text to DevTools.
|
// certain features without printing pages of text to DevTools.
|
||||||
image: {
|
image: {
|
||||||
@ -484,7 +484,7 @@ export class SourceSelector extends React.Component<
|
|||||||
title: string,
|
title: string,
|
||||||
sourcePath: string,
|
sourcePath: string,
|
||||||
description: string,
|
description: string,
|
||||||
error?: Error,
|
error?: Error
|
||||||
) {
|
) {
|
||||||
const imageError = errors.createUserError({
|
const imageError = errors.createUserError({
|
||||||
title,
|
title,
|
||||||
@ -499,7 +499,7 @@ export class SourceSelector extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async openImageSelector() {
|
private async openImageSelector() {
|
||||||
analytics.logEvent('Open image selector');
|
analytics.logEvent("Open image selector");
|
||||||
this.setState({ imageSelectorOpen: true });
|
this.setState({ imageSelectorOpen: true });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -507,10 +507,10 @@ export class SourceSelector extends React.Component<
|
|||||||
// Avoid analytics and selection state changes
|
// Avoid analytics and selection state changes
|
||||||
// if no file was resolved from the dialog.
|
// if no file was resolved from the dialog.
|
||||||
if (!imagePath) {
|
if (!imagePath) {
|
||||||
analytics.logEvent('Image selector closed');
|
analytics.logEvent("Image selector closed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.selectSource(imagePath, 'File').promise;
|
await this.selectSource(imagePath, "File").promise;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
exceptionReporter.report(error);
|
exceptionReporter.report(error);
|
||||||
} finally {
|
} finally {
|
||||||
@ -521,12 +521,12 @@ export class SourceSelector extends React.Component<
|
|||||||
private async onDrop(event: React.DragEvent<HTMLDivElement>) {
|
private async onDrop(event: React.DragEvent<HTMLDivElement>) {
|
||||||
const [file] = event.dataTransfer.files;
|
const [file] = event.dataTransfer.files;
|
||||||
if (file) {
|
if (file) {
|
||||||
await this.selectSource(file.path, 'File').promise;
|
await this.selectSource(file.path, "File").promise;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private openURLSelector() {
|
private openURLSelector() {
|
||||||
analytics.logEvent('Open image URL selector');
|
analytics.logEvent("Open image URL selector");
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
showURLSelector: true,
|
showURLSelector: true,
|
||||||
@ -534,7 +534,7 @@ export class SourceSelector extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private openDriveSelector() {
|
private openDriveSelector() {
|
||||||
analytics.logEvent('Open drive selector');
|
analytics.logEvent("Open drive selector");
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
showDriveSelector: true,
|
showDriveSelector: true,
|
||||||
@ -552,7 +552,7 @@ export class SourceSelector extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private showSelectedImageDetails() {
|
private showSelectedImageDetails() {
|
||||||
analytics.logEvent('Show selected image tooltip', {
|
analytics.logEvent("Show selected image tooltip", {
|
||||||
imagePath: selectionState.getImage()?.path,
|
imagePath: selectionState.getImage()?.path,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -590,11 +590,11 @@ export class SourceSelector extends React.Component<
|
|||||||
// noop
|
// noop
|
||||||
};
|
};
|
||||||
image.name = image.description || image.name;
|
image.name = image.description || image.name;
|
||||||
const imagePath = image.path || image.displayName || '';
|
const imagePath = image.path || image.displayName || "";
|
||||||
const imageBasename = path.basename(imagePath);
|
const imageBasename = path.basename(imagePath);
|
||||||
const imageName = image.name || '';
|
const imageName = image.name || "";
|
||||||
const imageSize = image.size;
|
const imageSize = image.size;
|
||||||
const imageLogo = image.logo || '';
|
const imageLogo = image.logo || "";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -634,7 +634,7 @@ export class SourceSelector extends React.Component<
|
|||||||
mb={14}
|
mb={14}
|
||||||
onClick={() => this.reselectSource()}
|
onClick={() => this.reselectSource()}
|
||||||
>
|
>
|
||||||
{i18next.t('cancel')}
|
{i18next.t("cancel")}
|
||||||
</ChangeButton>
|
</ChangeButton>
|
||||||
)}
|
)}
|
||||||
{!isNil(imageSize) && !imageLoading && (
|
{!isNil(imageSize) && !imageLoading && (
|
||||||
@ -649,7 +649,7 @@ export class SourceSelector extends React.Component<
|
|||||||
key="Flash from file"
|
key="Flash from file"
|
||||||
flow={{
|
flow={{
|
||||||
onClick: () => this.openImageSelector(),
|
onClick: () => this.openImageSelector(),
|
||||||
label: i18next.t('source.fromFile'),
|
label: i18next.t("source.fromFile"),
|
||||||
icon: <FileSvg height="1em" fill="currentColor" />,
|
icon: <FileSvg height="1em" fill="currentColor" />,
|
||||||
}}
|
}}
|
||||||
onMouseEnter={() => this.setDefaultFlowActive(false)}
|
onMouseEnter={() => this.setDefaultFlowActive(false)}
|
||||||
@ -659,7 +659,7 @@ export class SourceSelector extends React.Component<
|
|||||||
key="Flash from URL"
|
key="Flash from URL"
|
||||||
flow={{
|
flow={{
|
||||||
onClick: () => this.openURLSelector(),
|
onClick: () => this.openURLSelector(),
|
||||||
label: i18next.t('source.fromURL'),
|
label: i18next.t("source.fromURL"),
|
||||||
icon: <LinkSvg height="1em" fill="currentColor" />,
|
icon: <LinkSvg height="1em" fill="currentColor" />,
|
||||||
}}
|
}}
|
||||||
onMouseEnter={() => this.setDefaultFlowActive(false)}
|
onMouseEnter={() => this.setDefaultFlowActive(false)}
|
||||||
@ -669,7 +669,7 @@ export class SourceSelector extends React.Component<
|
|||||||
key="Clone drive"
|
key="Clone drive"
|
||||||
flow={{
|
flow={{
|
||||||
onClick: () => this.openDriveSelector(),
|
onClick: () => this.openDriveSelector(),
|
||||||
label: i18next.t('source.clone'),
|
label: i18next.t("source.clone"),
|
||||||
icon: <CopySvg height="1em" fill="currentColor" />,
|
icon: <CopySvg height="1em" fill="currentColor" />,
|
||||||
}}
|
}}
|
||||||
onMouseEnter={() => this.setDefaultFlowActive(false)}
|
onMouseEnter={() => this.setDefaultFlowActive(false)}
|
||||||
@ -682,15 +682,15 @@ export class SourceSelector extends React.Component<
|
|||||||
{this.state.warning != null && (
|
{this.state.warning != null && (
|
||||||
<SmallModal
|
<SmallModal
|
||||||
style={{
|
style={{
|
||||||
boxShadow: '0 3px 7px rgba(0, 0, 0, 0.3)',
|
boxShadow: "0 3px 7px rgba(0, 0, 0, 0.3)",
|
||||||
}}
|
}}
|
||||||
titleElement={
|
titleElement={
|
||||||
<span>
|
<span>
|
||||||
<ExclamationTriangleSvg fill="#fca321" height="1em" />{' '}
|
<ExclamationTriangleSvg fill="#fca321" height="1em" />{" "}
|
||||||
<span>{this.state.warning.title}</span>
|
<span>{this.state.warning.title}</span>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
action={i18next.t('continue')}
|
action={i18next.t("continue")}
|
||||||
cancel={() => {
|
cancel={() => {
|
||||||
this.setState({ warning: null });
|
this.setState({ warning: null });
|
||||||
this.reselectSource();
|
this.reselectSource();
|
||||||
@ -708,17 +708,17 @@ export class SourceSelector extends React.Component<
|
|||||||
|
|
||||||
{showImageDetails && (
|
{showImageDetails && (
|
||||||
<SmallModal
|
<SmallModal
|
||||||
title={i18next.t('source.image')}
|
title={i18next.t("source.image")}
|
||||||
done={() => {
|
done={() => {
|
||||||
this.setState({ showImageDetails: false });
|
this.setState({ showImageDetails: false });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Txt.p>
|
<Txt.p>
|
||||||
<Txt.span bold>{i18next.t('source.name')}</Txt.span>
|
<Txt.span bold>{i18next.t("source.name")}</Txt.span>
|
||||||
<Txt.span>{imageName || imageBasename}</Txt.span>
|
<Txt.span>{imageName || imageBasename}</Txt.span>
|
||||||
</Txt.p>
|
</Txt.p>
|
||||||
<Txt.p>
|
<Txt.p>
|
||||||
<Txt.span bold>{i18next.t('source.path')}</Txt.span>
|
<Txt.span bold>{i18next.t("source.path")}</Txt.span>
|
||||||
<Txt.span>{imagePath}</Txt.span>
|
<Txt.span>{imagePath}</Txt.span>
|
||||||
</Txt.p>
|
</Txt.p>
|
||||||
</SmallModal>
|
</SmallModal>
|
||||||
@ -736,13 +736,13 @@ export class SourceSelector extends React.Component<
|
|||||||
// Avoid analytics and selection state changes
|
// Avoid analytics and selection state changes
|
||||||
// if no file was resolved from the dialog.
|
// if no file was resolved from the dialog.
|
||||||
if (!imageURL) {
|
if (!imageURL) {
|
||||||
analytics.logEvent('URL selector closed');
|
analytics.logEvent("URL selector closed");
|
||||||
} else {
|
} else {
|
||||||
let promise;
|
let promise;
|
||||||
({ promise, cancel: cancelURLSelection } = this.selectSource(
|
({ promise, cancel: cancelURLSelection } = this.selectSource(
|
||||||
imageURL,
|
imageURL,
|
||||||
'Http',
|
"Http",
|
||||||
auth,
|
auth
|
||||||
));
|
));
|
||||||
await promise;
|
await promise;
|
||||||
}
|
}
|
||||||
@ -757,14 +757,14 @@ export class SourceSelector extends React.Component<
|
|||||||
<DriveSelector
|
<DriveSelector
|
||||||
write={false}
|
write={false}
|
||||||
multipleSelection={false}
|
multipleSelection={false}
|
||||||
titleLabel={i18next.t('source.selectSource')}
|
titleLabel={i18next.t("source.selectSource")}
|
||||||
emptyListLabel={i18next.t('source.plugSource')}
|
emptyListLabel={i18next.t("source.plugSource")}
|
||||||
emptyListIcon={<SrcSvg width="40px" />}
|
emptyListIcon={<SrcSvg width="40px" />}
|
||||||
cancel={(originalList) => {
|
cancel={(originalList) => {
|
||||||
if (originalList.length) {
|
if (originalList.length) {
|
||||||
const originalSource = originalList[0];
|
const originalSource = originalList[0];
|
||||||
if (selectionImage?.drive?.device !== originalSource.device) {
|
if (selectionImage?.drive?.device !== originalSource.device) {
|
||||||
this.selectSource(originalSource, 'BlockDevice');
|
this.selectSource(originalSource, "BlockDevice");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
selectionState.deselectImage();
|
selectionState.deselectImage();
|
||||||
@ -779,7 +779,7 @@ export class SourceSelector extends React.Component<
|
|||||||
) {
|
) {
|
||||||
return selectionState.deselectImage();
|
return selectionState.deselectImage();
|
||||||
}
|
}
|
||||||
this.selectSource(drive, 'BlockDevice');
|
this.selectSource(drive, "BlockDevice");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -14,25 +14,25 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg';
|
import ExclamationTriangleSvg from "@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg";
|
||||||
import * as React from 'react';
|
import * as React from "react";
|
||||||
import { Flex, FlexProps, Txt } from 'rendition';
|
import { Flex, FlexProps, Txt } from "rendition";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getDriveImageCompatibilityStatuses,
|
getDriveImageCompatibilityStatuses,
|
||||||
DriveStatus,
|
DriveStatus,
|
||||||
} from '../../../../shared/drive-constraints';
|
} from "../../../../shared/drive-constraints";
|
||||||
import { compatibility, warning } from '../../../../shared/messages';
|
import { compatibility, warning } from "../../../../shared/messages";
|
||||||
import * as prettyBytes from 'pretty-bytes';
|
import prettyBytes from "pretty-bytes";
|
||||||
import { getImage, getSelectedDrives } from '../../models/selection-state';
|
import { getImage, getSelectedDrives } from "../../models/selection-state";
|
||||||
import {
|
import {
|
||||||
ChangeButton,
|
ChangeButton,
|
||||||
DetailsText,
|
DetailsText,
|
||||||
StepButton,
|
StepButton,
|
||||||
StepNameButton,
|
StepNameButton,
|
||||||
} from '../../styled-components';
|
} from "../../styled-components";
|
||||||
import { middleEllipsis } from '../../utils/middle-ellipsis';
|
import { middleEllipsis } from "../../utils/middle-ellipsis";
|
||||||
import * as i18next from 'i18next';
|
import * as i18next from "i18next";
|
||||||
|
|
||||||
interface TargetSelectorProps {
|
interface TargetSelectorProps {
|
||||||
targets: any[];
|
targets: any[];
|
||||||
@ -53,7 +53,7 @@ function getDriveWarning(status: DriveStatus) {
|
|||||||
case compatibility.system():
|
case compatibility.system():
|
||||||
return warning.systemDrive();
|
return warning.systemDrive();
|
||||||
default:
|
default:
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,12 +64,12 @@ const DriveCompatibilityWarning = ({
|
|||||||
warnings: string[];
|
warnings: string[];
|
||||||
} & FlexProps) => {
|
} & FlexProps) => {
|
||||||
const systemDrive = warnings.find(
|
const systemDrive = warnings.find(
|
||||||
(message) => message === warning.systemDrive(),
|
(message) => message === warning.systemDrive()
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<Flex tooltip={warnings.join(', ')} {...props}>
|
<Flex tooltip={warnings.join(", ")} {...props}>
|
||||||
<ExclamationTriangleSvg
|
<ExclamationTriangleSvg
|
||||||
fill={systemDrive ? '#fca321' : '#8f9297'}
|
fill={systemDrive ? "#fca321" : "#8f9297"}
|
||||||
height="1em"
|
height="1em"
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -84,7 +84,7 @@ export function TargetSelectorButton(props: TargetSelectorProps) {
|
|||||||
const warnings = getDriveImageCompatibilityStatuses(
|
const warnings = getDriveImageCompatibilityStatuses(
|
||||||
target,
|
target,
|
||||||
getImage(),
|
getImage(),
|
||||||
true,
|
true
|
||||||
).map(getDriveWarning);
|
).map(getDriveWarning);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -96,7 +96,7 @@ export function TargetSelectorButton(props: TargetSelectorProps) {
|
|||||||
</StepNameButton>
|
</StepNameButton>
|
||||||
{!props.flashing && (
|
{!props.flashing && (
|
||||||
<ChangeButton plain mb={14} onClick={props.reselectDrive}>
|
<ChangeButton plain mb={14} onClick={props.reselectDrive}>
|
||||||
{i18next.t('target.change')}
|
{i18next.t("target.change")}
|
||||||
</ChangeButton>
|
</ChangeButton>
|
||||||
)}
|
)}
|
||||||
{target.size != null && (
|
{target.size != null && (
|
||||||
@ -112,13 +112,13 @@ export function TargetSelectorButton(props: TargetSelectorProps) {
|
|||||||
const warnings = getDriveImageCompatibilityStatuses(
|
const warnings = getDriveImageCompatibilityStatuses(
|
||||||
target,
|
target,
|
||||||
getImage(),
|
getImage(),
|
||||||
true,
|
true
|
||||||
).map(getDriveWarning);
|
).map(getDriveWarning);
|
||||||
targetsTemplate.push(
|
targetsTemplate.push(
|
||||||
<DetailsText
|
<DetailsText
|
||||||
key={target.device}
|
key={target.device}
|
||||||
tooltip={`${target.description} ${target.displayName} ${
|
tooltip={`${target.description} ${target.displayName} ${
|
||||||
target.size != null ? prettyBytes(target.size) : ''
|
target.size != null ? prettyBytes(target.size) : ""
|
||||||
}`}
|
}`}
|
||||||
px={21}
|
px={21}
|
||||||
>
|
>
|
||||||
@ -127,17 +127,17 @@ export function TargetSelectorButton(props: TargetSelectorProps) {
|
|||||||
) : null}
|
) : null}
|
||||||
<Txt mr={2}>{middleEllipsis(target.description, 14)}</Txt>
|
<Txt mr={2}>{middleEllipsis(target.description, 14)}</Txt>
|
||||||
{target.size != null && <Txt>{prettyBytes(target.size)}</Txt>}
|
{target.size != null && <Txt>{prettyBytes(target.size)}</Txt>}
|
||||||
</DetailsText>,
|
</DetailsText>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StepNameButton plain tooltip={props.tooltip}>
|
<StepNameButton plain tooltip={props.tooltip}>
|
||||||
{targets.length} {i18next.t('target.targets')}
|
{targets.length} {i18next.t("target.targets")}
|
||||||
</StepNameButton>
|
</StepNameButton>
|
||||||
{!props.flashing && (
|
{!props.flashing && (
|
||||||
<ChangeButton plain onClick={props.reselectDrive} mb={14}>
|
<ChangeButton plain onClick={props.reselectDrive} mb={14}>
|
||||||
{i18next.t('target.change')}
|
{i18next.t("target.change")}
|
||||||
</ChangeButton>
|
</ChangeButton>
|
||||||
)}
|
)}
|
||||||
{targetsTemplate}
|
{targetsTemplate}
|
||||||
@ -152,7 +152,7 @@ export function TargetSelectorButton(props: TargetSelectorProps) {
|
|||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
onClick={props.openDriveSelector}
|
onClick={props.openDriveSelector}
|
||||||
>
|
>
|
||||||
{i18next.t('target.selectTarget')}
|
{i18next.t("target.selectTarget")}
|
||||||
</StepButton>
|
</StepButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as prettyBytes from 'pretty-bytes';
|
import prettyBytes from "pretty-bytes";
|
||||||
import * as i18next from 'i18next';
|
import * as i18next from "i18next";
|
||||||
|
|
||||||
export interface FlashState {
|
export interface FlashState {
|
||||||
active: number;
|
active: number;
|
||||||
@ -23,61 +23,61 @@ export interface FlashState {
|
|||||||
percentage?: number;
|
percentage?: number;
|
||||||
speed: number;
|
speed: number;
|
||||||
position: number;
|
position: number;
|
||||||
type?: 'decompressing' | 'flashing' | 'verifying';
|
type?: "decompressing" | "flashing" | "verifying";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fromFlashState({
|
export function fromFlashState({
|
||||||
type,
|
type,
|
||||||
percentage,
|
percentage,
|
||||||
position,
|
position,
|
||||||
}: Pick<FlashState, 'type' | 'percentage' | 'position'>): {
|
}: Pick<FlashState, "type" | "percentage" | "position">): {
|
||||||
status: string;
|
status: string;
|
||||||
position?: string;
|
position?: string;
|
||||||
} {
|
} {
|
||||||
if (type === undefined) {
|
if (type === undefined) {
|
||||||
return { status: i18next.t('progress.starting') };
|
return { status: i18next.t("progress.starting") };
|
||||||
} else if (type === 'decompressing') {
|
} else if (type === "decompressing") {
|
||||||
if (percentage == null) {
|
if (percentage == null) {
|
||||||
return { status: i18next.t('progress.decompressing') };
|
return { status: i18next.t("progress.decompressing") };
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
position: `${percentage}%`,
|
position: `${percentage}%`,
|
||||||
status: i18next.t('progress.decompressing'),
|
status: i18next.t("progress.decompressing"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (type === 'flashing') {
|
} else if (type === "flashing") {
|
||||||
if (percentage != null) {
|
if (percentage != null) {
|
||||||
if (percentage < 100) {
|
if (percentage < 100) {
|
||||||
return {
|
return {
|
||||||
position: `${percentage}%`,
|
position: `${percentage}%`,
|
||||||
status: i18next.t('progress.flashing'),
|
status: i18next.t("progress.flashing"),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return { status: i18next.t('progress.finishing') };
|
return { status: i18next.t("progress.finishing") };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
status: i18next.t('progress.flashing'),
|
status: i18next.t("progress.flashing"),
|
||||||
position: `${position ? prettyBytes(position) : ''}`,
|
position: `${position ? prettyBytes(position) : ""}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (type === 'verifying') {
|
} else if (type === "verifying") {
|
||||||
if (percentage == null) {
|
if (percentage == null) {
|
||||||
return { status: i18next.t('progress.verifying') };
|
return { status: i18next.t("progress.verifying") };
|
||||||
} else if (percentage < 100) {
|
} else if (percentage < 100) {
|
||||||
return {
|
return {
|
||||||
position: `${percentage}%`,
|
position: `${percentage}%`,
|
||||||
status: i18next.t('progress.verifying'),
|
status: i18next.t("progress.verifying"),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return { status: i18next.t('progress.finishing') };
|
return { status: i18next.t("progress.finishing") };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { status: i18next.t('progress.failing') };
|
return { status: i18next.t("progress.failing") };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function titleFromFlashState(
|
export function titleFromFlashState(
|
||||||
state: Pick<FlashState, 'type' | 'percentage' | 'position'>,
|
state: Pick<FlashState, "type" | "percentage" | "position">
|
||||||
): string {
|
): string {
|
||||||
const { status, position } = fromFlashState(state);
|
const { status, position } = fromFlashState(state);
|
||||||
if (position !== undefined) {
|
if (position !== undefined) {
|
||||||
|
@ -18,7 +18,7 @@ import CogSvg from '@fortawesome/fontawesome-free/svgs/solid/cog.svg';
|
|||||||
import QuestionCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/question-circle.svg';
|
import QuestionCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/question-circle.svg';
|
||||||
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as prettyBytes from 'pretty-bytes';
|
import prettyBytes from 'pretty-bytes';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Flex } from 'rendition';
|
import { Flex } from 'rendition';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
@ -14,19 +14,19 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Dictionary } from 'lodash';
|
import { Dictionary } from "lodash";
|
||||||
import { outdent } from 'outdent';
|
import { outdent } from "outdent";
|
||||||
import * as prettyBytes from 'pretty-bytes';
|
import prettyBytes from "pretty-bytes";
|
||||||
import '../gui/app/i18n';
|
import "../gui/app/i18n";
|
||||||
import * as i18next from 'i18next';
|
import * as i18next from "i18next";
|
||||||
|
|
||||||
export const progress: Dictionary<(quantity: number) => string> = {
|
export const progress: Dictionary<(quantity: number) => string> = {
|
||||||
successful: (quantity: number) => {
|
successful: (quantity: number) => {
|
||||||
return i18next.t('message.flashSucceed', { count: quantity });
|
return i18next.t("message.flashSucceed", { count: quantity });
|
||||||
},
|
},
|
||||||
|
|
||||||
failed: (quantity: number) => {
|
failed: (quantity: number) => {
|
||||||
return i18next.t('message.flashFail', { count: quantity });
|
return i18next.t("message.flashFail", { count: quantity });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -34,122 +34,122 @@ export const info = {
|
|||||||
flashComplete: (
|
flashComplete: (
|
||||||
imageBasename: string,
|
imageBasename: string,
|
||||||
[drive]: [{ description: string; displayName: string }],
|
[drive]: [{ description: string; displayName: string }],
|
||||||
{ failed, successful }: { failed: number; successful: number },
|
{ failed, successful }: { failed: number; successful: number }
|
||||||
) => {
|
) => {
|
||||||
const targets = [];
|
const targets = [];
|
||||||
if (failed + successful === 1) {
|
if (failed + successful === 1) {
|
||||||
targets.push(
|
targets.push(
|
||||||
i18next.t('message.toDrive', {
|
i18next.t("message.toDrive", {
|
||||||
description: drive.description,
|
description: drive.description,
|
||||||
name: drive.displayName,
|
name: drive.displayName,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (successful) {
|
if (successful) {
|
||||||
targets.push(
|
targets.push(
|
||||||
i18next.t('message.toTarget', {
|
i18next.t("message.toTarget", {
|
||||||
count: successful,
|
count: successful,
|
||||||
num: successful,
|
num: successful,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (failed) {
|
if (failed) {
|
||||||
targets.push(
|
targets.push(
|
||||||
i18next.t('message.andFailTarget', { count: failed, num: failed }),
|
i18next.t("message.andFailTarget", { count: failed, num: failed })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return i18next.t('message.succeedTo', {
|
return i18next.t("message.succeedTo", {
|
||||||
name: imageBasename,
|
name: imageBasename,
|
||||||
target: targets.join(' '),
|
target: targets.join(" "),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const compatibility = {
|
export const compatibility = {
|
||||||
sizeNotRecommended: () => {
|
sizeNotRecommended: () => {
|
||||||
return i18next.t('message.sizeNotRecommended');
|
return i18next.t("message.sizeNotRecommended");
|
||||||
},
|
},
|
||||||
|
|
||||||
tooSmall: () => {
|
tooSmall: () => {
|
||||||
return i18next.t('message.tooSmall');
|
return i18next.t("message.tooSmall");
|
||||||
},
|
},
|
||||||
|
|
||||||
locked: () => {
|
locked: () => {
|
||||||
return i18next.t('message.locked');
|
return i18next.t("message.locked");
|
||||||
},
|
},
|
||||||
|
|
||||||
system: () => {
|
system: () => {
|
||||||
return i18next.t('message.system');
|
return i18next.t("message.system");
|
||||||
},
|
},
|
||||||
|
|
||||||
containsImage: () => {
|
containsImage: () => {
|
||||||
return i18next.t('message.containsImage');
|
return i18next.t("message.containsImage");
|
||||||
},
|
},
|
||||||
|
|
||||||
// The drive is large and therefore likely not a medium you want to write to.
|
// The drive is large and therefore likely not a medium you want to write to.
|
||||||
largeDrive: () => {
|
largeDrive: () => {
|
||||||
return i18next.t('message.largeDrive');
|
return i18next.t("message.largeDrive");
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const warning = {
|
export const warning = {
|
||||||
tooSmall: (source: { size: number }, target: { size: number }) => {
|
tooSmall: (source: { size: number }, target: { size: number }) => {
|
||||||
return outdent({ newline: ' ' })`
|
return outdent({ newline: " " })`
|
||||||
${i18next.t('message.sourceLarger', {
|
${i18next.t("message.sourceLarger", {
|
||||||
byte: prettyBytes(source.size - target.size),
|
byte: prettyBytes(source.size - target.size),
|
||||||
})}
|
})}
|
||||||
`;
|
`;
|
||||||
},
|
},
|
||||||
|
|
||||||
exitWhileFlashing: () => {
|
exitWhileFlashing: () => {
|
||||||
return i18next.t('message.exitWhileFlashing');
|
return i18next.t("message.exitWhileFlashing");
|
||||||
},
|
},
|
||||||
|
|
||||||
looksLikeWindowsImage: () => {
|
looksLikeWindowsImage: () => {
|
||||||
return i18next.t('message.looksLikeWindowsImage');
|
return i18next.t("message.looksLikeWindowsImage");
|
||||||
},
|
},
|
||||||
|
|
||||||
missingPartitionTable: () => {
|
missingPartitionTable: () => {
|
||||||
return i18next.t('message.missingPartitionTable', {
|
return i18next.t("message.missingPartitionTable", {
|
||||||
type: i18next.t('message.image'),
|
type: i18next.t("message.image"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
driveMissingPartitionTable: () => {
|
driveMissingPartitionTable: () => {
|
||||||
return i18next.t('message.missingPartitionTable', {
|
return i18next.t("message.missingPartitionTable", {
|
||||||
type: i18next.t('message.drive'),
|
type: i18next.t("message.drive"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
largeDriveSize: () => {
|
largeDriveSize: () => {
|
||||||
return i18next.t('message.largeDriveSize');
|
return i18next.t("message.largeDriveSize");
|
||||||
},
|
},
|
||||||
|
|
||||||
systemDrive: () => {
|
systemDrive: () => {
|
||||||
return i18next.t('message.systemDrive');
|
return i18next.t("message.systemDrive");
|
||||||
},
|
},
|
||||||
|
|
||||||
sourceDrive: () => {
|
sourceDrive: () => {
|
||||||
return i18next.t('message.sourceDrive');
|
return i18next.t("message.sourceDrive");
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const error = {
|
export const error = {
|
||||||
notEnoughSpaceInDrive: () => {
|
notEnoughSpaceInDrive: () => {
|
||||||
return i18next.t('message.noSpace');
|
return i18next.t("message.noSpace");
|
||||||
},
|
},
|
||||||
|
|
||||||
genericFlashError: (err: Error) => {
|
genericFlashError: (err: Error) => {
|
||||||
return i18next.t('message.genericFlashError', { error: err.message });
|
return i18next.t("message.genericFlashError", { error: err.message });
|
||||||
},
|
},
|
||||||
|
|
||||||
validation: () => {
|
validation: () => {
|
||||||
return i18next.t('message.validation');
|
return i18next.t("message.validation");
|
||||||
},
|
},
|
||||||
|
|
||||||
openSource: (sourceName: string, errorMessage: string) => {
|
openSource: (sourceName: string, errorMessage: string) => {
|
||||||
return i18next.t('message.openError', {
|
return i18next.t("message.openError", {
|
||||||
source: sourceName,
|
source: sourceName,
|
||||||
error: errorMessage,
|
error: errorMessage,
|
||||||
});
|
});
|
||||||
@ -157,37 +157,37 @@ export const error = {
|
|||||||
|
|
||||||
flashFailure: (
|
flashFailure: (
|
||||||
imageBasename: string,
|
imageBasename: string,
|
||||||
drives: Array<{ description: string; displayName: string }>,
|
drives: Array<{ description: string; displayName: string }>
|
||||||
) => {
|
) => {
|
||||||
const target =
|
const target =
|
||||||
drives.length === 1
|
drives.length === 1
|
||||||
? i18next.t('message.toDrive', {
|
? i18next.t("message.toDrive", {
|
||||||
description: drives[0].description,
|
description: drives[0].description,
|
||||||
name: drives[0].displayName,
|
name: drives[0].displayName,
|
||||||
})
|
})
|
||||||
: i18next.t('message.toTarget', {
|
: i18next.t("message.toTarget", {
|
||||||
count: drives.length,
|
count: drives.length,
|
||||||
num: drives.length,
|
num: drives.length,
|
||||||
});
|
});
|
||||||
return i18next.t('message.flashError', {
|
return i18next.t("message.flashError", {
|
||||||
image: imageBasename,
|
image: imageBasename,
|
||||||
targets: target,
|
targets: target,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
driveUnplugged: () => {
|
driveUnplugged: () => {
|
||||||
return i18next.t('message.unplug');
|
return i18next.t("message.unplug");
|
||||||
},
|
},
|
||||||
|
|
||||||
inputOutput: () => {
|
inputOutput: () => {
|
||||||
return i18next.t('message.cannotWrite');
|
return i18next.t("message.cannotWrite");
|
||||||
},
|
},
|
||||||
|
|
||||||
childWriterDied: () => {
|
childWriterDied: () => {
|
||||||
return i18next.t('message.childWriterDied');
|
return i18next.t("message.childWriterDied");
|
||||||
},
|
},
|
||||||
|
|
||||||
unsupportedProtocol: () => {
|
unsupportedProtocol: () => {
|
||||||
return i18next.t('message.badProtocol');
|
return i18next.t("message.badProtocol");
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
13
npm-shrinkwrap.json
generated
13
npm-shrinkwrap.json
generated
@ -57,7 +57,7 @@
|
|||||||
"path-is-inside": "1.0.2",
|
"path-is-inside": "1.0.2",
|
||||||
"pkg": "^5.8.1",
|
"pkg": "^5.8.1",
|
||||||
"pnp-webpack-plugin": "1.7.0",
|
"pnp-webpack-plugin": "1.7.0",
|
||||||
"pretty-bytes": "5.6.0",
|
"pretty-bytes": "6.1.1",
|
||||||
"react": "16.8.5",
|
"react": "16.8.5",
|
||||||
"react-dom": "16.8.5",
|
"react-dom": "16.8.5",
|
||||||
"react-i18next": "11.18.6",
|
"react-i18next": "11.18.6",
|
||||||
@ -14733,11 +14733,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pretty-bytes": {
|
"node_modules/pretty-bytes": {
|
||||||
"version": "5.6.0",
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": "^14.13.1 || >=16.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
@ -28543,7 +28544,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"pretty-bytes": {
|
"pretty-bytes": {
|
||||||
"version": "5.6.0",
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"prismjs": {
|
"prismjs": {
|
||||||
|
@ -98,7 +98,7 @@
|
|||||||
"path-is-inside": "1.0.2",
|
"path-is-inside": "1.0.2",
|
||||||
"pkg": "^5.8.1",
|
"pkg": "^5.8.1",
|
||||||
"pnp-webpack-plugin": "1.7.0",
|
"pnp-webpack-plugin": "1.7.0",
|
||||||
"pretty-bytes": "5.6.0",
|
"pretty-bytes": "6.1.1",
|
||||||
"react": "16.8.5",
|
"react": "16.8.5",
|
||||||
"react-dom": "16.8.5",
|
"react-dom": "16.8.5",
|
||||||
"react-i18next": "11.18.6",
|
"react-i18next": "11.18.6",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user