mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-14 14:56:32 +00:00
Merge pull request #3325 from balena-io/new-success-screen-2
New success screen 2
This commit is contained in:
commit
a2a0f2ef41
@ -356,6 +356,16 @@ async function main() {
|
|||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
React.createElement(MainPage),
|
React.createElement(MainPage),
|
||||||
document.getElementById('main'),
|
document.getElementById('main'),
|
||||||
|
// callback to set the correct zoomFactor for webviews as well
|
||||||
|
async () => {
|
||||||
|
const fullscreen = await settings.get('fullscreen');
|
||||||
|
const width = fullscreen ? window.screen.width : window.outerWidth;
|
||||||
|
try {
|
||||||
|
electron.webFrame.setZoomFactor(width / settings.DEFAULT_WIDTH);
|
||||||
|
} catch (err) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,15 +18,7 @@ import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exc
|
|||||||
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 {
|
import { Flex, ModalProps, Txt, Badge, Link, TableColumn } from 'rendition';
|
||||||
Flex,
|
|
||||||
ModalProps,
|
|
||||||
Txt,
|
|
||||||
Badge,
|
|
||||||
Link,
|
|
||||||
Table,
|
|
||||||
TableColumn,
|
|
||||||
} from 'rendition';
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -43,7 +35,12 @@ 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 { Alert, Modal, ScrollableFlex } from '../../styled-components';
|
import {
|
||||||
|
Alert,
|
||||||
|
GenericTableProps,
|
||||||
|
Modal,
|
||||||
|
Table,
|
||||||
|
} from '../../styled-components';
|
||||||
|
|
||||||
import DriveSVGIcon from '../../../assets/tgt.svg';
|
import DriveSVGIcon from '../../../assets/tgt.svg';
|
||||||
import { SourceMetadata } from '../source-selector/source-selector';
|
import { SourceMetadata } from '../source-selector/source-selector';
|
||||||
@ -75,29 +72,14 @@ function isDrivelistDrive(drive: Drive): drive is DrivelistDrive {
|
|||||||
return typeof (drive as DrivelistDrive).size === 'number';
|
return typeof (drive as DrivelistDrive).size === 'number';
|
||||||
}
|
}
|
||||||
|
|
||||||
const DrivesTable = styled(({ refFn, ...props }) => (
|
const DrivesTable = styled((props: GenericTableProps<Drive>) => (
|
||||||
<div>
|
<Table<Drive> {...props} />
|
||||||
<Table<Drive> ref={refFn} {...props} />
|
|
||||||
</div>
|
|
||||||
))`
|
))`
|
||||||
[data-display='table-head']
|
[data-display='table-head'],
|
||||||
> [data-display='table-row']
|
[data-display='table-body'] {
|
||||||
> [data-display='table-cell'] {
|
> [data-display='table-row'] > [data-display='table-cell'] {
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
background-color: ${(props) => props.theme.colors.quartenary.light};
|
|
||||||
|
|
||||||
input[type='checkbox'] + div {
|
|
||||||
display: ${({ multipleSelection }) =>
|
|
||||||
multipleSelection ? 'flex' : 'none'};
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
padding-left: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(2) {
|
&:nth-child(2) {
|
||||||
width: 38%;
|
width: 32%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:nth-child(3) {
|
&:nth-child(3) {
|
||||||
@ -112,36 +94,6 @@ const DrivesTable = styled(({ refFn, ...props }) => (
|
|||||||
width: 32%;
|
width: 32%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-display='table-body'] > [data-display='table-row'] {
|
|
||||||
> [data-display='table-cell']:first-child {
|
|
||||||
padding-left: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> [data-display='table-cell']:last-child {
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-highlight='true'] {
|
|
||||||
&.system {
|
|
||||||
background-color: ${(props) =>
|
|
||||||
props.showWarnings ? '#fff5e6' : '#e8f5fc'};
|
|
||||||
}
|
|
||||||
|
|
||||||
> [data-display='table-cell']:first-child {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&& [data-display='table-row'] > [data-display='table-cell'] {
|
|
||||||
padding: 6px 8px;
|
|
||||||
color: #2a506f;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type='checkbox'] + div {
|
|
||||||
border-radius: ${({ multipleSelection }) =>
|
|
||||||
multipleSelection ? '4px' : '50%'};
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -393,6 +345,16 @@ export class DriveSelector extends React.Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private deselectingAll(rows: DrivelistDrive[]) {
|
||||||
|
return (
|
||||||
|
rows.length > 0 &&
|
||||||
|
rows.length === this.state.selectedList.length &&
|
||||||
|
this.state.selectedList.every(
|
||||||
|
(d) => rows.findIndex((r) => d.device === r.device) > -1,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.unsubscribe = store.subscribe(() => {
|
this.unsubscribe = store.subscribe(() => {
|
||||||
const drives = getDrives();
|
const drives = getDrives();
|
||||||
@ -453,7 +415,6 @@ export class DriveSelector extends React.Component<
|
|||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<Flex width="100%" height="90%">
|
|
||||||
{!hasAvailableDrives() ? (
|
{!hasAvailableDrives() ? (
|
||||||
<Flex
|
<Flex
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
@ -465,13 +426,14 @@ export class DriveSelector extends React.Component<
|
|||||||
<b>{this.props.emptyListLabel}</b>
|
<b>{this.props.emptyListLabel}</b>
|
||||||
</Flex>
|
</Flex>
|
||||||
) : (
|
) : (
|
||||||
<ScrollableFlex flexDirection="column" width="100%">
|
<>
|
||||||
<DrivesTable
|
<DrivesTable
|
||||||
refFn={(t: Table<Drive>) => {
|
refFn={(t) => {
|
||||||
if (t !== null) {
|
if (t !== null) {
|
||||||
t.setRowSelection(selectedList);
|
t.setRowSelection(selectedList);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
checkedRowsNumber={selectedList.length}
|
||||||
multipleSelection={this.props.multipleSelection}
|
multipleSelection={this.props.multipleSelection}
|
||||||
columns={this.tableColumns}
|
columns={this.tableColumns}
|
||||||
data={displayedDrives}
|
data={displayedDrives}
|
||||||
@ -481,8 +443,11 @@ export class DriveSelector extends React.Component<
|
|||||||
}
|
}
|
||||||
rowKey="displayName"
|
rowKey="displayName"
|
||||||
onCheck={(rows: Drive[]) => {
|
onCheck={(rows: Drive[]) => {
|
||||||
const newSelection = rows.filter(isDrivelistDrive);
|
let newSelection = rows.filter(isDrivelistDrive);
|
||||||
if (this.props.multipleSelection) {
|
if (this.props.multipleSelection) {
|
||||||
|
if (this.deselectingAll(newSelection)) {
|
||||||
|
newSelection = [];
|
||||||
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedList: newSelection,
|
selectedList: newSelection,
|
||||||
});
|
});
|
||||||
@ -499,25 +464,21 @@ export class DriveSelector extends React.Component<
|
|||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.props.multipleSelection) {
|
const index = selectedList.findIndex(
|
||||||
const newList = [...selectedList];
|
(d) => d.device === row.device,
|
||||||
const selectedIndex = selectedList.findIndex(
|
|
||||||
(drive) => drive.device === row.device,
|
|
||||||
);
|
);
|
||||||
if (selectedIndex === -1) {
|
const newList = this.props.multipleSelection
|
||||||
|
? [...selectedList]
|
||||||
|
: [];
|
||||||
|
if (index === -1) {
|
||||||
newList.push(row);
|
newList.push(row);
|
||||||
} else {
|
} else {
|
||||||
// Deselect if selected
|
// Deselect if selected
|
||||||
newList.splice(selectedIndex, 1);
|
newList.splice(index, 1);
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedList: newList,
|
selectedList: newList,
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
selectedList: [row],
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{numberOfHiddenSystemDrives > 0 && (
|
{numberOfHiddenSystemDrives > 0 && (
|
||||||
@ -533,15 +494,13 @@ export class DriveSelector extends React.Component<
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</ScrollableFlex>
|
</>
|
||||||
)}
|
)}
|
||||||
{this.props.showWarnings && hasSystemDrives ? (
|
{this.props.showWarnings && hasSystemDrives ? (
|
||||||
<Alert className="system-drive-alert" style={{ width: '67%' }}>
|
<Alert className="system-drive-alert" style={{ width: '67%' }}>
|
||||||
Selecting your system drive is dangerous and will erase your
|
Selecting your system drive is dangerous and will erase your drive!
|
||||||
drive!
|
|
||||||
</Alert>
|
</Alert>
|
||||||
) : null}
|
) : null}
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{missingDriversModal.drive !== undefined && (
|
{missingDriversModal.drive !== undefined && (
|
||||||
<Modal
|
<Modal
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Flex } from 'rendition';
|
import { Flex } from 'rendition';
|
||||||
import { v4 as uuidV4 } from 'uuid';
|
import { v4 as uuidV4 } from 'uuid';
|
||||||
@ -23,13 +22,9 @@ import * as flashState from '../../models/flash-state';
|
|||||||
import * as selectionState from '../../models/selection-state';
|
import * as selectionState from '../../models/selection-state';
|
||||||
import { Actions, store } from '../../models/store';
|
import { Actions, store } from '../../models/store';
|
||||||
import * as analytics from '../../modules/analytics';
|
import * as analytics from '../../modules/analytics';
|
||||||
import { open as openExternal } from '../../os/open-external/services/open-external';
|
|
||||||
import { FlashAnother } from '../flash-another/flash-another';
|
import { FlashAnother } from '../flash-another/flash-another';
|
||||||
import { FlashResults } from '../flash-results/flash-results';
|
import { FlashResults, FlashError } from '../flash-results/flash-results';
|
||||||
|
import { SafeWebview } from '../safe-webview/safe-webview';
|
||||||
import EtcherSvg from '../../../assets/etcher.svg';
|
|
||||||
import LoveSvg from '../../../assets/love.svg';
|
|
||||||
import BalenaSvg from '../../../assets/balena.svg';
|
|
||||||
|
|
||||||
function restart(goToMain: () => void) {
|
function restart(goToMain: () => void) {
|
||||||
selectionState.deselectAllDrives();
|
selectionState.deselectAllDrives();
|
||||||
@ -44,22 +39,56 @@ function restart(goToMain: () => void) {
|
|||||||
goToMain();
|
goToMain();
|
||||||
}
|
}
|
||||||
|
|
||||||
function formattedErrors() {
|
|
||||||
const errors = _.map(
|
|
||||||
_.get(flashState.getFlashResults(), ['results', 'errors']),
|
|
||||||
(error) => {
|
|
||||||
return `${error.device}: ${error.message || error.code}`;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return errors.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
function FinishPage({ goToMain }: { goToMain: () => void }) {
|
function FinishPage({ goToMain }: { goToMain: () => void }) {
|
||||||
const results = flashState.getFlashResults().results || {};
|
const [webviewShowing, setWebviewShowing] = React.useState(false);
|
||||||
|
const flashResults = flashState.getFlashResults();
|
||||||
|
const errors: FlashError[] = (
|
||||||
|
store.getState().toJS().failedDeviceErrors || []
|
||||||
|
).map(([, error]: [string, FlashError]) => ({
|
||||||
|
...error,
|
||||||
|
}));
|
||||||
|
const {
|
||||||
|
averageSpeed,
|
||||||
|
blockmappedSize,
|
||||||
|
bytesWritten,
|
||||||
|
failed,
|
||||||
|
size,
|
||||||
|
} = flashState.getFlashState();
|
||||||
|
const {
|
||||||
|
skip,
|
||||||
|
results = {
|
||||||
|
bytesWritten,
|
||||||
|
sourceMetadata: {
|
||||||
|
size,
|
||||||
|
blockmappedSize,
|
||||||
|
},
|
||||||
|
averageFlashingSpeed: averageSpeed,
|
||||||
|
devices: { failed, successful: 0 },
|
||||||
|
},
|
||||||
|
} = flashResults;
|
||||||
return (
|
return (
|
||||||
<Flex flexDirection="column" width="100%" color="#fff">
|
<Flex height="100%" justifyContent="space-between">
|
||||||
<Flex height="160px" alignItems="center" justifyContent="center">
|
<Flex
|
||||||
<FlashResults results={results} errors={formattedErrors()} />
|
width={webviewShowing ? '36.2vw' : '100vw'}
|
||||||
|
height="100vh"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
flexDirection="column"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
zIndex: 1,
|
||||||
|
boxShadow: '0 2px 15px 0 rgba(0, 0, 0, 0.2)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FlashResults
|
||||||
|
image={selectionState.getImage()?.name}
|
||||||
|
results={results}
|
||||||
|
skip={skip}
|
||||||
|
errors={errors}
|
||||||
|
mb="32px"
|
||||||
|
goToMain={goToMain}
|
||||||
|
/>
|
||||||
|
|
||||||
<FlashAnother
|
<FlashAnother
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -67,35 +96,19 @@ function FinishPage({ goToMain }: { goToMain: () => void }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<SafeWebview
|
||||||
<Flex
|
src="https://www.balena.io/etcher/success-banner?borderTop=false&darkBackground=true"
|
||||||
flexDirection="column"
|
onWebviewShow={setWebviewShowing}
|
||||||
height="320px"
|
style={{
|
||||||
justifyContent="space-between"
|
display: webviewShowing ? 'flex' : 'none',
|
||||||
alignItems="center"
|
position: 'absolute',
|
||||||
>
|
right: 0,
|
||||||
<Flex fontSize="28px" mt="40px">
|
bottom: 0,
|
||||||
Thanks for using
|
width: '63.8vw',
|
||||||
<EtcherSvg
|
height: '100vh',
|
||||||
width="165px"
|
}}
|
||||||
style={{ margin: '0 10px', cursor: 'pointer' }}
|
|
||||||
onClick={() =>
|
|
||||||
openExternal('https://balena.io/etcher?ref=etcher_offline_banner')
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex mb="10px">
|
|
||||||
made with
|
|
||||||
<LoveSvg height="20px" style={{ margin: '0 10px' }} />
|
|
||||||
by
|
|
||||||
<BalenaSvg
|
|
||||||
height="20px"
|
|
||||||
style={{ margin: '0 10px', cursor: 'pointer' }}
|
|
||||||
onClick={() => openExternal('https://balena.io?ref=etcher_success')}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ export interface FlashAnotherProps {
|
|||||||
export const FlashAnother = (props: FlashAnotherProps) => {
|
export const FlashAnother = (props: FlashAnotherProps) => {
|
||||||
return (
|
return (
|
||||||
<BaseButton primary onClick={props.onClick}>
|
<BaseButton primary onClick={props.onClick}>
|
||||||
Flash Another
|
Flash another
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -16,19 +16,106 @@
|
|||||||
|
|
||||||
import CircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle.svg';
|
import CircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle.svg';
|
||||||
import CheckCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/check-circle.svg';
|
import CheckCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/check-circle.svg';
|
||||||
|
import TimesCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/times-circle.svg';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import outdent from 'outdent';
|
import outdent from 'outdent';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Flex, Txt } from 'rendition';
|
import { Flex, FlexProps, Link, TableColumn, Txt } from 'rendition';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { progress } from '../../../../shared/messages';
|
import { progress } from '../../../../shared/messages';
|
||||||
import { bytesToMegabytes } from '../../../../shared/units';
|
import { bytesToMegabytes } from '../../../../shared/units';
|
||||||
|
|
||||||
|
import FlashSvg from '../../../assets/flash.svg';
|
||||||
|
import { getDrives } from '../../models/available-drives';
|
||||||
|
import { resetState } from '../../models/flash-state';
|
||||||
|
import * as selection from '../../models/selection-state';
|
||||||
|
import { middleEllipsis } from '../../utils/middle-ellipsis';
|
||||||
|
import { Modal, Table } from '../../styled-components';
|
||||||
|
|
||||||
|
const ErrorsTable = styled((props) => <Table<FlashError> {...props} />)`
|
||||||
|
&&& [data-display='table-head'],
|
||||||
|
&&& [data-display='table-body'] {
|
||||||
|
> [data-display='table-row'] {
|
||||||
|
> [data-display='table-cell'] {
|
||||||
|
&:first-child {
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const DoneIcon = (props: {
|
||||||
|
skipped: boolean;
|
||||||
|
color: string;
|
||||||
|
allFailed: boolean;
|
||||||
|
}) => {
|
||||||
|
const svgProps = {
|
||||||
|
width: '28px',
|
||||||
|
fill: props.color,
|
||||||
|
style: {
|
||||||
|
marginTop: '-25px',
|
||||||
|
marginLeft: '13px',
|
||||||
|
zIndex: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return props.allFailed && !props.skipped ? (
|
||||||
|
<TimesCircleSvg {...svgProps} />
|
||||||
|
) : (
|
||||||
|
<CheckCircleSvg {...svgProps} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface FlashError extends Error {
|
||||||
|
description: string;
|
||||||
|
device: string;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formattedErrors(errors: FlashError[]) {
|
||||||
|
return errors
|
||||||
|
.map((error) => `${error.device}: ${error.message || error.code}`)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns: Array<TableColumn<FlashError>> = [
|
||||||
|
{
|
||||||
|
field: 'description',
|
||||||
|
label: 'Target',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'device',
|
||||||
|
label: 'Location',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'message',
|
||||||
|
label: 'Error',
|
||||||
|
render: (message: string, { code }: FlashError) => {
|
||||||
|
return message ?? code;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export function FlashResults({
|
export function FlashResults({
|
||||||
|
goToMain,
|
||||||
|
image = '',
|
||||||
errors,
|
errors,
|
||||||
results,
|
results,
|
||||||
|
skip,
|
||||||
|
...props
|
||||||
}: {
|
}: {
|
||||||
errors: string;
|
goToMain: () => void;
|
||||||
|
image?: string;
|
||||||
|
errors: FlashError[];
|
||||||
|
skip: boolean;
|
||||||
results: {
|
results: {
|
||||||
bytesWritten: number;
|
bytesWritten: number;
|
||||||
sourceMetadata: {
|
sourceMetadata: {
|
||||||
@ -38,8 +125,10 @@ export function FlashResults({
|
|||||||
averageFlashingSpeed: number;
|
averageFlashingSpeed: number;
|
||||||
devices: { failed: number; successful: number };
|
devices: { failed: number; successful: number };
|
||||||
};
|
};
|
||||||
}) {
|
} & FlexProps) {
|
||||||
const allDevicesFailed = results.devices.successful === 0;
|
const [showErrorsInfo, setShowErrorsInfo] = React.useState(false);
|
||||||
|
const allFailed = results.devices.successful === 0;
|
||||||
|
const someFailed = results.devices.failed !== 0 || errors.length !== 0;
|
||||||
const effectiveSpeed = _.round(
|
const effectiveSpeed = _.round(
|
||||||
bytesToMegabytes(
|
bytesToMegabytes(
|
||||||
results.sourceMetadata.size /
|
results.sourceMetadata.size /
|
||||||
@ -48,44 +137,55 @@ export function FlashResults({
|
|||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex flexDirection="column" {...props}>
|
||||||
flexDirection="column"
|
<Flex alignItems="center" flexDirection="column">
|
||||||
mr="80px"
|
|
||||||
height="90px"
|
|
||||||
style={{
|
|
||||||
position: 'relative',
|
|
||||||
top: '25px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex alignItems="center">
|
|
||||||
<CheckCircleSvg
|
|
||||||
width="24px"
|
|
||||||
fill={allDevicesFailed ? '#c6c8c9' : '#1ac135'}
|
|
||||||
style={{
|
|
||||||
margin: '0 15px 0 0',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Txt fontSize={24} color="#fff">
|
|
||||||
Flash Complete!
|
|
||||||
</Txt>
|
|
||||||
</Flex>
|
|
||||||
<Flex flexDirection="column" mr="0" mb="0" ml="40px" color="#7e8085">
|
|
||||||
{Object.entries(results.devices).map(([type, quantity]) => {
|
|
||||||
return quantity ? (
|
|
||||||
<Flex
|
<Flex
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
tooltip={type === 'failed' ? errors : undefined}
|
mt="50px"
|
||||||
|
mb="32px"
|
||||||
|
color="#7e8085"
|
||||||
|
flexDirection="column"
|
||||||
>
|
>
|
||||||
<CircleSvg
|
<FlashSvg width="40px" height="40px" className="disabled" />
|
||||||
width="14px"
|
<DoneIcon
|
||||||
fill={type === 'failed' ? '#ff4444' : '#1ac135'}
|
skipped={skip}
|
||||||
|
allFailed={allFailed}
|
||||||
|
color={allFailed || someFailed ? '#c6c8c9' : '#1ac135'}
|
||||||
/>
|
/>
|
||||||
<Txt ml={10}>{quantity}</Txt>
|
<Txt>{middleEllipsis(image, 24)}</Txt>
|
||||||
<Txt ml={10}>{progress[type](quantity)}</Txt>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
) : null;
|
<Txt fontSize={24} color="#fff" mb="17px">
|
||||||
})}
|
Flash Complete!
|
||||||
{!allDevicesFailed && (
|
</Txt>
|
||||||
|
{skip ? <Txt color="#7e8085">Validation has been skipped</Txt> : null}
|
||||||
|
</Flex>
|
||||||
|
<Flex flexDirection="column" color="#7e8085">
|
||||||
|
{results.devices.successful !== 0 ? (
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<CircleSvg width="14px" fill="#1ac135" />
|
||||||
|
<Txt ml="10px" color="#fff">
|
||||||
|
{results.devices.successful}
|
||||||
|
</Txt>
|
||||||
|
<Txt ml="10px">
|
||||||
|
{progress.successful(results.devices.successful)}
|
||||||
|
</Txt>
|
||||||
|
</Flex>
|
||||||
|
) : null}
|
||||||
|
{errors.length !== 0 ? (
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<CircleSvg width="14px" fill="#ff4444" />
|
||||||
|
<Txt ml="10px" color="#fff">
|
||||||
|
{errors.length}
|
||||||
|
</Txt>
|
||||||
|
<Txt ml="10px" tooltip={formattedErrors(errors)}>
|
||||||
|
{progress.failed(errors.length)}
|
||||||
|
</Txt>
|
||||||
|
<Link ml="10px" onClick={() => setShowErrorsInfo(true)}>
|
||||||
|
more info
|
||||||
|
</Link>
|
||||||
|
</Flex>
|
||||||
|
) : null}
|
||||||
|
{!allFailed && (
|
||||||
<Txt
|
<Txt
|
||||||
fontSize="10px"
|
fontSize="10px"
|
||||||
style={{
|
style={{
|
||||||
@ -101,6 +201,36 @@ export function FlashResults({
|
|||||||
</Txt>
|
</Txt>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
{showErrorsInfo && (
|
||||||
|
<Modal
|
||||||
|
titleElement={
|
||||||
|
<Flex alignItems="baseline" mb={18}>
|
||||||
|
<Txt fontSize={24} align="left">
|
||||||
|
Failed targets
|
||||||
|
</Txt>
|
||||||
|
</Flex>
|
||||||
|
}
|
||||||
|
action="Retry failed targets"
|
||||||
|
cancel={() => setShowErrorsInfo(false)}
|
||||||
|
done={() => {
|
||||||
|
setShowErrorsInfo(false);
|
||||||
|
resetState();
|
||||||
|
getDrives()
|
||||||
|
.map((drive) => {
|
||||||
|
selection.deselectDrive(drive.device);
|
||||||
|
return drive.device;
|
||||||
|
})
|
||||||
|
.filter((driveDevice) =>
|
||||||
|
errors.some((error) => error.device === driveDevice),
|
||||||
|
)
|
||||||
|
.forEach((driveDevice) => selection.selectDrive(driveDevice));
|
||||||
|
goToMain();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ErrorsTable columns={columns} data={errors} />
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ interface ProgressButtonProps {
|
|||||||
percentage: number;
|
percentage: number;
|
||||||
position: number;
|
position: number;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
cancel: () => void;
|
cancel: (type: string) => void;
|
||||||
callback: () => void;
|
callback: () => void;
|
||||||
warning?: boolean;
|
warning?: boolean;
|
||||||
}
|
}
|
||||||
@ -60,11 +60,14 @@ const colors = {
|
|||||||
verifying: '#1ac135',
|
verifying: '#1ac135',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const CancelButton = styled((props) => (
|
const CancelButton = styled(({ type, onClick, ...props }) => {
|
||||||
<Button plain {...props}>
|
const status = type === 'verifying' ? 'Skip' : 'Cancel';
|
||||||
Cancel
|
return (
|
||||||
|
<Button plain onClick={() => onClick(status)} {...props}>
|
||||||
|
{status}
|
||||||
</Button>
|
</Button>
|
||||||
))`
|
);
|
||||||
|
})`
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
&&& {
|
&&& {
|
||||||
width: auto;
|
width: auto;
|
||||||
@ -75,11 +78,14 @@ const CancelButton = styled((props) => (
|
|||||||
|
|
||||||
export class ProgressButton extends React.PureComponent<ProgressButtonProps> {
|
export class ProgressButton extends React.PureComponent<ProgressButtonProps> {
|
||||||
public render() {
|
public render() {
|
||||||
|
const percentage = this.props.percentage;
|
||||||
|
const warning = this.props.warning;
|
||||||
const { status, position } = fromFlashState({
|
const { status, position } = fromFlashState({
|
||||||
type: this.props.type,
|
type: this.props.type,
|
||||||
|
percentage,
|
||||||
position: this.props.position,
|
position: this.props.position,
|
||||||
percentage: this.props.percentage,
|
|
||||||
});
|
});
|
||||||
|
const type = this.props.type || 'default';
|
||||||
if (this.props.active) {
|
if (this.props.active) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -96,21 +102,24 @@ export class ProgressButton extends React.PureComponent<ProgressButtonProps> {
|
|||||||
>
|
>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Txt color="#fff">{status} </Txt>
|
<Txt color="#fff">{status} </Txt>
|
||||||
<Txt color={colors[this.props.type]}>{position}</Txt>
|
<Txt color={colors[type]}>{position}</Txt>
|
||||||
</Flex>
|
</Flex>
|
||||||
<CancelButton onClick={this.props.cancel} color="#00aeef" />
|
{type && (
|
||||||
</Flex>
|
<CancelButton
|
||||||
<FlashProgressBar
|
type={type}
|
||||||
background={colors[this.props.type]}
|
onClick={this.props.cancel}
|
||||||
value={this.props.percentage}
|
color="#00aeef"
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
<FlashProgressBar background={colors[type]} value={percentage} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<StepButton
|
<StepButton
|
||||||
primary={!this.props.warning}
|
primary={!warning}
|
||||||
warning={this.props.warning}
|
warning={warning}
|
||||||
onClick={this.props.callback}
|
onClick={this.props.callback}
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
style={{
|
style={{
|
||||||
|
@ -61,7 +61,7 @@ async function getSettingsList(): Promise<Setting[]> {
|
|||||||
{
|
{
|
||||||
name: 'updatesEnabled',
|
name: 'updatesEnabled',
|
||||||
label: 'Auto-updates enabled',
|
label: 'Auto-updates enabled',
|
||||||
hide: _.includes(['rpm', 'deb'], packageType),
|
hide: ['rpm', 'deb'].includes(packageType),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -121,9 +121,9 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) {
|
|||||||
done={() => toggleModal(false)}
|
done={() => toggleModal(false)}
|
||||||
>
|
>
|
||||||
<Flex flexDirection="column">
|
<Flex flexDirection="column">
|
||||||
{_.map(settingsList, (setting: Setting, i: number) => {
|
{settingsList.map((setting: Setting, i: number) => {
|
||||||
return setting.hide ? null : (
|
return setting.hide ? null : (
|
||||||
<Flex key={setting.name}>
|
<Flex key={setting.name} mb={14}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
toggle
|
toggle
|
||||||
tabIndex={6 + i}
|
tabIndex={6 + i}
|
||||||
@ -135,12 +135,13 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<Flex
|
<Flex
|
||||||
mt={28}
|
mt={18}
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
color="#00aeef"
|
color="#00aeef"
|
||||||
style={{
|
style={{
|
||||||
width: 'fit-content',
|
width: 'fit-content',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
fontSize: 14,
|
||||||
}}
|
}}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
openExternal(
|
openExternal(
|
||||||
|
@ -116,10 +116,11 @@ const ModalText = styled.p`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
function getState() {
|
function getState() {
|
||||||
|
const image = selectionState.getImage();
|
||||||
return {
|
return {
|
||||||
hasImage: selectionState.hasImage(),
|
hasImage: selectionState.hasImage(),
|
||||||
imageName: selectionState.getImageName(),
|
imageName: image?.name,
|
||||||
imageSize: selectionState.getImageSize(),
|
imageSize: image?.size,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,22 +214,28 @@ interface Flow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const FlowSelector = styled(
|
const FlowSelector = styled(
|
||||||
({ flow, ...props }: { flow: Flow; props?: ButtonProps }) => {
|
({ flow, ...props }: { flow: Flow } & ButtonProps) => (
|
||||||
return (
|
|
||||||
<StepButton
|
<StepButton
|
||||||
plain
|
plain={!props.primary}
|
||||||
onClick={(evt) => flow.onClick(evt)}
|
primary={props.primary}
|
||||||
|
onClick={(evt: React.MouseEvent<Element, MouseEvent>) =>
|
||||||
|
flow.onClick(evt)
|
||||||
|
}
|
||||||
icon={flow.icon}
|
icon={flow.icon}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{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);
|
||||||
|
|
||||||
|
:enabled:focus,
|
||||||
|
:enabled:focus svg {
|
||||||
|
color: ${colors.primary.foreground} !important;
|
||||||
|
}
|
||||||
|
|
||||||
:enabled:hover {
|
:enabled:hover {
|
||||||
background-color: ${colors.primary.background};
|
background-color: ${colors.primary.background};
|
||||||
color: ${colors.primary.foreground};
|
color: ${colors.primary.foreground};
|
||||||
@ -269,6 +276,7 @@ interface SourceSelectorState {
|
|||||||
showImageDetails: boolean;
|
showImageDetails: boolean;
|
||||||
showURLSelector: boolean;
|
showURLSelector: boolean;
|
||||||
showDriveSelector: boolean;
|
showDriveSelector: boolean;
|
||||||
|
defaultFlowActive: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SourceSelector extends React.Component<
|
export class SourceSelector extends React.Component<
|
||||||
@ -285,7 +293,11 @@ export class SourceSelector extends React.Component<
|
|||||||
showImageDetails: false,
|
showImageDetails: false,
|
||||||
showURLSelector: false,
|
showURLSelector: false,
|
||||||
showDriveSelector: false,
|
showDriveSelector: false,
|
||||||
|
defaultFlowActive: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Bind `this` since it's used in an event's callback
|
||||||
|
this.onSelectImage = this.onSelectImage.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
@ -519,7 +531,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.getImagePath(),
|
imagePath: selectionState.getImage()?.path,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -527,6 +539,10 @@ export class SourceSelector extends React.Component<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setDefaultFlowActive(defaultFlowActive: boolean) {
|
||||||
|
this.setState({ defaultFlowActive });
|
||||||
|
}
|
||||||
|
|
||||||
// TODO add a visual change when dragging a file over the selector
|
// TODO add a visual change when dragging a file over the selector
|
||||||
public render() {
|
public render() {
|
||||||
const { flashing } = this.props;
|
const { flashing } = this.props;
|
||||||
@ -593,12 +609,15 @@ export class SourceSelector extends React.Component<
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<FlowSelector
|
<FlowSelector
|
||||||
|
primary={this.state.defaultFlowActive}
|
||||||
key="Flash from file"
|
key="Flash from file"
|
||||||
flow={{
|
flow={{
|
||||||
onClick: () => this.openImageSelector(),
|
onClick: () => this.openImageSelector(),
|
||||||
label: 'Flash from file',
|
label: 'Flash from file',
|
||||||
icon: <FileSvg height="1em" fill="currentColor" />,
|
icon: <FileSvg height="1em" fill="currentColor" />,
|
||||||
}}
|
}}
|
||||||
|
onMouseEnter={() => this.setDefaultFlowActive(false)}
|
||||||
|
onMouseLeave={() => this.setDefaultFlowActive(true)}
|
||||||
/>
|
/>
|
||||||
<FlowSelector
|
<FlowSelector
|
||||||
key="Flash from URL"
|
key="Flash from URL"
|
||||||
@ -607,6 +626,8 @@ export class SourceSelector extends React.Component<
|
|||||||
label: 'Flash from URL',
|
label: 'Flash from URL',
|
||||||
icon: <LinkSvg height="1em" fill="currentColor" />,
|
icon: <LinkSvg height="1em" fill="currentColor" />,
|
||||||
}}
|
}}
|
||||||
|
onMouseEnter={() => this.setDefaultFlowActive(false)}
|
||||||
|
onMouseLeave={() => this.setDefaultFlowActive(true)}
|
||||||
/>
|
/>
|
||||||
<FlowSelector
|
<FlowSelector
|
||||||
key="Clone drive"
|
key="Clone drive"
|
||||||
@ -615,6 +636,8 @@ export class SourceSelector extends React.Component<
|
|||||||
label: 'Clone drive',
|
label: 'Clone drive',
|
||||||
icon: <CopySvg height="1em" fill="currentColor" />,
|
icon: <CopySvg height="1em" fill="currentColor" />,
|
||||||
}}
|
}}
|
||||||
|
onMouseEnter={() => this.setDefaultFlowActive(false)}
|
||||||
|
onMouseLeave={() => this.setDefaultFlowActive(true)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -75,14 +75,25 @@ export function setDevicePaths(devicePaths: string[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addFailedDevicePath(devicePath: string) {
|
export function addFailedDeviceError({
|
||||||
const failedDevicePathsSet = new Set(
|
device,
|
||||||
store.getState().toJS().failedDevicePaths,
|
error,
|
||||||
|
}: {
|
||||||
|
device: sdk.scanner.adapters.DrivelistDrive;
|
||||||
|
error: Error;
|
||||||
|
}) {
|
||||||
|
const failedDeviceErrorsMap = new Map(
|
||||||
|
store.getState().toJS().failedDeviceErrors,
|
||||||
);
|
);
|
||||||
failedDevicePathsSet.add(devicePath);
|
failedDeviceErrorsMap.set(device.device, {
|
||||||
|
description: device.description,
|
||||||
|
device: device.device,
|
||||||
|
devicePath: device.devicePath,
|
||||||
|
...error,
|
||||||
|
});
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
type: Actions.SET_FAILED_DEVICE_PATHS,
|
type: Actions.SET_FAILED_DEVICE_ERRORS,
|
||||||
data: Array.from(failedDevicePathsSet),
|
data: Array.from(failedDeviceErrorsMap),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,12 +188,15 @@ function stateObserver(state: typeof DEFAULT_STATE) {
|
|||||||
} else {
|
} else {
|
||||||
selectedDrivesPaths = s.devicePaths;
|
selectedDrivesPaths = s.devicePaths;
|
||||||
}
|
}
|
||||||
|
const failedDevicePaths = s.failedDeviceErrors.map(
|
||||||
|
([devicePath]: [string]) => devicePath,
|
||||||
|
);
|
||||||
const newLedsState = {
|
const newLedsState = {
|
||||||
step,
|
step,
|
||||||
sourceDrive: sourceDrivePath,
|
sourceDrive: sourceDrivePath,
|
||||||
availableDrives: availableDrivesPaths,
|
availableDrives: availableDrivesPaths,
|
||||||
selectedDrives: selectedDrivesPaths,
|
selectedDrives: selectedDrivesPaths,
|
||||||
failedDrives: s.failedDevicePaths,
|
failedDrives: failedDevicePaths,
|
||||||
};
|
};
|
||||||
if (!_.isEqual(newLedsState, ledsState)) {
|
if (!_.isEqual(newLedsState, ledsState)) {
|
||||||
updateLeds(newLedsState);
|
updateLeds(newLedsState);
|
||||||
|
@ -72,26 +72,6 @@ export function getImage(): SourceMetadata | undefined {
|
|||||||
return store.getState().toJS().selection.image;
|
return store.getState().toJS().selection.image;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getImagePath() {
|
|
||||||
return getImage()?.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getImageSize() {
|
|
||||||
return getImage()?.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getImageName() {
|
|
||||||
return getImage()?.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getImageLogo() {
|
|
||||||
return getImage()?.logo;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getImageSupportUrl() {
|
|
||||||
return getImage()?.supportUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Check if there is a selected drive
|
* @summary Check if there is a selected drive
|
||||||
*/
|
*/
|
||||||
|
@ -26,6 +26,9 @@ const debug = _debug('etcher:models:settings');
|
|||||||
|
|
||||||
const JSON_INDENT = 2;
|
const JSON_INDENT = 2;
|
||||||
|
|
||||||
|
export const DEFAULT_WIDTH = 800;
|
||||||
|
export const DEFAULT_HEIGHT = 480;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Userdata directory path
|
* @summary Userdata directory path
|
||||||
* @description
|
* @description
|
||||||
@ -35,12 +38,12 @@ const JSON_INDENT = 2;
|
|||||||
* - `~/Library/Application Support/etcher` on macOS
|
* - `~/Library/Application Support/etcher` on macOS
|
||||||
* See https://electronjs.org/docs/api/app#appgetpathname
|
* See https://electronjs.org/docs/api/app#appgetpathname
|
||||||
*
|
*
|
||||||
* NOTE: The ternary is due to this module being loaded both,
|
* NOTE: We use the remote property when this module
|
||||||
* Electron's main process and renderer process
|
* is loaded in the Electron's renderer process
|
||||||
*/
|
*/
|
||||||
const USER_DATA_DIR = electron.app
|
const app = electron.app || electron.remote.app;
|
||||||
? electron.app.getPath('userData')
|
|
||||||
: electron.remote.app.getPath('userData');
|
const USER_DATA_DIR = app.getPath('userData');
|
||||||
|
|
||||||
const CONFIG_PATH = join(USER_DATA_DIR, 'config.json');
|
const CONFIG_PATH = join(USER_DATA_DIR, 'config.json');
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ export const DEFAULT_STATE = Immutable.fromJS({
|
|||||||
},
|
},
|
||||||
isFlashing: false,
|
isFlashing: false,
|
||||||
devicePaths: [],
|
devicePaths: [],
|
||||||
failedDevicePaths: [],
|
failedDeviceErrors: [],
|
||||||
flashResults: {},
|
flashResults: {},
|
||||||
flashState: {
|
flashState: {
|
||||||
active: 0,
|
active: 0,
|
||||||
@ -79,7 +79,7 @@ export const DEFAULT_STATE = Immutable.fromJS({
|
|||||||
*/
|
*/
|
||||||
export enum Actions {
|
export enum Actions {
|
||||||
SET_DEVICE_PATHS,
|
SET_DEVICE_PATHS,
|
||||||
SET_FAILED_DEVICE_PATHS,
|
SET_FAILED_DEVICE_ERRORS,
|
||||||
SET_AVAILABLE_TARGETS,
|
SET_AVAILABLE_TARGETS,
|
||||||
SET_FLASH_STATE,
|
SET_FLASH_STATE,
|
||||||
RESET_FLASH_STATE,
|
RESET_FLASH_STATE,
|
||||||
@ -269,7 +269,7 @@ function storeReducer(
|
|||||||
.set('flashState', DEFAULT_STATE.get('flashState'))
|
.set('flashState', DEFAULT_STATE.get('flashState'))
|
||||||
.set('flashResults', DEFAULT_STATE.get('flashResults'))
|
.set('flashResults', DEFAULT_STATE.get('flashResults'))
|
||||||
.set('devicePaths', DEFAULT_STATE.get('devicePaths'))
|
.set('devicePaths', DEFAULT_STATE.get('devicePaths'))
|
||||||
.set('failedDevicePaths', DEFAULT_STATE.get('failedDevicePaths'))
|
.set('failedDeviceErrors', DEFAULT_STATE.get('failedDeviceErrors'))
|
||||||
.set(
|
.set(
|
||||||
'lastAverageFlashingSpeed',
|
'lastAverageFlashingSpeed',
|
||||||
DEFAULT_STATE.get('lastAverageFlashingSpeed'),
|
DEFAULT_STATE.get('lastAverageFlashingSpeed'),
|
||||||
@ -295,6 +295,7 @@ function storeReducer(
|
|||||||
|
|
||||||
_.defaults(action.data, {
|
_.defaults(action.data, {
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
|
skip: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!_.isBoolean(action.data.cancelled)) {
|
if (!_.isBoolean(action.data.cancelled)) {
|
||||||
@ -335,6 +336,12 @@ function storeReducer(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.data.skip) {
|
||||||
|
return state
|
||||||
|
.set('isFlashing', false)
|
||||||
|
.set('flashResults', Immutable.fromJS(action.data));
|
||||||
|
}
|
||||||
|
|
||||||
return state
|
return state
|
||||||
.set('isFlashing', false)
|
.set('isFlashing', false)
|
||||||
.set('flashResults', Immutable.fromJS(action.data))
|
.set('flashResults', Immutable.fromJS(action.data))
|
||||||
@ -509,8 +516,8 @@ function storeReducer(
|
|||||||
return state.set('devicePaths', action.data);
|
return state.set('devicePaths', action.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.SET_FAILED_DEVICE_PATHS: {
|
case Actions.SET_FAILED_DEVICE_ERRORS: {
|
||||||
return state.set('failedDevicePaths', action.data);
|
return state.set('failedDeviceErrors', action.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
|
@ -131,6 +131,7 @@ function writerEnv() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface FlashResults {
|
interface FlashResults {
|
||||||
|
skip?: boolean;
|
||||||
cancelled?: boolean;
|
cancelled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,6 +141,7 @@ async function performWrite(
|
|||||||
onProgress: sdk.multiWrite.OnProgressFunction,
|
onProgress: sdk.multiWrite.OnProgressFunction,
|
||||||
): Promise<{ cancelled?: boolean }> {
|
): Promise<{ cancelled?: boolean }> {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
|
let skip = false;
|
||||||
ipc.serve();
|
ipc.serve();
|
||||||
const {
|
const {
|
||||||
unmountOnSuccess,
|
unmountOnSuccess,
|
||||||
@ -171,7 +173,7 @@ async function performWrite(
|
|||||||
|
|
||||||
ipc.server.on('fail', ({ device, error }) => {
|
ipc.server.on('fail', ({ device, error }) => {
|
||||||
if (device.devicePath) {
|
if (device.devicePath) {
|
||||||
flashState.addFailedDevicePath(device.devicePath);
|
flashState.addFailedDeviceError({ device, error });
|
||||||
}
|
}
|
||||||
handleErrorLogging(error, analyticsData);
|
handleErrorLogging(error, analyticsData);
|
||||||
});
|
});
|
||||||
@ -188,6 +190,11 @@ async function performWrite(
|
|||||||
cancelled = true;
|
cancelled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipc.server.on('skip', () => {
|
||||||
|
terminateServer();
|
||||||
|
skip = true;
|
||||||
|
});
|
||||||
|
|
||||||
ipc.server.on('state', onProgress);
|
ipc.server.on('state', onProgress);
|
||||||
|
|
||||||
ipc.server.on('ready', (_data, socket) => {
|
ipc.server.on('ready', (_data, socket) => {
|
||||||
@ -213,6 +220,7 @@ async function performWrite(
|
|||||||
environment: env,
|
environment: env,
|
||||||
});
|
});
|
||||||
flashResults.cancelled = cancelled || results.cancelled;
|
flashResults.cancelled = cancelled || results.cancelled;
|
||||||
|
flashResults.skip = skip;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// This happens when the child is killed using SIGKILL
|
// This happens when the child is killed using SIGKILL
|
||||||
const SIGKILL_EXIT_CODE = 137;
|
const SIGKILL_EXIT_CODE = 137;
|
||||||
@ -229,6 +237,7 @@ async function performWrite(
|
|||||||
// This likely means the child died halfway through
|
// This likely means the child died halfway through
|
||||||
if (
|
if (
|
||||||
!flashResults.cancelled &&
|
!flashResults.cancelled &&
|
||||||
|
!flashResults.skip &&
|
||||||
!_.get(flashResults, ['results', 'bytesWritten'])
|
!_.get(flashResults, ['results', 'bytesWritten'])
|
||||||
) {
|
) {
|
||||||
reject(
|
reject(
|
||||||
@ -286,8 +295,7 @@ export async function flash(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
flashState.unsetFlashingFlag({ cancelled: false, errorCode: error.code });
|
flashState.unsetFlashingFlag({ cancelled: false, errorCode: error.code });
|
||||||
windowProgress.clear();
|
windowProgress.clear();
|
||||||
let { results } = flashState.getFlashResults();
|
const { results = {} } = flashState.getFlashResults();
|
||||||
results = results || {};
|
|
||||||
const eventData = {
|
const eventData = {
|
||||||
...analyticsData,
|
...analyticsData,
|
||||||
errors: results.errors,
|
errors: results.errors,
|
||||||
@ -306,7 +314,7 @@ export async function flash(
|
|||||||
};
|
};
|
||||||
analytics.logEvent('Elevation cancelled', eventData);
|
analytics.logEvent('Elevation cancelled', eventData);
|
||||||
} else {
|
} else {
|
||||||
const { results } = flashState.getFlashResults();
|
const { results = {} } = flashState.getFlashResults();
|
||||||
const eventData = {
|
const eventData = {
|
||||||
...analyticsData,
|
...analyticsData,
|
||||||
errors: results.errors,
|
errors: results.errors,
|
||||||
@ -322,17 +330,18 @@ export async function flash(
|
|||||||
/**
|
/**
|
||||||
* @summary Cancel write operation
|
* @summary Cancel write operation
|
||||||
*/
|
*/
|
||||||
export async function cancel() {
|
export async function cancel(type: string) {
|
||||||
|
const status = type.toLowerCase();
|
||||||
const drives = selectionState.getSelectedDevices();
|
const drives = selectionState.getSelectedDevices();
|
||||||
const analyticsData = {
|
const analyticsData = {
|
||||||
image: selectionState.getImagePath(),
|
image: selectionState.getImage()?.path,
|
||||||
drives,
|
drives,
|
||||||
driveCount: drives.length,
|
driveCount: drives.length,
|
||||||
uuid: flashState.getFlashUuid(),
|
uuid: flashState.getFlashUuid(),
|
||||||
flashInstanceUuid: flashState.getFlashUuid(),
|
flashInstanceUuid: flashState.getFlashUuid(),
|
||||||
unmountOnSuccess: await settings.get('unmountOnSuccess'),
|
unmountOnSuccess: await settings.get('unmountOnSuccess'),
|
||||||
validateWriteOnSuccess: await settings.get('validateWriteOnSuccess'),
|
validateWriteOnSuccess: await settings.get('validateWriteOnSuccess'),
|
||||||
status: 'cancel',
|
status,
|
||||||
};
|
};
|
||||||
analytics.logEvent('Cancel', analyticsData);
|
analytics.logEvent('Cancel', analyticsData);
|
||||||
|
|
||||||
@ -342,7 +351,7 @@ export async function cancel() {
|
|||||||
// @ts-ignore (no Server.sockets in @types/node-ipc)
|
// @ts-ignore (no Server.sockets in @types/node-ipc)
|
||||||
const [socket] = ipc.server.sockets;
|
const [socket] = ipc.server.sockets;
|
||||||
if (socket !== undefined) {
|
if (socket !== undefined) {
|
||||||
ipc.server.emit(socket, 'cancel');
|
ipc.server.emit(socket, status);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
analytics.logException(error);
|
analytics.logException(error);
|
||||||
|
@ -82,14 +82,12 @@ async function flashImageToDrive(
|
|||||||
try {
|
try {
|
||||||
await imageWriter.flash(image, drives);
|
await imageWriter.flash(image, drives);
|
||||||
if (!flashState.wasLastFlashCancelled()) {
|
if (!flashState.wasLastFlashCancelled()) {
|
||||||
const flashResults: any = flashState.getFlashResults();
|
const {
|
||||||
|
results = { devices: { successful: 0, failed: 0 } },
|
||||||
|
} = flashState.getFlashResults();
|
||||||
notification.send(
|
notification.send(
|
||||||
'Flash complete!',
|
'Flash complete!',
|
||||||
messages.info.flashComplete(
|
messages.info.flashComplete(basename, drives as any, results.devices),
|
||||||
basename,
|
|
||||||
drives as any,
|
|
||||||
flashResults.results.devices,
|
|
||||||
),
|
|
||||||
iconPath,
|
iconPath,
|
||||||
);
|
);
|
||||||
goToSuccess();
|
goToSuccess();
|
||||||
|
@ -25,7 +25,6 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import FinishPage from '../../components/finish/finish';
|
import FinishPage from '../../components/finish/finish';
|
||||||
import { ReducedFlashingInfos } from '../../components/reduced-flashing-infos/reduced-flashing-infos';
|
import { ReducedFlashingInfos } from '../../components/reduced-flashing-infos/reduced-flashing-infos';
|
||||||
import { SafeWebview } from '../../components/safe-webview/safe-webview';
|
|
||||||
import { SettingsModal } from '../../components/settings/settings';
|
import { SettingsModal } from '../../components/settings/settings';
|
||||||
import {
|
import {
|
||||||
SourceMetadata,
|
SourceMetadata,
|
||||||
@ -48,6 +47,7 @@ import {
|
|||||||
import { FlashStep } from './Flash';
|
import { FlashStep } from './Flash';
|
||||||
|
|
||||||
import EtcherSvg from '../../../assets/etcher.svg';
|
import EtcherSvg from '../../../assets/etcher.svg';
|
||||||
|
import { SafeWebview } from '../../components/safe-webview/safe-webview';
|
||||||
|
|
||||||
const Icon = styled(BaseIcon)`
|
const Icon = styled(BaseIcon)`
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
@ -132,12 +132,13 @@ export class MainPage extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private stateHelper(): MainPageStateFromStore {
|
private stateHelper(): MainPageStateFromStore {
|
||||||
|
const image = selectionState.getImage();
|
||||||
return {
|
return {
|
||||||
isFlashing: flashState.isFlashing(),
|
isFlashing: flashState.isFlashing(),
|
||||||
hasImage: selectionState.hasImage(),
|
hasImage: selectionState.hasImage(),
|
||||||
hasDrive: selectionState.hasDrive(),
|
hasDrive: selectionState.hasDrive(),
|
||||||
imageLogo: selectionState.getImageLogo(),
|
imageLogo: image?.logo,
|
||||||
imageSize: selectionState.getImageSize(),
|
imageSize: image?.size,
|
||||||
imageName: getImageBasename(selectionState.getImage()),
|
imageName: getImageBasename(selectionState.getImage()),
|
||||||
driveTitle: getDrivesTitle(),
|
driveTitle: getDrivesTitle(),
|
||||||
driveLabel: getDriveListLabel(),
|
driveLabel: getDriveListLabel(),
|
||||||
@ -169,71 +170,6 @@ export class MainPage extends React.Component<
|
|||||||
const notFlashingOrSplitView =
|
const notFlashingOrSplitView =
|
||||||
!this.state.isFlashing || !this.state.isWebviewShowing;
|
!this.state.isFlashing || !this.state.isWebviewShowing;
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<Flex
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
paddingTop="14px"
|
|
||||||
style={{
|
|
||||||
// Allow window to be dragged from header
|
|
||||||
// @ts-ignore
|
|
||||||
'-webkit-app-region': 'drag',
|
|
||||||
position: 'relative',
|
|
||||||
zIndex: 1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex width="100%" />
|
|
||||||
<Flex width="100%" alignItems="center" justifyContent="center">
|
|
||||||
<EtcherSvg
|
|
||||||
width="123px"
|
|
||||||
height="22px"
|
|
||||||
style={{
|
|
||||||
cursor: 'pointer',
|
|
||||||
}}
|
|
||||||
onClick={() =>
|
|
||||||
openExternal('https://www.balena.io/etcher?ref=etcher_footer')
|
|
||||||
}
|
|
||||||
tabIndex={100}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex width="100%" alignItems="center" justifyContent="flex-end">
|
|
||||||
<Icon
|
|
||||||
icon={<CogSvg height="1em" fill="currentColor" />}
|
|
||||||
plain
|
|
||||||
tabIndex={5}
|
|
||||||
onClick={() => this.setState({ hideSettings: false })}
|
|
||||||
style={{
|
|
||||||
// Make touch events click instead of dragging
|
|
||||||
'-webkit-app-region': 'no-drag',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{!settings.getSync('disableExternalLinks') && (
|
|
||||||
<Icon
|
|
||||||
icon={<QuestionCircleSvg height="1em" fill="currentColor" />}
|
|
||||||
onClick={() =>
|
|
||||||
openExternal(
|
|
||||||
selectionState.getImageSupportUrl() ||
|
|
||||||
'https://github.com/balena-io/etcher/blob/master/SUPPORT.md',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
tabIndex={6}
|
|
||||||
style={{
|
|
||||||
// Make touch events click instead of dragging
|
|
||||||
'-webkit-app-region': 'no-drag',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
{this.state.hideSettings ? null : (
|
|
||||||
<SettingsModal
|
|
||||||
toggleModal={(value: boolean) => {
|
|
||||||
this.setState({ hideSettings: !value });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Flex
|
<Flex
|
||||||
m={`110px ${this.state.isWebviewShowing ? 35 : 55}px`}
|
m={`110px ${this.state.isWebviewShowing ? 35 : 55}px`}
|
||||||
justifyContent="space-between"
|
justifyContent="space-between"
|
||||||
@ -315,35 +251,86 @@ export class MainPage extends React.Component<
|
|||||||
style={{ zIndex: 1 }}
|
style={{ zIndex: 1 }}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderSuccess() {
|
private renderSuccess() {
|
||||||
return (
|
return (
|
||||||
<Flex flexDirection="column" alignItems="center" height="100%">
|
|
||||||
<FinishPage
|
<FinishPage
|
||||||
goToMain={() => {
|
goToMain={() => {
|
||||||
flashState.resetState();
|
flashState.resetState();
|
||||||
this.setState({ current: 'main' });
|
this.setState({ current: 'main' });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<SafeWebview
|
|
||||||
src="https://www.balena.io/etcher/success-banner/"
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '320px',
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: 0,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<ThemedProvider style={{ height: '100%', width: '100%' }}>
|
<ThemedProvider style={{ height: '100%', width: '100%' }}>
|
||||||
|
<Flex
|
||||||
|
justifyContent="space-between"
|
||||||
|
alignItems="center"
|
||||||
|
paddingTop="14px"
|
||||||
|
style={{
|
||||||
|
// Allow window to be dragged from header
|
||||||
|
// @ts-ignore
|
||||||
|
'-webkit-app-region': 'drag',
|
||||||
|
position: 'relative',
|
||||||
|
zIndex: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex width="100%" />
|
||||||
|
<Flex width="100%" alignItems="center" justifyContent="center">
|
||||||
|
<EtcherSvg
|
||||||
|
width="123px"
|
||||||
|
height="22px"
|
||||||
|
style={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
onClick={() =>
|
||||||
|
openExternal('https://www.balena.io/etcher?ref=etcher_footer')
|
||||||
|
}
|
||||||
|
tabIndex={100}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex width="100%" alignItems="center" justifyContent="flex-end">
|
||||||
|
<Icon
|
||||||
|
icon={<CogSvg height="1em" fill="currentColor" />}
|
||||||
|
plain
|
||||||
|
tabIndex={5}
|
||||||
|
onClick={() => this.setState({ hideSettings: false })}
|
||||||
|
style={{
|
||||||
|
// Make touch events click instead of dragging
|
||||||
|
'-webkit-app-region': 'no-drag',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{!settings.getSync('disableExternalLinks') && (
|
||||||
|
<Icon
|
||||||
|
icon={<QuestionCircleSvg height="1em" fill="currentColor" />}
|
||||||
|
onClick={() =>
|
||||||
|
openExternal(
|
||||||
|
selectionState.getImage()?.supportUrl ||
|
||||||
|
'https://github.com/balena-io/etcher/blob/master/SUPPORT.md',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
tabIndex={6}
|
||||||
|
style={{
|
||||||
|
// Make touch events click instead of dragging
|
||||||
|
'-webkit-app-region': 'no-drag',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
{this.state.hideSettings ? null : (
|
||||||
|
<SettingsModal
|
||||||
|
toggleModal={(value: boolean) => {
|
||||||
|
this.setState({ hideSettings: !value });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{this.state.current === 'main'
|
{this.state.current === 'main'
|
||||||
? this.renderMain()
|
? this.renderMain()
|
||||||
: this.renderSuccess()}
|
: this.renderSuccess()}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import * as _ from 'lodash';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {
|
||||||
Alert as AlertBase,
|
Alert as AlertBase,
|
||||||
@ -23,27 +24,16 @@ import {
|
|||||||
ButtonProps,
|
ButtonProps,
|
||||||
Modal as ModalBase,
|
Modal as ModalBase,
|
||||||
Provider,
|
Provider,
|
||||||
|
Table as BaseTable,
|
||||||
|
TableProps as BaseTableProps,
|
||||||
Txt,
|
Txt,
|
||||||
Theme as renditionTheme,
|
|
||||||
} from 'rendition';
|
} from 'rendition';
|
||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
import { colors, theme } from './theme';
|
import { colors, theme } from './theme';
|
||||||
|
|
||||||
const defaultTheme = {
|
|
||||||
...renditionTheme,
|
|
||||||
...theme,
|
|
||||||
layer: {
|
|
||||||
extend: () => `
|
|
||||||
> div:first-child {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ThemedProvider = (props: any) => (
|
export const ThemedProvider = (props: any) => (
|
||||||
<Provider theme={defaultTheme} {...props}></Provider>
|
<Provider theme={theme} {...props}></Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const BaseButton = styled(Button)`
|
export const BaseButton = styled(Button)`
|
||||||
@ -134,25 +124,8 @@ const modalFooterShadowCss = css`
|
|||||||
background-attachment: local, local, scroll, scroll;
|
background-attachment: local, local, scroll, scroll;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Modal = styled(({ style, ...props }) => {
|
export const Modal = styled(({ style, children, ...props }) => {
|
||||||
return (
|
return (
|
||||||
<Provider
|
|
||||||
theme={{
|
|
||||||
...defaultTheme,
|
|
||||||
header: {
|
|
||||||
height: '50px',
|
|
||||||
},
|
|
||||||
layer: {
|
|
||||||
extend: () => `
|
|
||||||
${defaultTheme.layer.extend()}
|
|
||||||
|
|
||||||
> div:last-child {
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ModalBase
|
<ModalBase
|
||||||
position="top"
|
position="top"
|
||||||
width="97vw"
|
width="97vw"
|
||||||
@ -167,14 +140,22 @@ export const Modal = styled(({ style, ...props }) => {
|
|||||||
...style,
|
...style,
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
>
|
||||||
</Provider>
|
<ScrollableFlex flexDirection="column" width="100%" height="90%">
|
||||||
|
{...children}
|
||||||
|
</ScrollableFlex>
|
||||||
|
</ModalBase>
|
||||||
);
|
);
|
||||||
})`
|
})`
|
||||||
> div {
|
> div {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
> div:first-child {
|
||||||
|
height: 81%;
|
||||||
|
padding: 24px 30px 0;
|
||||||
|
}
|
||||||
|
|
||||||
> h3 {
|
> h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 24px 30px 0;
|
padding: 24px 30px 0;
|
||||||
@ -188,12 +169,9 @@ export const Modal = styled(({ style, ...props }) => {
|
|||||||
|
|
||||||
> div:nth-child(2) {
|
> div:nth-child(2) {
|
||||||
height: 61%;
|
height: 61%;
|
||||||
|
|
||||||
> div:not(.system-drive-alert) {
|
|
||||||
padding: 0 30px;
|
padding: 0 30px;
|
||||||
${modalFooterShadowCss}
|
${modalFooterShadowCss}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
> div:last-child {
|
> div:last-child {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -249,3 +227,99 @@ export const Alert = styled((props) => (
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export interface GenericTableProps<T> extends BaseTableProps<T> {
|
||||||
|
refFn: (t: BaseTable<T>) => void;
|
||||||
|
data: T[];
|
||||||
|
checkedRowsNumber?: number;
|
||||||
|
multipleSelection: boolean;
|
||||||
|
showWarnings?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GenericTable: <T>(
|
||||||
|
props: GenericTableProps<T>,
|
||||||
|
) => React.ReactElement<GenericTableProps<T>> = <T extends {}>({
|
||||||
|
refFn,
|
||||||
|
...props
|
||||||
|
}: GenericTableProps<T>) => (
|
||||||
|
<div>
|
||||||
|
<BaseTable<T> ref={refFn} {...props} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
function StyledTable<T>() {
|
||||||
|
return styled((props: GenericTableProps<T>) => (
|
||||||
|
<GenericTable<T> {...props} />
|
||||||
|
))`
|
||||||
|
[data-display='table-head']
|
||||||
|
> [data-display='table-row']
|
||||||
|
> [data-display='table-cell'] {
|
||||||
|
position: sticky;
|
||||||
|
background-color: #f8f9fd;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
input[type='checkbox'] + div {
|
||||||
|
display: ${(props) => (props.multipleSelection ? 'flex' : 'none')};
|
||||||
|
|
||||||
|
${(props) =>
|
||||||
|
props.multipleSelection &&
|
||||||
|
props.checkedRowsNumber !== 0 &&
|
||||||
|
props.checkedRowsNumber !== props.data.length
|
||||||
|
? `
|
||||||
|
font-weight: 600;
|
||||||
|
color: ${colors.primary.foreground};
|
||||||
|
background: ${colors.primary.background};
|
||||||
|
|
||||||
|
::after {
|
||||||
|
content: '–';
|
||||||
|
}
|
||||||
|
`
|
||||||
|
: ''}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-display='table-head'] > [data-display='table-row'],
|
||||||
|
[data-display='table-body'] > [data-display='table-row'] {
|
||||||
|
> [data-display='table-cell']:first-child {
|
||||||
|
padding-left: 15px;
|
||||||
|
width: 6%;
|
||||||
|
}
|
||||||
|
|
||||||
|
> [data-display='table-cell']:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-display='table-body'] > [data-display='table-row'] {
|
||||||
|
&:nth-of-type(2n) {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-highlight='true'] {
|
||||||
|
&.system {
|
||||||
|
background-color: ${(props) => (props.showWarnings ? '#fff5e6' : '#e8f5fc')};
|
||||||
|
}
|
||||||
|
|
||||||
|
> [data-display='table-cell']:first-child {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&& [data-display='table-row'] > [data-display='table-cell'] {
|
||||||
|
padding: 6px 8px;
|
||||||
|
color: #2a506f;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='checkbox'] + div {
|
||||||
|
border-radius: ${(props) => (props.multipleSelection ? '4px' : '50%')};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Table = <T extends {}>(props: GenericTableProps<T>) => {
|
||||||
|
const TypedStyledFunctional = StyledTable<T>();
|
||||||
|
return <TypedStyledFunctional {...props} />;
|
||||||
|
};
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import { Theme } from 'rendition';
|
||||||
|
|
||||||
export const colors = {
|
export const colors = {
|
||||||
dark: {
|
dark: {
|
||||||
foreground: '#fff',
|
foreground: '#fff',
|
||||||
@ -67,9 +70,12 @@ export const colors = {
|
|||||||
|
|
||||||
const font = 'SourceSansPro';
|
const font = 'SourceSansPro';
|
||||||
|
|
||||||
export const theme = {
|
export const theme = _.merge({}, Theme, {
|
||||||
colors,
|
colors,
|
||||||
font,
|
font,
|
||||||
|
header: {
|
||||||
|
height: '40px',
|
||||||
|
},
|
||||||
global: {
|
global: {
|
||||||
font: {
|
font: {
|
||||||
family: font,
|
family: font,
|
||||||
@ -109,4 +115,11 @@ export const theme = {
|
|||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
};
|
layer: {
|
||||||
|
extend: () => `
|
||||||
|
> div:first-child {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
@ -122,8 +122,8 @@ interface AutoUpdaterConfig {
|
|||||||
|
|
||||||
async function createMainWindow() {
|
async function createMainWindow() {
|
||||||
const fullscreen = Boolean(await settings.get('fullscreen'));
|
const fullscreen = Boolean(await settings.get('fullscreen'));
|
||||||
const defaultWidth = 800;
|
const defaultWidth = settings.DEFAULT_WIDTH;
|
||||||
const defaultHeight = 480;
|
const defaultHeight = settings.DEFAULT_HEIGHT;
|
||||||
let width = defaultWidth;
|
let width = defaultWidth;
|
||||||
let height = defaultHeight;
|
let height = defaultHeight;
|
||||||
if (fullscreen) {
|
if (fullscreen) {
|
||||||
|
@ -55,8 +55,9 @@ function log(message: string) {
|
|||||||
/**
|
/**
|
||||||
* @summary Terminate the child writer process
|
* @summary Terminate the child writer process
|
||||||
*/
|
*/
|
||||||
function terminate(exitCode: number) {
|
async function terminate(exitCode: number) {
|
||||||
ipc.disconnect(IPC_SERVER_ID);
|
ipc.disconnect(IPC_SERVER_ID);
|
||||||
|
await cleanupTmpFiles(Date.now());
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
process.exit(exitCode || SUCCESS);
|
process.exit(exitCode || SUCCESS);
|
||||||
});
|
});
|
||||||
@ -68,17 +69,28 @@ function terminate(exitCode: number) {
|
|||||||
async function handleError(error: Error) {
|
async function handleError(error: Error) {
|
||||||
ipc.of[IPC_SERVER_ID].emit('error', toJSON(error));
|
ipc.of[IPC_SERVER_ID].emit('error', toJSON(error));
|
||||||
await delay(DISCONNECT_DELAY);
|
await delay(DISCONNECT_DELAY);
|
||||||
terminate(GENERAL_ERROR);
|
await terminate(GENERAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WriteResult {
|
export interface FlashError extends Error {
|
||||||
bytesWritten: number;
|
description: string;
|
||||||
devices: {
|
device: string;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WriteResult {
|
||||||
|
bytesWritten?: number;
|
||||||
|
devices?: {
|
||||||
failed: number;
|
failed: number;
|
||||||
successful: number;
|
successful: number;
|
||||||
};
|
};
|
||||||
errors: Array<Error & { device: string }>;
|
errors: FlashError[];
|
||||||
sourceMetadata: sdk.sourceDestination.Metadata;
|
sourceMetadata?: sdk.sourceDestination.Metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FlashResults extends WriteResult {
|
||||||
|
skip?: boolean;
|
||||||
|
cancelled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,8 +148,10 @@ async function writeAndValidate({
|
|||||||
sourceMetadata,
|
sourceMetadata,
|
||||||
};
|
};
|
||||||
for (const [destination, error] of failures) {
|
for (const [destination, error] of failures) {
|
||||||
const err = error as Error & { device: string };
|
const err = error as FlashError;
|
||||||
err.device = (destination as sdk.sourceDestination.BlockDevice).device;
|
const drive = destination as sdk.sourceDestination.BlockDevice;
|
||||||
|
err.device = drive.device;
|
||||||
|
err.description = drive.description;
|
||||||
result.errors.push(err);
|
result.errors.push(err);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -163,22 +177,22 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
|||||||
// no flashing information is available, then it will
|
// no flashing information is available, then it will
|
||||||
// assume that the child died halfway through.
|
// assume that the child died halfway through.
|
||||||
|
|
||||||
process.once('SIGINT', () => {
|
process.once('SIGINT', async () => {
|
||||||
terminate(SUCCESS);
|
await terminate(SUCCESS);
|
||||||
});
|
});
|
||||||
|
|
||||||
process.once('SIGTERM', () => {
|
process.once('SIGTERM', async () => {
|
||||||
terminate(SUCCESS);
|
await terminate(SUCCESS);
|
||||||
});
|
});
|
||||||
|
|
||||||
// The IPC server failed. Abort.
|
// The IPC server failed. Abort.
|
||||||
ipc.of[IPC_SERVER_ID].on('error', () => {
|
ipc.of[IPC_SERVER_ID].on('error', async () => {
|
||||||
terminate(SUCCESS);
|
await terminate(SUCCESS);
|
||||||
});
|
});
|
||||||
|
|
||||||
// The IPC server was disconnected. Abort.
|
// The IPC server was disconnected. Abort.
|
||||||
ipc.of[IPC_SERVER_ID].on('disconnect', () => {
|
ipc.of[IPC_SERVER_ID].on('disconnect', async () => {
|
||||||
terminate(SUCCESS);
|
await terminate(SUCCESS);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc.of[IPC_SERVER_ID].on('write', async (options: WriteOptions) => {
|
ipc.of[IPC_SERVER_ID].on('write', async (options: WriteOptions) => {
|
||||||
@ -203,11 +217,20 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
|||||||
log('Abort');
|
log('Abort');
|
||||||
ipc.of[IPC_SERVER_ID].emit('abort');
|
ipc.of[IPC_SERVER_ID].emit('abort');
|
||||||
await delay(DISCONNECT_DELAY);
|
await delay(DISCONNECT_DELAY);
|
||||||
terminate(exitCode);
|
await terminate(exitCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSkip = async () => {
|
||||||
|
log('Skip validation');
|
||||||
|
ipc.of[IPC_SERVER_ID].emit('skip');
|
||||||
|
await delay(DISCONNECT_DELAY);
|
||||||
|
await terminate(exitCode);
|
||||||
};
|
};
|
||||||
|
|
||||||
ipc.of[IPC_SERVER_ID].on('cancel', onAbort);
|
ipc.of[IPC_SERVER_ID].on('cancel', onAbort);
|
||||||
|
|
||||||
|
ipc.of[IPC_SERVER_ID].on('skip', onSkip);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Failure handler (non-fatal errors)
|
* @summary Failure handler (non-fatal errors)
|
||||||
* @param {SourceDestination} destination - destination
|
* @param {SourceDestination} destination - destination
|
||||||
@ -275,7 +298,7 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
|||||||
});
|
});
|
||||||
ipc.of[IPC_SERVER_ID].emit('done', { results });
|
ipc.of[IPC_SERVER_ID].emit('done', { results });
|
||||||
await delay(DISCONNECT_DELAY);
|
await delay(DISCONNECT_DELAY);
|
||||||
terminate(exitCode);
|
await terminate(exitCode);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log(`Error: ${error.message}`);
|
log(`Error: ${error.message}`);
|
||||||
exitCode = GENERAL_ERROR;
|
exitCode = GENERAL_ERROR;
|
||||||
|
@ -73,9 +73,7 @@ export function isSourceDrive(
|
|||||||
): boolean {
|
): boolean {
|
||||||
if (selection) {
|
if (selection) {
|
||||||
if (selection.drive) {
|
if (selection.drive) {
|
||||||
const sourcePath = selection.drive.devicePath || selection.drive.device;
|
return selection.drive.device === drive.device;
|
||||||
const drivePath = drive.devicePath || drive.device;
|
|
||||||
return pathIsInside(sourcePath, drivePath);
|
|
||||||
}
|
}
|
||||||
if (selection.path) {
|
if (selection.path) {
|
||||||
return sourceIsInsideDrive(selection.path, drive);
|
return sourceIsInsideDrive(selection.path, drive);
|
||||||
|
155
npm-shrinkwrap.json
generated
155
npm-shrinkwrap.json
generated
@ -1570,34 +1570,32 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@react-google-maps/api": {
|
"@react-google-maps/api": {
|
||||||
"version": "1.9.12",
|
"version": "1.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-1.9.12.tgz",
|
"resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-1.10.1.tgz",
|
||||||
"integrity": "sha512-YpYZOMduxiQIt8+njdffoqD4fYdOugudoafnAD1N+mEUrVnFlslUPMQ+gOJwuYdlkTAR5NZUbCt80LJWEN+ZnA==",
|
"integrity": "sha512-hb8urUcwZw99Cu3yQnZWUbXjR1Ym/8C21kSX6B02I29l6DXNxDbJ5Jo/T5swhnizPKY7TNhR1oTctC/HY7SQWA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@react-google-maps/infobox": "1.9.11",
|
"@react-google-maps/infobox": "1.10.0",
|
||||||
"@react-google-maps/marker-clusterer": "1.9.11",
|
"@react-google-maps/marker-clusterer": "1.10.0",
|
||||||
"acorn": "7.4.0",
|
|
||||||
"acorn-jsx": "^5.2.0",
|
|
||||||
"invariant": "2.2.4"
|
"invariant": "2.2.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@react-google-maps/infobox": {
|
"@react-google-maps/infobox": {
|
||||||
"version": "1.9.11",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-1.9.11.tgz",
|
"resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-1.10.0.tgz",
|
||||||
"integrity": "sha512-22ewm+OpOh69ikypG29idsdRz2OWeFsN+8zvYBzSETxKP782rmUGqhSIvXXmHa8TOcktm7EaEqOWWvZwaxymag==",
|
"integrity": "sha512-MhT2nMmjeG7TCxRv/JdylDyNd/n66ggSQQhTWVjJJTtdB/xqd0T8BHCkBWDN9uF0i0yCZzMFl2P2Y1zJ+xppBg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@react-google-maps/marker-clusterer": {
|
"@react-google-maps/marker-clusterer": {
|
||||||
"version": "1.9.11",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-1.9.11.tgz",
|
"resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-1.10.0.tgz",
|
||||||
"integrity": "sha512-yIABKlkORju131efXUZs/tL7FCK9IXtvy2M9SQRZy/mwgoOIYeoJlPPaBjn81DQqZLRj6AdAocydk+MnjWqFiQ==",
|
"integrity": "sha512-3GLVgeXNStVcdiLMxzi3cBjr32ctlexLPPGQguwcYd6yPLaCcnVCwyzhV68KvL00xqOAD1c3aABV9EGgY8u6Qw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@rjsf/core": {
|
"@rjsf/core": {
|
||||||
"version": "2.3.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rjsf/core/-/core-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rjsf/core/-/core-2.4.0.tgz",
|
||||||
"integrity": "sha512-OZKYHt9tjKhzOH4CvsPiCwepuIacqI++cNmnL2fsxh1IF+uEWGlo3NLDWhhSaBbOv9jps6a5YQcLbLtjNuSwug==",
|
"integrity": "sha512-8zlydBkGldOxGXFEwNGFa1gzTxpcxaYn7ofegcu8XHJ7IKMCfpnU3ABg+H3eml1KZCX3FODmj1tHFJKuTmfynw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime-corejs2": "^7.8.7",
|
"@babel/runtime-corejs2": "^7.8.7",
|
||||||
@ -2180,9 +2178,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/react-native": {
|
"@types/react-native": {
|
||||||
"version": "0.63.9",
|
"version": "0.63.18",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.63.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.63.18.tgz",
|
||||||
"integrity": "sha512-6ec/z9zjAkFH3rD1RYqbrA/Lj+jux6bumWCte4yRy3leyelTdqtmOd2Ph+86IXQQzsIArEMBwmraAbNQ0J3UAA==",
|
"integrity": "sha512-WwEWqmHiqFn61M1FZR/+frj+E8e2o8i5cPqu9mjbjtZS/gBfCKVESF2ai/KAlaQECkkWkx/nMJeCc5eHMmLQgw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
@ -2237,9 +2235,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/styled-components": {
|
"@types/styled-components": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.3.tgz",
|
||||||
"integrity": "sha512-HNocYLfrsnNNm8NTS/W53OERSjRA8dx5Bn6wBd2rXXwt4Z3s+oqvY6/PbVt3e6sgtzI63GX//WiWiRhWur08qQ==",
|
"integrity": "sha512-HGpirof3WOhiX17lb61Q/tpgqn48jxO8EfZkdJ8ueYqwLbK2AHQe/G08DasdA2IdKnmwOIP1s9X2bopxKXgjRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/hoist-non-react-statics": "*",
|
"@types/hoist-non-react-statics": "*",
|
||||||
@ -2692,18 +2690,6 @@
|
|||||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"acorn": {
|
|
||||||
"version": "7.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
|
|
||||||
"integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==",
|
|
||||||
"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
|
|
||||||
},
|
|
||||||
"agent-base": {
|
"agent-base": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
|
||||||
@ -5281,6 +5267,12 @@
|
|||||||
"assert-plus": "^1.0.0"
|
"assert-plus": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"date-fns": {
|
||||||
|
"version": "2.16.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz",
|
||||||
|
"integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"de-indent": {
|
"de-indent": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||||
@ -5496,15 +5488,6 @@
|
|||||||
"minimalistic-assert": "^1.0.0"
|
"minimalistic-assert": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"detab": {
|
|
||||||
"version": "2.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/detab/-/detab-2.0.3.tgz",
|
|
||||||
"integrity": "sha512-Up8P0clUVwq0FnFjDclzZsy9PadzRn5FFxrr47tQQvMHqyiFYVbpH8oXDzWtF0Q7pYy3l+RPmtBl+BsFF6wH0A==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"repeat-string": "^1.5.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"detect-file": {
|
"detect-file": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
|
||||||
@ -8939,9 +8922,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"json-e": {
|
"json-e": {
|
||||||
"version": "4.1.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/json-e/-/json-e-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/json-e/-/json-e-4.3.0.tgz",
|
||||||
"integrity": "sha512-Jb8kMB1lICgjAAppv+q0EFFovOPdjE3htb7pt9+uE2j3J1W5ZCuBOmAdGi0OUetCZ4wqSO6qT/Np36XDRjHH7w==",
|
"integrity": "sha512-E3zcmx6pHsBgQ4ZztQNG4OAZHreBZfGBrg68kv9nGOkRqAdKfs792asP/wp9Fayfx1THDiHKYStqWJj/N7Bb9A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"json-stable-stringify-without-jsonify": "^1.0.1"
|
"json-stable-stringify-without-jsonify": "^1.0.1"
|
||||||
@ -9749,18 +9732,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mdast-util-to-hast": {
|
"mdast-util-to-hast": {
|
||||||
"version": "9.1.0",
|
"version": "9.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-9.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-9.1.1.tgz",
|
||||||
"integrity": "sha512-Akl2Vi9y9cSdr19/Dfu58PVwifPXuFt1IrHe7l+Crme1KvgUT+5z+cHLVcQVGCiNTZZcdqjnuv9vPkGsqWytWA==",
|
"integrity": "sha512-vpMWKFKM2mnle+YbNgDXxx95vv0CoLU0v/l3F5oFAG5DV7qwkZVWA206LsAdOnEVyf5vQcLnb3cWJywu7mUxsQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/mdast": "^3.0.0",
|
"@types/mdast": "^3.0.0",
|
||||||
"@types/unist": "^2.0.3",
|
"@types/unist": "^2.0.3",
|
||||||
"collapse-white-space": "^1.0.0",
|
|
||||||
"detab": "^2.0.0",
|
|
||||||
"mdast-util-definitions": "^3.0.0",
|
"mdast-util-definitions": "^3.0.0",
|
||||||
"mdurl": "^1.0.0",
|
"mdurl": "^1.0.0",
|
||||||
"trim-lines": "^1.0.0",
|
|
||||||
"unist-builder": "^2.0.0",
|
"unist-builder": "^2.0.0",
|
||||||
"unist-util-generated": "^1.0.0",
|
"unist-util-generated": "^1.0.0",
|
||||||
"unist-util-position": "^3.0.0",
|
"unist-util-position": "^3.0.0",
|
||||||
@ -9842,9 +9822,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"crypto-random-string": {
|
"crypto-random-string": {
|
||||||
"version": "3.2.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-3.3.0.tgz",
|
||||||
"integrity": "sha512-8vPu5bsKaq2uKRy3OL7h1Oo7RayAWB8sYexLKAqvCXVib8SxgbmoF1IN4QMKjBv8uI8mp5gPPMbiRah25GMrVQ==",
|
"integrity": "sha512-teWAwfMb1d6brahYyKqcBEb5Yp8PJPvPOdOonXDnvaKOTmKDFNVE8E3Y2XQuzjNV/3XMwHbrX9fHWvrhRKt4Gg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"type-fest": "^0.8.1"
|
"type-fest": "^0.8.1"
|
||||||
@ -11897,9 +11877,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"polished": {
|
"polished": {
|
||||||
"version": "3.6.5",
|
"version": "3.6.6",
|
||||||
"resolved": "https://registry.npmjs.org/polished/-/polished-3.6.5.tgz",
|
"resolved": "https://registry.npmjs.org/polished/-/polished-3.6.6.tgz",
|
||||||
"integrity": "sha512-VwhC9MlhW7O5dg/z7k32dabcAFW1VI2+7fSe8cE/kXcfL7mVdoa5UxciYGW2sJU78ldDLT6+ROEKIZKFNTnUXQ==",
|
"integrity": "sha512-yiB2ims2DZPem0kCD6V0wnhcVGFEhNh0Iw0axNpKU+oSAgFt6yx6HxIT23Qg0WWvgS379cS35zT4AOyZZRzpQQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.9.2"
|
"@babel/runtime": "^7.9.2"
|
||||||
@ -12511,9 +12491,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-notifications-component": {
|
"react-notifications-component": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-notifications-component/-/react-notifications-component-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-notifications-component/-/react-notifications-component-2.4.1.tgz",
|
||||||
"integrity": "sha512-0IhtgqAmsKSyjY1wBUxciUVXiYGRr5BRdn67pYDlkqq9ORF98NZekpG7/MNX0BzzfGvt9Wg7rFhT1BtwOvvLLg==",
|
"integrity": "sha512-RloHzm15egnuPihf8PvldIEvPQoT9+5BE9UxCNTt+GfsWeI3SEZKyaX9mq90v899boqteLiOI736Zd4tXtl7Tg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"prop-types": "^15.6.2"
|
"prop-types": "^15.6.2"
|
||||||
@ -12660,6 +12640,21 @@
|
|||||||
"integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==",
|
"integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"regexp-match-indices": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"regexp-tree": "^0.1.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"regexp-tree": {
|
||||||
|
"version": "0.1.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.21.tgz",
|
||||||
|
"integrity": "sha512-kUUXjX4AnqnR8KRTCrayAo9PzYMRKmVoGgaz2tBuz0MF3g1ZbGebmtW0yFHfFK9CmBjQKeYIgoL22pFLBJY7sw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"regexpu-core": {
|
"regexpu-core": {
|
||||||
"version": "4.7.0",
|
"version": "4.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz",
|
||||||
@ -12827,9 +12822,9 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"rendition": {
|
"rendition": {
|
||||||
"version": "18.4.1",
|
"version": "18.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/rendition/-/rendition-18.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/rendition/-/rendition-18.8.3.tgz",
|
||||||
"integrity": "sha512-mV/0p+M8XR/Xa/ZFzgflZPHelpuONiTSa/CMMuHkmXR7vhF7tB2ORxLRc/DbymmdN6cWQwXAyA81t9TDAOhgVQ==",
|
"integrity": "sha512-kDuXFheXY9KlSvIMdB4Er2OeAnwgj9aya5Xu43hwpXxC4KlFlNKqQNmcOvKLc/Fk9dyw04TKOr1SbXyM148yRg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.25",
|
"@fortawesome/fontawesome-svg-core": "^1.2.25",
|
||||||
@ -12855,6 +12850,7 @@
|
|||||||
"color": "^3.1.2",
|
"color": "^3.1.2",
|
||||||
"color-hash": "^1.0.3",
|
"color-hash": "^1.0.3",
|
||||||
"copy-to-clipboard": "^3.0.8",
|
"copy-to-clipboard": "^3.0.8",
|
||||||
|
"date-fns": "^2.16.1",
|
||||||
"grommet": "^2.14.0",
|
"grommet": "^2.14.0",
|
||||||
"hast-util-sanitize": "^3.0.0",
|
"hast-util-sanitize": "^3.0.0",
|
||||||
"json-e": "^4.1.0",
|
"json-e": "^4.1.0",
|
||||||
@ -12869,6 +12865,7 @@
|
|||||||
"react-simplemde-editor": "^4.1.1",
|
"react-simplemde-editor": "^4.1.1",
|
||||||
"recompose": "0.26.0",
|
"recompose": "0.26.0",
|
||||||
"regex-parser": "^2.2.7",
|
"regex-parser": "^2.2.7",
|
||||||
|
"regexp-match-indices": "^1.0.2",
|
||||||
"rehype-raw": "^4.0.2",
|
"rehype-raw": "^4.0.2",
|
||||||
"rehype-react": "^6.1.0",
|
"rehype-react": "^6.1.0",
|
||||||
"rehype-sanitize": "^3.0.1",
|
"rehype-sanitize": "^3.0.1",
|
||||||
@ -12885,9 +12882,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "13.13.15",
|
"version": "13.13.20",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.20.tgz",
|
||||||
"integrity": "sha512-kwbcs0jySLxzLsa2nWUAGOd/s21WU1jebrEdtzhsj1D4Yps1EOuyI1Qcu+FD56dL7NRNIJtDDjcqIG22NwkgLw==",
|
"integrity": "sha512-1kx55tU3AvGX2Cjk2W4GMBxbgIz892V+X10S2gUreIAq8qCWgaQH+tZBOWc0bi2BKFhQt+CX0BTx28V9QPNa+A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"uuid": {
|
"uuid": {
|
||||||
@ -14745,12 +14742,6 @@
|
|||||||
"integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=",
|
"integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"trim-lines": {
|
|
||||||
"version": "1.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-1.1.3.tgz",
|
|
||||||
"integrity": "sha512-E0ZosSWYK2mkSu+KEtQ9/KqarVjA9HztOSX+9FDdNacRAq29RRV6ZQNgob3iuW8Htar9vAfEa6yyt5qBAHZDBA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"trim-trailing-lines": {
|
"trim-trailing-lines": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz",
|
||||||
@ -15035,9 +15026,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"uglify-js": {
|
"uglify-js": {
|
||||||
"version": "3.10.2",
|
"version": "3.10.4",
|
||||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.2.tgz",
|
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.4.tgz",
|
||||||
"integrity": "sha512-GXCYNwqoo0MbLARghYjxVBxDCnU0tLqN7IPLdHHbibCb1NI5zBkU2EPcy/GaVxc0BtTjqyGXJCINe6JMR2Dpow==",
|
"integrity": "sha512-kBFT3U4Dcj4/pJ52vfjCSfyLyvG9VYYuGYPmrPvAxRw/i7xHiT4VvCev+uiEMcEEiu6UNB6KgWmGtSUYIWScbw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"unbzip2-stream": {
|
"unbzip2-stream": {
|
||||||
@ -16466,9 +16457,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"whatwg-fetch": {
|
"whatwg-fetch": {
|
||||||
"version": "3.4.0",
|
"version": "3.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz",
|
||||||
"integrity": "sha512-rsum2ulz2iuZH08mJkT0Yi6JnKhwdw4oeyMjokgxd+mmqYSd9cPpOQf01TIWgjxG/U4+QR+AwKq6lSbXVxkyoQ==",
|
"integrity": "sha512-sofZVzE1wKwO+EYPbWfiwzaKovWiZXf4coEzjGP9b2GBVgQRLQUZ2QcuPpQExGDAW5GItpEm6Tl4OU5mywnAoQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"which": {
|
"which": {
|
||||||
@ -16660,9 +16651,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"xterm": {
|
"xterm": {
|
||||||
"version": "4.8.1",
|
"version": "4.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/xterm/-/xterm-4.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/xterm/-/xterm-4.9.0.tgz",
|
||||||
"integrity": "sha512-ax91ny4tI5eklqIfH79OUSGE2PUX2rGbwONmB6DfqpyhSZO8/cf++sqiaMWEVCMjACyMfnISW7C3gGMoNvNolQ==",
|
"integrity": "sha512-wGfqufmioctKr8VkbRuZbVDfjlXWGZZ1PWHy1yqqpGT3Nm6yaJx8lxDbSEBANtgaiVPTcKSp97sxOy5IlpqYfw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"xterm-addon-fit": {
|
"xterm-addon-fit": {
|
||||||
|
@ -94,7 +94,7 @@
|
|||||||
"react": "^16.8.5",
|
"react": "^16.8.5",
|
||||||
"react-dom": "^16.8.5",
|
"react-dom": "^16.8.5",
|
||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
"rendition": "^18.4.1",
|
"rendition": "^18.8.3",
|
||||||
"resin-corvus": "^2.0.5",
|
"resin-corvus": "^2.0.5",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.2",
|
||||||
"simple-progress-webpack-plugin": "^1.1.2",
|
"simple-progress-webpack-plugin": "^1.1.2",
|
||||||
|
@ -393,6 +393,7 @@ describe('Model: flashState', function () {
|
|||||||
|
|
||||||
expect(flashResults).to.deep.equal({
|
expect(flashResults).to.deep.equal({
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
|
skip: false,
|
||||||
sourceChecksum: '1234',
|
sourceChecksum: '1234',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -33,26 +33,6 @@ describe('Model: selectionState', function () {
|
|||||||
expect(selectionState.getImage()).to.be.undefined;
|
expect(selectionState.getImage()).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImagePath() should return undefined', function () {
|
|
||||||
expect(selectionState.getImagePath()).to.be.undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getImageSize() should return undefined', function () {
|
|
||||||
expect(selectionState.getImageSize()).to.be.undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getImageName() should return undefined', function () {
|
|
||||||
expect(selectionState.getImageName()).to.be.undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getImageLogo() should return undefined', function () {
|
|
||||||
expect(selectionState.getImageLogo()).to.be.undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getImageSupportUrl() should return undefined', function () {
|
|
||||||
expect(selectionState.getImageSupportUrl()).to.be.undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('hasDrive() should return false', function () {
|
it('hasDrive() should return false', function () {
|
||||||
const hasDrive = selectionState.hasDrive();
|
const hasDrive = selectionState.hasDrive();
|
||||||
expect(hasDrive).to.be.false;
|
expect(hasDrive).to.be.false;
|
||||||
@ -379,43 +359,6 @@ describe('Model: selectionState', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.getImagePath()', function () {
|
|
||||||
it('should return the image path', function () {
|
|
||||||
const imagePath = selectionState.getImagePath();
|
|
||||||
expect(imagePath).to.equal('foo.img');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('.getImageSize()', function () {
|
|
||||||
it('should return the image size', function () {
|
|
||||||
const imageSize = selectionState.getImageSize();
|
|
||||||
expect(imageSize).to.equal(999999999);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('.getImageName()', function () {
|
|
||||||
it('should return the image name', function () {
|
|
||||||
const imageName = selectionState.getImageName();
|
|
||||||
expect(imageName).to.equal('Raspbian');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('.getImageLogo()', function () {
|
|
||||||
it('should return the image logo', function () {
|
|
||||||
const imageLogo = selectionState.getImageLogo();
|
|
||||||
expect(imageLogo).to.equal(
|
|
||||||
'<svg><text fill="red">Raspbian</text></svg>',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('.getImageSupportUrl()', function () {
|
|
||||||
it('should return the image support url', function () {
|
|
||||||
const imageSupportUrl = selectionState.getImageSupportUrl();
|
|
||||||
expect(imageSupportUrl).to.equal('https://www.raspbian.org/forums/');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('.hasImage()', function () {
|
describe('.hasImage()', function () {
|
||||||
it('should return true', function () {
|
it('should return true', function () {
|
||||||
const hasImage = selectionState.hasImage();
|
const hasImage = selectionState.hasImage();
|
||||||
@ -435,9 +378,9 @@ describe('Model: selectionState', function () {
|
|||||||
SourceType: File,
|
SourceType: File,
|
||||||
});
|
});
|
||||||
|
|
||||||
const imagePath = selectionState.getImagePath();
|
const imagePath = selectionState.getImage()?.path;
|
||||||
expect(imagePath).to.equal('bar.img');
|
expect(imagePath).to.equal('bar.img');
|
||||||
const imageSize = selectionState.getImageSize();
|
const imageSize = selectionState.getImage()?.size;
|
||||||
expect(imageSize).to.equal(999999999);
|
expect(imageSize).to.equal(999999999);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -446,9 +389,9 @@ describe('Model: selectionState', function () {
|
|||||||
it('should clear the image', function () {
|
it('should clear the image', function () {
|
||||||
selectionState.deselectImage();
|
selectionState.deselectImage();
|
||||||
|
|
||||||
const imagePath = selectionState.getImagePath();
|
const imagePath = selectionState.getImage()?.path;
|
||||||
expect(imagePath).to.be.undefined;
|
expect(imagePath).to.be.undefined;
|
||||||
const imageSize = selectionState.getImageSize();
|
const imageSize = selectionState.getImage()?.size;
|
||||||
expect(imageSize).to.be.undefined;
|
expect(imageSize).to.be.undefined;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -472,9 +415,9 @@ describe('Model: selectionState', function () {
|
|||||||
it('should be able to set an image', function () {
|
it('should be able to set an image', function () {
|
||||||
selectionState.selectSource(image);
|
selectionState.selectSource(image);
|
||||||
|
|
||||||
const imagePath = selectionState.getImagePath();
|
const imagePath = selectionState.getImage()?.path;
|
||||||
expect(imagePath).to.equal('foo.img');
|
expect(imagePath).to.equal('foo.img');
|
||||||
const imageSize = selectionState.getImageSize();
|
const imageSize = selectionState.getImage()?.size;
|
||||||
expect(imageSize).to.equal(999999999);
|
expect(imageSize).to.equal(999999999);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -485,7 +428,7 @@ describe('Model: selectionState', function () {
|
|||||||
archiveExtension: 'zip',
|
archiveExtension: 'zip',
|
||||||
});
|
});
|
||||||
|
|
||||||
const imagePath = selectionState.getImagePath();
|
const imagePath = selectionState.getImage()?.path;
|
||||||
expect(imagePath).to.equal('foo.zip');
|
expect(imagePath).to.equal('foo.zip');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -496,7 +439,7 @@ describe('Model: selectionState', function () {
|
|||||||
archiveExtension: 'xz',
|
archiveExtension: 'xz',
|
||||||
});
|
});
|
||||||
|
|
||||||
const imagePath = selectionState.getImagePath();
|
const imagePath = selectionState.getImage()?.path;
|
||||||
expect(imagePath).to.equal('foo.xz');
|
expect(imagePath).to.equal('foo.xz');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -507,7 +450,7 @@ describe('Model: selectionState', function () {
|
|||||||
archiveExtension: 'gz',
|
archiveExtension: 'gz',
|
||||||
});
|
});
|
||||||
|
|
||||||
const imagePath = selectionState.getImagePath();
|
const imagePath = selectionState.getImage()?.path;
|
||||||
expect(imagePath).to.equal('something.linux-x86-64.gz');
|
expect(imagePath).to.equal('something.linux-x86-64.gz');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -675,12 +618,12 @@ describe('Model: selectionState', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('getImagePath() should return undefined', function () {
|
it('getImagePath() should return undefined', function () {
|
||||||
const imagePath = selectionState.getImagePath();
|
const imagePath = selectionState.getImage()?.path;
|
||||||
expect(imagePath).to.be.undefined;
|
expect(imagePath).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImageSize() should return undefined', function () {
|
it('getImageSize() should return undefined', function () {
|
||||||
const imageSize = selectionState.getImageSize();
|
const imageSize = selectionState.getImage()?.size;
|
||||||
expect(imageSize).to.be.undefined;
|
expect(imageSize).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -700,12 +643,12 @@ describe('Model: selectionState', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('getImagePath() should return the image path', function () {
|
it('getImagePath() should return the image path', function () {
|
||||||
const imagePath = selectionState.getImagePath();
|
const imagePath = selectionState.getImage()?.path;
|
||||||
expect(imagePath).to.equal('foo.img');
|
expect(imagePath).to.equal('foo.img');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getImageSize() should return the image size', function () {
|
it('getImageSize() should return the image size', function () {
|
||||||
const imageSize = selectionState.getImageSize();
|
const imageSize = selectionState.getImage()?.size;
|
||||||
expect(imageSize).to.equal(999999999);
|
expect(imageSize).to.equal(999999999);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -700,11 +700,6 @@ describe('Shared: DriveConstraints', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if the drive is not large enough and is a source drive', function () {
|
it('should return false if the drive is not large enough and is a source drive', function () {
|
||||||
console.log('YAYYY', {
|
|
||||||
...image,
|
|
||||||
path: path.join(this.mountpoint, 'rpi.img'),
|
|
||||||
size: 5000000000,
|
|
||||||
});
|
|
||||||
expect(
|
expect(
|
||||||
constraints.isDriveValid(this.drive, {
|
constraints.isDriveValid(this.drive, {
|
||||||
...image,
|
...image,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user