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 {
// The website source URL
src: string;
// @summary Refresh the webview
refreshNow?: boolean;
// Webview lifecycle event
onWebviewShow?: (isWebviewShowing: boolean) => void;
style?: React.CSSProperties;

View File

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

View File

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

View File

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

View File

@ -24,7 +24,6 @@ import * as React from 'react';
import { Flex } from 'rendition';
import styled from 'styled-components';
import { FeaturedProject } from '../../components/featured-project/featured-project';
import FinishPage from '../../components/finish/finish';
import { ReducedFlashingInfos } from '../../components/reduced-flashing-infos/reduced-flashing-infos';
import { SafeWebview } from '../../components/safe-webview/safe-webview';
@ -114,6 +113,7 @@ interface MainPageState {
isWebviewShowing: boolean;
hideSettings: boolean;
source: SourceOptions;
featuredProjectURL?: string;
}
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(() => {
this.setState(this.stateHelper());
});
this.setState({ featuredProjectURL: await this.getFeaturedProjectURL() });
}
private renderMain() {
@ -163,17 +174,19 @@ export class MainPage extends React.Component<
return (
<>
<Flex
id="app-header"
justifyContent="center"
justifyContent="space-between"
alignItems="center"
paddingTop="14px"
style={{
width: '100%',
height: '50px',
padding: '13px 14px',
textAlign: 'center',
// 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"
@ -185,19 +198,18 @@ export class MainPage extends React.Component<
}
tabIndex={100}
/>
</Flex>
<Flex
style={{
float: 'right',
position: 'absolute',
right: 0,
}}
>
<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
@ -209,6 +221,10 @@ export class MainPage extends React.Component<
)
}
tabIndex={6}
style={{
// Make touch events click instead of dragging
'-webkit-app-region': 'no-drag',
}}
/>
)}
</Flex>
@ -226,36 +242,28 @@ export class MainPage extends React.Component<
justifyContent="space-between"
>
{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 && (
<>
{this.state.isFlashing && this.state.isWebviewShowing && (
<Flex
style={{
position: 'absolute',
@ -265,7 +273,6 @@ export class MainPage extends React.Component<
height: '100vh',
zIndex: 1,
boxShadow: '0 2px 15px 0 rgba(0, 0, 0, 0.2)',
display: this.state.isWebviewShowing ? 'block' : 'none',
}}
>
<ReducedFlashingInfos
@ -286,8 +293,10 @@ export class MainPage extends React.Component<
}}
/>
</Flex>
<FeaturedProject
shouldShow={this.state.isWebviewShowing}
)}
{this.state.isFlashing && this.state.featuredProjectURL && (
<SafeWebview
src={this.state.featuredProjectURL}
onWebviewShow={(isWebviewShowing: boolean) => {
this.setState({ isWebviewShowing });
}}
@ -299,7 +308,6 @@ export class MainPage extends React.Component<
height: '100vh',
}}
/>
</>
)}
<FlashStep
@ -307,7 +315,6 @@ export class MainPage extends React.Component<
shouldFlashStepBeDisabled={shouldFlashStepBeDisabled}
source={this.state.source}
isFlashing={this.state.isFlashing}
isWebviewShowing={this.state.isWebviewShowing}
step={state.type}
percentage={state.percentage}
position={state.position}

42
npm-shrinkwrap.json generated
View File

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

View File

@ -77,7 +77,7 @@
"electron-notarize": "^1.0.0",
"electron-rebuild": "^1.11.0",
"electron-updater": "^4.3.2",
"etcher-sdk": "^4.1.24",
"etcher-sdk": "^4.1.26",
"file-loader": "^6.0.0",
"husky": "^4.2.5",
"immutable": "^3.8.1",
@ -94,7 +94,7 @@
"react": "^16.8.5",
"react-dom": "^16.8.5",
"redux": "^4.0.5",
"rendition": "^18.1.0",
"rendition": "^18.4.1",
"resin-corvus": "^2.0.5",
"semver": "^7.3.2",
"simple-progress-webpack-plugin": "^1.1.2",