mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-29 14:16:36 +00:00
commit
12cd8a39c1
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
|
@ -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,
|
||||||
});
|
});
|
||||||
|
@ -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,
|
||||||
|
@ -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';
|
||||||
|
@ -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
44
npm-shrinkwrap.json
generated
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user