Merge pull request #3284 from balena-io/105

105
This commit is contained in:
bulldozer-balena[bot] 2020-08-26 11:11:16 +00:00 committed by GitHub
commit 12cd8a39c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 237 additions and 281 deletions

View File

@ -1,73 +0,0 @@
/*
* Copyright 2016 balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as React from 'react';
import * as settings from '../../models/settings';
import * as analytics from '../../modules/analytics';
import { SafeWebview } from '../safe-webview/safe-webview';
interface FeaturedProjectProps {
shouldShow: boolean;
onWebviewShow: (isWebviewShowing: boolean) => void;
style?: React.CSSProperties;
}
interface FeaturedProjectState {
endpoint: string | null;
show: boolean;
}
export class FeaturedProject extends React.Component<
FeaturedProjectProps,
FeaturedProjectState
> {
constructor(props: FeaturedProjectProps) {
super(props);
this.state = {
endpoint: null,
show: false,
};
}
public async componentDidMount() {
try {
const url = new URL(
(await settings.get('featuredProjectEndpoint')) ||
'https://assets.balena.io/etcher-featured/index.html',
);
url.searchParams.append('borderRight', 'false');
url.searchParams.append('darkBackground', 'true');
this.setState({ endpoint: url.toString() });
} catch (error) {
analytics.logException(error);
}
}
public render() {
const { style = {} } = this.props;
return this.state.endpoint ? (
<SafeWebview
src={this.state.endpoint}
style={{
display: this.state.show ? 'block' : 'none',
...style,
}}
{...this.props}
></SafeWebview>
) : null;
}
}

View File

@ -58,8 +58,6 @@ const API_VERSION = '2';
interface SafeWebviewProps { interface SafeWebviewProps {
// The website source URL // The website source URL
src: string; src: string;
// @summary Refresh the webview
refreshNow?: boolean;
// Webview lifecycle event // Webview lifecycle event
onWebviewShow?: (isWebviewShowing: boolean) => void; onWebviewShow?: (isWebviewShowing: boolean) => void;
style?: React.CSSProperties; style?: React.CSSProperties;

View File

@ -30,6 +30,7 @@ import {
Txt, Txt,
Card as BaseCard, Card as BaseCard,
Input, Input,
Spinner,
} from 'rendition'; } from 'rendition';
import styled from 'styled-components'; import styled from 'styled-components';
@ -137,8 +138,9 @@ const URLSelector = ({
<Modal <Modal
cancel={cancel} cancel={cancel}
primaryButtonProps={{ primaryButtonProps={{
className: loading || !imageURL ? 'disabled' : '', disabled: loading || !imageURL,
}} }}
action={loading ? <Spinner /> : 'OK'}
done={async () => { done={async () => {
setLoading(true); setLoading(true);
const urlStrings = recentImages.map((url: URL) => url.href); const urlStrings = recentImages.map((url: URL) => url.href);
@ -288,7 +290,7 @@ export class SourceSelector extends React.Component<
await this.selectImageByPath({ await this.selectImageByPath({
imagePath, imagePath,
SourceType: isURL ? sourceDestination.Http : sourceDestination.File, SourceType: isURL ? sourceDestination.Http : sourceDestination.File,
}); }).promise;
} }
private reselectImage() { private reselectImage() {
@ -344,74 +346,97 @@ export class SourceSelector extends React.Component<
} }
} }
private async selectImageByPath({ imagePath, SourceType }: SourceOptions) { private selectImageByPath({
try { imagePath,
imagePath = await replaceWindowsNetworkDriveLetter(imagePath); SourceType,
} catch (error) { }: SourceOptions): { promise: Promise<void>; cancel: () => void } {
analytics.logException(error); let cancelled = false;
} return {
cancel: () => {
cancelled = true;
},
promise: (async () => {
try {
imagePath = await replaceWindowsNetworkDriveLetter(imagePath);
} catch (error) {
analytics.logException(error);
}
if (cancelled) {
return;
}
let source; let source;
if (SourceType === sourceDestination.File) { if (SourceType === sourceDestination.File) {
source = new sourceDestination.File({ source = new sourceDestination.File({
path: imagePath, path: imagePath,
}); });
} else { } else {
if ( if (
!imagePath.startsWith('https://') && !imagePath.startsWith('https://') &&
!imagePath.startsWith('http://') !imagePath.startsWith('http://')
) { ) {
const invalidImageError = errors.createUserError({ const invalidImageError = errors.createUserError({
title: 'Unsupported protocol', title: 'Unsupported protocol',
description: messages.error.unsupportedProtocol(), description: messages.error.unsupportedProtocol(),
}); });
osDialog.showError(invalidImageError); osDialog.showError(invalidImageError);
analytics.logEvent('Unsupported protocol', { path: imagePath }); analytics.logEvent('Unsupported protocol', { path: imagePath });
return; return;
} }
source = new sourceDestination.Http({ url: imagePath }); source = new sourceDestination.Http({ url: imagePath });
} }
try { try {
const innerSource = await source.getInnerSource(); const innerSource = await source.getInnerSource();
const metadata = (await innerSource.getMetadata()) as sourceDestination.Metadata & { if (cancelled) {
hasMBR: boolean; return;
partitions: MBRPartition[] | GPTPartition[]; }
path: string; const metadata = (await innerSource.getMetadata()) as sourceDestination.Metadata & {
extension: string; hasMBR: boolean;
}; partitions: MBRPartition[] | GPTPartition[];
const partitionTable = await innerSource.getPartitionTable(); path: string;
if (partitionTable) { extension: string;
metadata.hasMBR = true; };
metadata.partitions = partitionTable.partitions; if (cancelled) {
} else { return;
metadata.hasMBR = false; }
} const partitionTable = await innerSource.getPartitionTable();
metadata.path = imagePath; if (cancelled) {
metadata.extension = path.extname(imagePath).slice(1); return;
this.selectImage(metadata); }
this.afterSelected({ if (partitionTable) {
imagePath, metadata.hasMBR = true;
SourceType, metadata.partitions = partitionTable.partitions;
}); } else {
} catch (error) { metadata.hasMBR = false;
const imageError = errors.createUserError({ }
title: 'Error opening image', metadata.path = imagePath;
description: messages.error.openImage( metadata.extension = path.extname(imagePath).slice(1);
path.basename(imagePath), this.selectImage(metadata);
error.message, this.afterSelected({
), imagePath,
}); SourceType,
osDialog.showError(imageError); });
analytics.logException(error); } catch (error) {
} finally { const imageError = errors.createUserError({
try { title: 'Error opening image',
await source.close(); description: messages.error.openImage(
} catch (error) { path.basename(imagePath),
// Noop error.message,
} ),
} });
osDialog.showError(imageError);
analytics.logException(error);
} finally {
try {
await source.close();
} catch (error) {
// Noop
}
}
})(),
};
} }
private async openImageSelector() { private async openImageSelector() {
@ -425,22 +450,22 @@ export class SourceSelector extends React.Component<
analytics.logEvent('Image selector closed'); analytics.logEvent('Image selector closed');
return; return;
} }
this.selectImageByPath({ await this.selectImageByPath({
imagePath, imagePath,
SourceType: sourceDestination.File, SourceType: sourceDestination.File,
}); }).promise;
} catch (error) { } catch (error) {
exceptionReporter.report(error); exceptionReporter.report(error);
} }
} }
private onDrop(event: React.DragEvent<HTMLDivElement>) { private async onDrop(event: React.DragEvent<HTMLDivElement>) {
const [file] = event.dataTransfer.files; const [file] = event.dataTransfer.files;
if (file) { if (file) {
this.selectImageByPath({ await this.selectImageByPath({
imagePath: file.path, imagePath: file.path,
SourceType: sourceDestination.File, SourceType: sourceDestination.File,
}); }).promise;
} }
} }
@ -484,6 +509,9 @@ export class SourceSelector extends React.Component<
const imageName = selectionState.getImageName(); const imageName = selectionState.getImageName();
const imageSize = selectionState.getImageSize(); const imageSize = selectionState.getImageSize();
const imageLogo = selectionState.getImageLogo(); const imageLogo = selectionState.getImageLogo();
let cancelURLSelection = () => {
// noop
};
return ( return (
<> <>
@ -585,6 +613,7 @@ export class SourceSelector extends React.Component<
{showURLSelector && ( {showURLSelector && (
<URLSelector <URLSelector
cancel={() => { cancel={() => {
cancelURLSelection();
this.setState({ this.setState({
showURLSelector: false, showURLSelector: false,
}); });
@ -594,16 +623,17 @@ export class SourceSelector extends React.Component<
// if no file was resolved from the dialog. // if no file was resolved from the dialog.
if (!imageURL) { if (!imageURL) {
analytics.logEvent('URL selector closed'); analytics.logEvent('URL selector closed');
this.setState({ } else {
showURLSelector: false, let promise;
}); ({
return; promise,
cancel: cancelURLSelection,
} = this.selectImageByPath({
imagePath: imageURL,
SourceType: sourceDestination.Http,
}));
await promise;
} }
await this.selectImageByPath({
imagePath: imageURL,
SourceType: sourceDestination.Http,
});
this.setState({ this.setState({
showURLSelector: false, showURLSelector: false,
}); });

View File

@ -49,11 +49,6 @@ body {
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
/* Allow window to be dragged from header */
#app-header {
-webkit-app-region: drag;
}
/* Prevent blue outline */ /* Prevent blue outline */
a:focus, a:focus,
input:focus, input:focus,

View File

@ -146,7 +146,6 @@ interface FlashStepProps {
goToSuccess: () => void; goToSuccess: () => void;
source: SourceOptions; source: SourceOptions;
isFlashing: boolean; isFlashing: boolean;
isWebviewShowing: boolean;
style?: React.CSSProperties; style?: React.CSSProperties;
// TODO: factorize // TODO: factorize
step: 'decompressing' | 'flashing' | 'verifying'; step: 'decompressing' | 'flashing' | 'verifying';

View File

@ -24,7 +24,6 @@ import * as React from 'react';
import { Flex } from 'rendition'; import { Flex } from 'rendition';
import styled from 'styled-components'; import styled from 'styled-components';
import { FeaturedProject } from '../../components/featured-project/featured-project';
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 { SafeWebview } from '../../components/safe-webview/safe-webview';
@ -114,6 +113,7 @@ interface MainPageState {
isWebviewShowing: boolean; isWebviewShowing: boolean;
hideSettings: boolean; hideSettings: boolean;
source: SourceOptions; source: SourceOptions;
featuredProjectURL?: string;
} }
export class MainPage extends React.Component< export class MainPage extends React.Component<
@ -147,10 +147,21 @@ export class MainPage extends React.Component<
}; };
} }
public componentDidMount() { private async getFeaturedProjectURL() {
const url = new URL(
(await settings.get('featuredProjectEndpoint')) ||
'https://assets.balena.io/etcher-featured/index.html',
);
url.searchParams.append('borderRight', 'false');
url.searchParams.append('darkBackground', 'true');
return url.toString();
}
public async componentDidMount() {
observe(() => { observe(() => {
this.setState(this.stateHelper()); this.setState(this.stateHelper());
}); });
this.setState({ featuredProjectURL: await this.getFeaturedProjectURL() });
} }
private renderMain() { private renderMain() {
@ -163,41 +174,42 @@ export class MainPage extends React.Component<
return ( return (
<> <>
<Flex <Flex
id="app-header" justifyContent="space-between"
justifyContent="center" alignItems="center"
paddingTop="14px"
style={{ style={{
width: '100%', // Allow window to be dragged from header
height: '50px', // @ts-ignore
padding: '13px 14px', '-webkit-app-region': 'drag',
textAlign: 'center',
position: 'relative', position: 'relative',
zIndex: 1, zIndex: 1,
}} }}
> >
<EtcherSvg <Flex width="100%" />
width="123px" <Flex width="100%" alignItems="center" justifyContent="center">
height="22px" <EtcherSvg
style={{ width="123px"
cursor: 'pointer', height="22px"
}} style={{
onClick={() => cursor: 'pointer',
openExternal('https://www.balena.io/etcher?ref=etcher_footer') }}
} onClick={() =>
tabIndex={100} openExternal('https://www.balena.io/etcher?ref=etcher_footer')
/> }
tabIndex={100}
/>
</Flex>
<Flex <Flex width="100%" alignItems="center" justifyContent="flex-end">
style={{
float: 'right',
position: 'absolute',
right: 0,
}}
>
<Icon <Icon
icon={<CogSvg height="1em" fill="currentColor" />} icon={<CogSvg height="1em" fill="currentColor" />}
plain plain
tabIndex={5} tabIndex={5}
onClick={() => this.setState({ hideSettings: false })} onClick={() => this.setState({ hideSettings: false })}
style={{
// Make touch events click instead of dragging
'-webkit-app-region': 'no-drag',
}}
/> />
{!settings.getSync('disableExternalLinks') && ( {!settings.getSync('disableExternalLinks') && (
<Icon <Icon
@ -209,6 +221,10 @@ export class MainPage extends React.Component<
) )
} }
tabIndex={6} tabIndex={6}
style={{
// Make touch events click instead of dragging
'-webkit-app-region': 'no-drag',
}}
/> />
)} )}
</Flex> </Flex>
@ -226,80 +242,72 @@ export class MainPage extends React.Component<
justifyContent="space-between" justifyContent="space-between"
> >
{notFlashingOrSplitView && ( {notFlashingOrSplitView && (
<SourceSelector
flashing={this.state.isFlashing}
afterSelected={(source: SourceOptions) =>
this.setState({ source })
}
/>
)}
{notFlashingOrSplitView && (
<Flex>
<StepBorder disabled={shouldDriveStepBeDisabled} left />
</Flex>
)}
{notFlashingOrSplitView && (
<DriveSelector
disabled={shouldDriveStepBeDisabled}
hasDrive={this.state.hasDrive}
flashing={this.state.isFlashing}
/>
)}
{notFlashingOrSplitView && (
<Flex>
<StepBorder disabled={shouldFlashStepBeDisabled} right />
</Flex>
)}
{this.state.isFlashing && (
<> <>
<Flex <SourceSelector
style={{ flashing={this.state.isFlashing}
position: 'absolute', afterSelected={(source: SourceOptions) =>
top: 0, this.setState({ source })
left: 0, }
width: '36.2vw', />
height: '100vh', <Flex>
zIndex: 1, <StepBorder disabled={shouldDriveStepBeDisabled} left />
boxShadow: '0 2px 15px 0 rgba(0, 0, 0, 0.2)',
display: this.state.isWebviewShowing ? 'block' : 'none',
}}
>
<ReducedFlashingInfos
imageLogo={this.state.imageLogo}
imageName={this.state.imageName}
imageSize={
_.isNumber(this.state.imageSize)
? (bytesToClosestUnit(this.state.imageSize) as string)
: ''
}
driveTitle={this.state.driveTitle}
driveLabel={this.state.driveLabel}
style={{
position: 'absolute',
color: '#fff',
left: 35,
top: 72,
}}
/>
</Flex> </Flex>
<FeaturedProject <DriveSelector
shouldShow={this.state.isWebviewShowing} disabled={shouldDriveStepBeDisabled}
onWebviewShow={(isWebviewShowing: boolean) => { hasDrive={this.state.hasDrive}
this.setState({ isWebviewShowing }); flashing={this.state.isFlashing}
}} />
<Flex>
<StepBorder disabled={shouldFlashStepBeDisabled} right />
</Flex>
</>
)}
{this.state.isFlashing && this.state.isWebviewShowing && (
<Flex
style={{
position: 'absolute',
top: 0,
left: 0,
width: '36.2vw',
height: '100vh',
zIndex: 1,
boxShadow: '0 2px 15px 0 rgba(0, 0, 0, 0.2)',
}}
>
<ReducedFlashingInfos
imageLogo={this.state.imageLogo}
imageName={this.state.imageName}
imageSize={
_.isNumber(this.state.imageSize)
? (bytesToClosestUnit(this.state.imageSize) as string)
: ''
}
driveTitle={this.state.driveTitle}
driveLabel={this.state.driveLabel}
style={{ style={{
position: 'absolute', position: 'absolute',
right: 0, color: '#fff',
bottom: 0, left: 35,
width: '63.8vw', top: 72,
height: '100vh',
}} }}
/> />
</> </Flex>
)}
{this.state.isFlashing && this.state.featuredProjectURL && (
<SafeWebview
src={this.state.featuredProjectURL}
onWebviewShow={(isWebviewShowing: boolean) => {
this.setState({ isWebviewShowing });
}}
style={{
position: 'absolute',
right: 0,
bottom: 0,
width: '63.8vw',
height: '100vh',
}}
/>
)} )}
<FlashStep <FlashStep
@ -307,7 +315,6 @@ export class MainPage extends React.Component<
shouldFlashStepBeDisabled={shouldFlashStepBeDisabled} shouldFlashStepBeDisabled={shouldFlashStepBeDisabled}
source={this.state.source} source={this.state.source}
isFlashing={this.state.isFlashing} isFlashing={this.state.isFlashing}
isWebviewShowing={this.state.isWebviewShowing}
step={state.type} step={state.type}
percentage={state.percentage} percentage={state.percentage}
position={state.position} position={state.position}

44
npm-shrinkwrap.json generated
View File

@ -2174,9 +2174,9 @@
} }
}, },
"@types/react-native": { "@types/react-native": {
"version": "0.63.8", "version": "0.63.9",
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.63.8.tgz", "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.63.9.tgz",
"integrity": "sha512-QRwGFRTyGafRVTUS+0GYyJrlpmS3boyBaFI0ULSc+mh/lQNxrzbdQvoL2k5X7+t9hxyqA4dTQAlP6l0ir/fNJQ==", "integrity": "sha512-6ec/z9zjAkFH3rD1RYqbrA/Lj+jux6bumWCte4yRy3leyelTdqtmOd2Ph+86IXQQzsIArEMBwmraAbNQ0J3UAA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/react": "*" "@types/react": "*"
@ -6677,9 +6677,9 @@
"dev": true "dev": true
}, },
"etcher-sdk": { "etcher-sdk": {
"version": "4.1.24", "version": "4.1.26",
"resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-4.1.24.tgz", "resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-4.1.26.tgz",
"integrity": "sha512-lRAKhXuXD7y68eA+bHJJn4MJpncbybeoH7FaIlXTcISuzdh9lr8NlguKnxvxkX35v1sWN7Ke6REloQY7oqqlvQ==", "integrity": "sha512-0Mb2EVoBn5dZdXer2sjINkvsccbfSYwm3Bgv0sYARyLazim+BljmsIFV9bNg8jmM8/h/Qxk+xLSJAyyOiSKyiA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@balena/udif": "^1.1.0", "@balena/udif": "^1.1.0",
@ -7800,9 +7800,9 @@
} }
}, },
"grommet": { "grommet": {
"version": "2.14.0", "version": "2.15.0",
"resolved": "https://registry.npmjs.org/grommet/-/grommet-2.14.0.tgz", "resolved": "https://registry.npmjs.org/grommet/-/grommet-2.15.0.tgz",
"integrity": "sha512-G/LTkr+uFri4NUNQGJMx8TtWWe9+KSpIHCXC9WgRaICI73R3+BvhLSNSzWMQ6YIQC+7PJFtruebeWjdUqR3Ykw==", "integrity": "sha512-5TVbiLrMpZOoB9oZAqWVttj6lO4rcKqBW1rWr4iovTuyyfYYOUQbuNfcFtUqp+MdB0fsQ1Vvci4PiTBvhRJqHA==",
"dev": true, "dev": true,
"requires": { "requires": {
"grommet-icons": "^4.2.0", "grommet-icons": "^4.2.0",
@ -12821,9 +12821,9 @@
"optional": true "optional": true
}, },
"rendition": { "rendition": {
"version": "18.1.0", "version": "18.4.1",
"resolved": "https://registry.npmjs.org/rendition/-/rendition-18.1.0.tgz", "resolved": "https://registry.npmjs.org/rendition/-/rendition-18.4.1.tgz",
"integrity": "sha512-B65e7VJadU+cxtXOn4eGBg8wql57I376NfUYWiu4zs4sf8l6/0h+P6FctoRdrZGX8RRRMToGLHnVC+3411Tmiw==", "integrity": "sha512-mV/0p+M8XR/Xa/ZFzgflZPHelpuONiTSa/CMMuHkmXR7vhF7tB2ORxLRc/DbymmdN6cWQwXAyA81t9TDAOhgVQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@fortawesome/fontawesome-svg-core": "^1.2.25", "@fortawesome/fontawesome-svg-core": "^1.2.25",
@ -15030,9 +15030,9 @@
"dev": true "dev": true
}, },
"uglify-js": { "uglify-js": {
"version": "3.10.1", "version": "3.10.2",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.1.tgz", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.2.tgz",
"integrity": "sha512-RjxApKkrPJB6kjJxQS3iZlf///REXWYxYJxO/MpmlQzVkDWVI3PSnCBWezMecmTU/TRkNxrl8bmsfFQCp+LO+Q==", "integrity": "sha512-GXCYNwqoo0MbLARghYjxVBxDCnU0tLqN7IPLdHHbibCb1NI5zBkU2EPcy/GaVxc0BtTjqyGXJCINe6JMR2Dpow==",
"dev": true "dev": true
}, },
"unbzip2-stream": { "unbzip2-stream": {
@ -15079,9 +15079,9 @@
"dev": true "dev": true
}, },
"unified": { "unified": {
"version": "9.1.0", "version": "9.2.0",
"resolved": "https://registry.npmjs.org/unified/-/unified-9.1.0.tgz", "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz",
"integrity": "sha512-VXOv7Ic6twsKGJDeZQ2wwPqXs2hM0KNu5Hkg9WgAZbSD1pxhZ7p8swqg583nw1Je2fhwHy6U8aEjiI79x1gvag==", "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==",
"dev": true, "dev": true,
"requires": { "requires": {
"bail": "^1.0.0", "bail": "^1.0.0",
@ -15543,9 +15543,9 @@
} }
}, },
"vfile-location": { "vfile-location": {
"version": "3.0.1", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.0.1.tgz", "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.1.0.tgz",
"integrity": "sha512-yYBO06eeN/Ki6Kh1QAkgzYpWT1d3Qln+ZCtSbJqFExPl1S3y2qqotJQXoh6qEvl/jDlgpUJolBn3PItVnnZRqQ==", "integrity": "sha512-FCZ4AN9xMcjFIG1oGmZKo61PjwJHRVA+0/tPUP2ul4uIwjGGndIxavEMRpWn5p4xwm/ZsdXp9YNygf1ZyE4x8g==",
"dev": true "dev": true
}, },
"vfile-message": { "vfile-message": {
@ -16769,4 +16769,4 @@
"dev": true "dev": true
} }
} }
} }

View File

@ -77,7 +77,7 @@
"electron-notarize": "^1.0.0", "electron-notarize": "^1.0.0",
"electron-rebuild": "^1.11.0", "electron-rebuild": "^1.11.0",
"electron-updater": "^4.3.2", "electron-updater": "^4.3.2",
"etcher-sdk": "^4.1.24", "etcher-sdk": "^4.1.26",
"file-loader": "^6.0.0", "file-loader": "^6.0.0",
"husky": "^4.2.5", "husky": "^4.2.5",
"immutable": "^3.8.1", "immutable": "^3.8.1",
@ -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.1.0", "rendition": "^18.4.1",
"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",