mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-29 14:16:36 +00:00
Move a couple of files to typescript and remove unnecessary $timeout
Change-type: patch Signed-off-by: Stevche Radevski <stevche@balena.io>
This commit is contained in:
parent
4e1f071951
commit
388852d6b7
@ -150,6 +150,7 @@ const TargetSelector = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TargetSelector.propTypes = {
|
TargetSelector.propTypes = {
|
||||||
|
targets: propTypes.array,
|
||||||
disabled: propTypes.bool,
|
disabled: propTypes.bool,
|
||||||
openDriveSelector: propTypes.func,
|
openDriveSelector: propTypes.func,
|
||||||
selection: propTypes.object,
|
selection: propTypes.object,
|
||||||
|
@ -14,183 +14,148 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict'
|
import * as _ from 'lodash';
|
||||||
|
import * as propTypes from 'prop-types';
|
||||||
|
import * as React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import * as TargetSelector from '../../components/drive-selector/target-selector.jsx';
|
||||||
|
import * as SvgIcon from '../../components/svg-icon/svg-icon.jsx';
|
||||||
|
import * as selectionState from '../../models/selection-state';
|
||||||
|
import * as settings from '../../models/settings';
|
||||||
|
import * as store from '../../models/store';
|
||||||
|
import * as analytics from '../../modules/analytics';
|
||||||
|
import * as exceptionReporter from '../../modules/exception-reporter';
|
||||||
|
import * as driveConstraints from '../../../../shared/drive-constraints';
|
||||||
|
import * as utils from '../../../../shared/utils';
|
||||||
|
|
||||||
const _ = require('lodash')
|
const StepBorder = styled.div<{
|
||||||
const propTypes = require('prop-types')
|
disabled: boolean;
|
||||||
const React = require('react')
|
left?: boolean;
|
||||||
const driveConstraints = require('../../../../shared/drive-constraints')
|
right?: boolean;
|
||||||
const utils = require('../../../../shared/utils')
|
}>`
|
||||||
const TargetSelector = require('../../components/drive-selector/target-selector.jsx')
|
height: 2px;
|
||||||
const SvgIcon = require('../../components/svg-icon/svg-icon.jsx')
|
background-color: ${props =>
|
||||||
const selectionState = require('../../models/selection-state')
|
props.disabled
|
||||||
const settings = require('../../models/settings')
|
? props.theme.customColors.dark.disabled.foreground
|
||||||
const store = require('../../models/store')
|
: props.theme.customColors.dark.foreground};
|
||||||
const analytics = require('../../modules/analytics')
|
position: absolute;
|
||||||
const exceptionReporter = require('../../modules/exception-reporter')
|
width: 124px;
|
||||||
|
top: 19px;
|
||||||
|
|
||||||
|
left: ${props => (props.left ? '-67px' : undefined)};
|
||||||
|
right: ${props => (props.right ? '-67px' : undefined)};
|
||||||
|
`;
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Get drive list label
|
|
||||||
* @function
|
|
||||||
* @public
|
|
||||||
*
|
|
||||||
* @returns {String} - 'list' of drives separated by newlines
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* console.log(getDriveListLabel())
|
|
||||||
* > 'My Drive (/dev/disk1)\nMy Other Drive (/dev/disk2)'
|
|
||||||
*/
|
|
||||||
const getDriveListLabel = () => {
|
const getDriveListLabel = () => {
|
||||||
return _.join(_.map(selectionState.getSelectedDrives(), (drive) => {
|
return _.join(
|
||||||
return `${drive.description} (${drive.displayName})`
|
_.map(selectionState.getSelectedDrives(), (drive: any) => {
|
||||||
}), '\n')
|
return `${drive.description} (${drive.displayName})`;
|
||||||
}
|
}),
|
||||||
|
'\n',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
const openDriveSelector = async (DriveSelectorService: any) => {
|
||||||
* @summary Open drive selector
|
try {
|
||||||
* @function
|
const drive = await DriveSelectorService.open();
|
||||||
* @public
|
if (!drive) {
|
||||||
* @param {Object} DriveSelectorService - drive selector service
|
return;
|
||||||
*
|
}
|
||||||
* @example
|
|
||||||
* openDriveSelector(DriveSelectorService);
|
|
||||||
*/
|
|
||||||
const openDriveSelector = async (DriveSelectorService) => {
|
|
||||||
try {
|
|
||||||
const drive = await DriveSelectorService.open()
|
|
||||||
if (!drive) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
selectionState.selectDrive(drive.device)
|
selectionState.selectDrive(drive.device);
|
||||||
|
|
||||||
analytics.logEvent('Select drive', {
|
analytics.logEvent('Select drive', {
|
||||||
device: drive.device,
|
device: drive.device,
|
||||||
unsafeMode: settings.get('unsafeMode') && !settings.get('disableUnsafeMode'),
|
unsafeMode:
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
settings.get('unsafeMode') && !settings.get('disableUnsafeMode'),
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid
|
applicationSessionUuid: (store as any).getState().toJS().applicationSessionUuid,
|
||||||
})
|
flashingWorkflowUuid: (store as any).getState().toJS().flashingWorkflowUuid,
|
||||||
} catch (error) {
|
});
|
||||||
exceptionReporter.report(error)
|
} catch (error) {
|
||||||
}
|
exceptionReporter.report(error);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
const reselectDrive = (DriveSelectorService: any) => {
|
||||||
* @summary Reselect a drive
|
openDriveSelector(DriveSelectorService);
|
||||||
* @function
|
analytics.logEvent('Reselect drive', {
|
||||||
* @public
|
applicationSessionUuid: (store as any).getState().toJS().applicationSessionUuid,
|
||||||
* @param {Object} DriveSelectorService - drive selector service
|
flashingWorkflowUuid: (store as any).getState().toJS().flashingWorkflowUuid,
|
||||||
*
|
});
|
||||||
* @example
|
};
|
||||||
* reselectDrive(DriveSelectorService);
|
|
||||||
*/
|
|
||||||
const reselectDrive = (DriveSelectorService) => {
|
|
||||||
openDriveSelector(DriveSelectorService)
|
|
||||||
analytics.logEvent('Reselect drive', {
|
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
const getMemoizedSelectedDrives = utils.memoize(
|
||||||
* @summary Get memoized selected drives
|
selectionState.getSelectedDrives,
|
||||||
* @function
|
_.isEqual,
|
||||||
* @public
|
);
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* getMemoizedSelectedDrives()
|
|
||||||
*/
|
|
||||||
const getMemoizedSelectedDrives = utils.memoize(selectionState.getSelectedDrives, _.isEqual)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Should the drive selection button be shown
|
|
||||||
* @function
|
|
||||||
* @public
|
|
||||||
*
|
|
||||||
* @returns {Boolean}
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* shouldShowDrivesButton()
|
|
||||||
*/
|
|
||||||
const shouldShowDrivesButton = () => {
|
const shouldShowDrivesButton = () => {
|
||||||
return !settings.get('disableExplicitDriveSelection')
|
return !settings.get('disableExplicitDriveSelection');
|
||||||
}
|
};
|
||||||
|
|
||||||
const getDriveSelectionStateSlice = () => ({
|
const getDriveSelectionStateSlice = () => ({
|
||||||
showDrivesButton: shouldShowDrivesButton(),
|
showDrivesButton: shouldShowDrivesButton(),
|
||||||
driveListLabel: getDriveListLabel(),
|
driveListLabel: getDriveListLabel(),
|
||||||
targets: getMemoizedSelectedDrives()
|
targets: getMemoizedSelectedDrives(),
|
||||||
})
|
});
|
||||||
|
|
||||||
const DriveSelector = ({
|
export const DriveSelector = ({
|
||||||
webviewShowing,
|
webviewShowing,
|
||||||
disabled,
|
disabled,
|
||||||
nextStepDisabled,
|
nextStepDisabled,
|
||||||
hasDrive,
|
hasDrive,
|
||||||
flashing,
|
flashing,
|
||||||
DriveSelectorService
|
DriveSelectorService,
|
||||||
}) => {
|
}: any) => {
|
||||||
// TODO: inject these from redux-connector
|
// TODO: inject these from redux-connector
|
||||||
const [ {
|
const [
|
||||||
showDrivesButton,
|
{ showDrivesButton, driveListLabel, targets },
|
||||||
driveListLabel,
|
setStateSlice,
|
||||||
targets
|
] = React.useState(getDriveSelectionStateSlice());
|
||||||
}, setStateSlice ] = React.useState(getDriveSelectionStateSlice())
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
return store.observe(() => {
|
return (store as any).observe(() => {
|
||||||
setStateSlice(getDriveSelectionStateSlice())
|
setStateSlice(getDriveSelectionStateSlice());
|
||||||
})
|
});
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
const showStepConnectingLines = !webviewShowing || !flashing
|
const showStepConnectingLines = !webviewShowing || !flashing;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="box text-center relative">
|
<div className="box text-center relative">
|
||||||
|
{showStepConnectingLines && (
|
||||||
|
<React.Fragment>
|
||||||
|
<StepBorder disabled={disabled} left />
|
||||||
|
<StepBorder disabled={nextStepDisabled} right />
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
|
||||||
{showStepConnectingLines && (
|
<div className="center-block">
|
||||||
<React.Fragment>
|
<SvgIcon paths={['../../assets/drive.svg']} disabled={disabled} />
|
||||||
<div
|
</div>
|
||||||
className="step-border-left"
|
|
||||||
disabled={disabled}
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
className="step-border-right"
|
|
||||||
disabled={nextStepDisabled}
|
|
||||||
></div>
|
|
||||||
</React.Fragment>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="center-block">
|
<div className="space-vertical-large">
|
||||||
<SvgIcon
|
<TargetSelector
|
||||||
paths={[ '../../assets/drive.svg' ]}
|
disabled={disabled}
|
||||||
disabled={disabled}
|
show={!hasDrive && showDrivesButton}
|
||||||
/>
|
tooltip={driveListLabel}
|
||||||
</div>
|
selection={selectionState}
|
||||||
|
openDriveSelector={() => openDriveSelector(DriveSelectorService)}
|
||||||
<div className="space-vertical-large">
|
reselectDrive={() => reselectDrive(DriveSelectorService)}
|
||||||
<TargetSelector
|
flashing={flashing}
|
||||||
disabled={disabled}
|
constraints={driveConstraints}
|
||||||
show={!hasDrive && showDrivesButton}
|
targets={targets}
|
||||||
tooltip={driveListLabel}
|
/>
|
||||||
selection={selectionState}
|
</div>
|
||||||
openDriveSelector={() => openDriveSelector(DriveSelectorService)}
|
</div>
|
||||||
reselectDrive={() => reselectDrive(DriveSelectorService)}
|
);
|
||||||
flashing={flashing}
|
};
|
||||||
constraints={driveConstraints}
|
|
||||||
targets={targets}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
DriveSelector.propTypes = {
|
DriveSelector.propTypes = {
|
||||||
webviewShowing: propTypes.bool,
|
webviewShowing: propTypes.bool,
|
||||||
disabled: propTypes.bool,
|
disabled: propTypes.bool,
|
||||||
nextStepDisabled: propTypes.bool,
|
nextStepDisabled: propTypes.bool,
|
||||||
hasDrive: propTypes.bool,
|
hasDrive: propTypes.bool,
|
||||||
flashing: propTypes.bool
|
flashing: propTypes.bool,
|
||||||
}
|
DriveSelectorService: propTypes.object,
|
||||||
|
};
|
||||||
module.exports = DriveSelector
|
|
||||||
|
@ -14,282 +14,299 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict'
|
import * as _ from 'lodash';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Modal, Txt } from 'rendition';
|
||||||
|
import * as ProgressButton from '../../components/progress-button/progress-button.jsx';
|
||||||
|
import * as SvgIcon from '../../components/svg-icon/svg-icon.jsx';
|
||||||
|
import * as availableDrives from '../../models/available-drives';
|
||||||
|
import * as flashState from '../../models/flash-state';
|
||||||
|
import * as selection from '../../models/selection-state';
|
||||||
|
import * as store from '../../models/store';
|
||||||
|
import * as analytics from '../../modules/analytics';
|
||||||
|
import * as driveScanner from '../../modules/drive-scanner';
|
||||||
|
import * as imageWriter from '../../modules/image-writer';
|
||||||
|
import * as progressStatus from '../../modules/progress-status';
|
||||||
|
import * as notification from '../../os/notification';
|
||||||
|
import * as constraints from '../../../../shared/drive-constraints';
|
||||||
|
import * as messages from '../../../../shared/messages';
|
||||||
|
|
||||||
const React = require('react')
|
const COMPLETED_PERCENTAGE = 100;
|
||||||
const _ = require('lodash')
|
const SPEED_PRECISION = 2;
|
||||||
|
|
||||||
const { Modal, Txt } = require('rendition')
|
const getWarningMessages = (drives: any, image: any) => {
|
||||||
const messages = require('../../../../shared/messages')
|
const warningMessages = [];
|
||||||
const flashState = require('../../models/flash-state')
|
for (const drive of drives) {
|
||||||
const driveScanner = require('../../modules/drive-scanner')
|
if (constraints.isDriveSizeLarge(drive)) {
|
||||||
const progressStatus = require('../../modules/progress-status')
|
warningMessages.push(messages.warning.largeDriveSize(drive));
|
||||||
const notification = require('../../os/notification')
|
} else if (!constraints.isDriveSizeRecommended(drive, image)) {
|
||||||
const analytics = require('../../modules/analytics')
|
warningMessages.push(
|
||||||
const imageWriter = require('../../modules/image-writer')
|
messages.warning.unrecommendedDriveSize(image, drive),
|
||||||
const path = require('path')
|
);
|
||||||
const store = require('../../models/store')
|
}
|
||||||
const constraints = require('../../../../shared/drive-constraints')
|
|
||||||
const availableDrives = require('../../models/available-drives')
|
|
||||||
const selection = require('../../models/selection-state')
|
|
||||||
const SvgIcon = require('../../components/svg-icon/svg-icon.jsx')
|
|
||||||
const ProgressButton = require('../../components/progress-button/progress-button.jsx')
|
|
||||||
|
|
||||||
const COMPLETED_PERCENTAGE = 100
|
// TODO(Shou): we should consider adding the same warning dialog for system drives and remove unsafe mode
|
||||||
const SPEED_PRECISION = 2
|
}
|
||||||
|
|
||||||
const getWarningMessages = (drives, image) => {
|
return warningMessages;
|
||||||
const warningMessages = []
|
};
|
||||||
for (const drive of drives) {
|
|
||||||
if (constraints.isDriveSizeLarge(drive)) {
|
|
||||||
warningMessages.push(messages.warning.largeDriveSize(drive))
|
|
||||||
} else if (!constraints.isDriveSizeRecommended(drive, image)) {
|
|
||||||
warningMessages.push(messages.warning.unrecommendedDriveSize(image, drive))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(Shou): we should consider adding the same warning dialog for system drives and remove unsafe mode
|
const getErrorMessageFromCode = (errorCode: string) => {
|
||||||
}
|
// TODO: All these error codes to messages translations
|
||||||
|
// should go away if the writer emitted user friendly
|
||||||
|
// messages on the first place.
|
||||||
|
if (errorCode === 'EVALIDATION') {
|
||||||
|
return messages.error.validation();
|
||||||
|
} else if (errorCode === 'EUNPLUGGED') {
|
||||||
|
return messages.error.driveUnplugged();
|
||||||
|
} else if (errorCode === 'EIO') {
|
||||||
|
return messages.error.inputOutput();
|
||||||
|
} else if (errorCode === 'ENOSPC') {
|
||||||
|
return messages.error.notEnoughSpaceInDrive();
|
||||||
|
} else if (errorCode === 'ECHILDDIED') {
|
||||||
|
return messages.error.childWriterDied();
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
return warningMessages
|
const flashImageToDrive = async (goToSuccess: () => void) => {
|
||||||
}
|
const devices = selection.getSelectedDevices();
|
||||||
|
const image: any = selection.getImage();
|
||||||
|
const drives = _.filter(availableDrives.getDrives(), (drive: any) => {
|
||||||
|
return _.includes(devices, drive.device);
|
||||||
|
});
|
||||||
|
|
||||||
const getErrorMessageFromCode = (errorCode) => {
|
// eslint-disable-next-line no-magic-numbers
|
||||||
// TODO: All these error codes to messages translations
|
if (drives.length === 0 || flashState.isFlashing()) {
|
||||||
// should go away if the writer emitted user friendly
|
return '';
|
||||||
// messages on the first place.
|
}
|
||||||
if (errorCode === 'EVALIDATION') {
|
|
||||||
return messages.error.validation()
|
|
||||||
} else if (errorCode === 'EUNPLUGGED') {
|
|
||||||
return messages.error.driveUnplugged()
|
|
||||||
} else if (errorCode === 'EIO') {
|
|
||||||
return messages.error.inputOutput()
|
|
||||||
} else if (errorCode === 'ENOSPC') {
|
|
||||||
return messages.error.notEnoughSpaceInDrive()
|
|
||||||
} else if (errorCode === 'ECHILDDIED') {
|
|
||||||
return messages.error.childWriterDied()
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const flashImageToDrive = async ($timeout, $state) => {
|
// Stop scanning drives when flashing
|
||||||
const devices = selection.getSelectedDevices()
|
// otherwise Windows throws EPERM
|
||||||
const image = selection.getImage()
|
driveScanner.stop();
|
||||||
const drives = _.filter(availableDrives.getDrives(), (drive) => {
|
|
||||||
return _.includes(devices, drive.device)
|
|
||||||
})
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-magic-numbers
|
const iconPath = '../../assets/icon.png';
|
||||||
if (drives.length === 0 || flashState.isFlashing()) {
|
const basename = path.basename(image.path);
|
||||||
return ''
|
try {
|
||||||
}
|
await imageWriter.flash(image.path, drives);
|
||||||
|
if (!flashState.wasLastFlashCancelled()) {
|
||||||
|
const flashResults: any = flashState.getFlashResults();
|
||||||
|
notification.send('Flash complete!', {
|
||||||
|
body: messages.info.flashComplete(
|
||||||
|
basename,
|
||||||
|
drives as any,
|
||||||
|
flashResults.results.devices,
|
||||||
|
),
|
||||||
|
icon: iconPath,
|
||||||
|
});
|
||||||
|
goToSuccess();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// When flashing is cancelled before starting above there is no error
|
||||||
|
if (!error) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
// Trigger Angular digests along with store updates, as the flash state
|
notification.send('Oops! Looks like the flash failed.', {
|
||||||
// updates. The angular components won't update without it.
|
body: messages.error.flashFailure(path.basename(image.path), drives),
|
||||||
// TODO: Remove once moved entirely to React
|
icon: iconPath,
|
||||||
const unsubscribe = store.observe($timeout)
|
});
|
||||||
|
|
||||||
// Stop scanning drives when flashing
|
let errorMessage = getErrorMessageFromCode(error.code);
|
||||||
// otherwise Windows throws EPERM
|
if (!errorMessage) {
|
||||||
driveScanner.stop()
|
error.image = basename;
|
||||||
|
analytics.logException(error);
|
||||||
|
errorMessage = messages.error.genericFlashError();
|
||||||
|
}
|
||||||
|
|
||||||
const iconPath = '../../assets/icon.png'
|
return errorMessage;
|
||||||
const basename = path.basename(image.path)
|
} finally {
|
||||||
try {
|
availableDrives.setDrives([]);
|
||||||
await imageWriter.flash(image.path, drives)
|
driveScanner.start();
|
||||||
if (!flashState.wasLastFlashCancelled()) {
|
}
|
||||||
const flashResults = flashState.getFlashResults()
|
|
||||||
notification.send('Flash complete!', {
|
|
||||||
body: messages.info.flashComplete(basename, drives, flashResults.results.devices),
|
|
||||||
icon: iconPath
|
|
||||||
})
|
|
||||||
$state.go('success')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// When flashing is cancelled before starting above there is no error
|
|
||||||
if (!error) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
notification.send('Oops! Looks like the flash failed.', {
|
return '';
|
||||||
body: messages.error.flashFailure(path.basename(image.path), drives),
|
};
|
||||||
icon: iconPath
|
|
||||||
})
|
|
||||||
|
|
||||||
let errorMessage = getErrorMessageFromCode(error.code)
|
|
||||||
if (!errorMessage) {
|
|
||||||
error.image = basename
|
|
||||||
analytics.logException(error)
|
|
||||||
errorMessage = messages.error.genericFlashError()
|
|
||||||
}
|
|
||||||
|
|
||||||
return errorMessage
|
|
||||||
} finally {
|
|
||||||
availableDrives.setDrives([])
|
|
||||||
driveScanner.start()
|
|
||||||
unsubscribe()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Get progress button label
|
* @summary Get progress button label
|
||||||
* @function
|
* @function
|
||||||
* @public
|
* @public
|
||||||
*
|
*
|
||||||
* @returns {String} progress button label
|
* @returns {String} progress button label
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const label = FlashController.getProgressButtonLabel()
|
* const label = FlashController.getProgressButtonLabel()
|
||||||
*/
|
*/
|
||||||
const getProgressButtonLabel = () => {
|
const getProgressButtonLabel = () => {
|
||||||
if (!flashState.isFlashing()) {
|
if (!flashState.isFlashing()) {
|
||||||
return 'Flash!'
|
return 'Flash!';
|
||||||
}
|
}
|
||||||
|
|
||||||
return progressStatus.fromFlashState(flashState.getFlashState())
|
return progressStatus.fromFlashState(flashState.getFlashState());
|
||||||
}
|
};
|
||||||
|
|
||||||
const formatSeconds = (totalSeconds) => {
|
const formatSeconds = (totalSeconds: number) => {
|
||||||
if (!totalSeconds && !_.isNumber(totalSeconds)) {
|
if (!totalSeconds && !_.isNumber(totalSeconds)) {
|
||||||
return ''
|
return '';
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line no-magic-numbers
|
// eslint-disable-next-line no-magic-numbers
|
||||||
const minutes = Math.floor(totalSeconds / 60)
|
const minutes = Math.floor(totalSeconds / 60);
|
||||||
// eslint-disable-next-line no-magic-numbers
|
// eslint-disable-next-line no-magic-numbers
|
||||||
const seconds = Math.floor(totalSeconds - minutes * 60)
|
const seconds = Math.floor(totalSeconds - minutes * 60);
|
||||||
|
|
||||||
return `${minutes}m${seconds}s`
|
return `${minutes}m${seconds}s`;
|
||||||
}
|
};
|
||||||
|
|
||||||
const Flash = ({
|
export const Flash = ({
|
||||||
shouldFlashStepBeDisabled, lastFlashErrorCode, progressMessage,
|
shouldFlashStepBeDisabled,
|
||||||
$timeout, $state, DriveSelectorService
|
lastFlashErrorCode,
|
||||||
}) => {
|
progressMessage,
|
||||||
const state = flashState.getFlashState()
|
goToSuccess,
|
||||||
const isFlashing = flashState.isFlashing()
|
DriveSelectorService,
|
||||||
const flashErrorCode = lastFlashErrorCode()
|
}: any) => {
|
||||||
|
const state: any = flashState.getFlashState();
|
||||||
|
const isFlashing = flashState.isFlashing();
|
||||||
|
const flashErrorCode = lastFlashErrorCode();
|
||||||
|
|
||||||
const [ warningMessages, setWarningMessages ] = React.useState([])
|
const [warningMessages, setWarningMessages] = React.useState<string[]>([]);
|
||||||
const [ errorMessage, setErrorMessage ] = React.useState('')
|
const [errorMessage, setErrorMessage] = React.useState('');
|
||||||
|
|
||||||
const handleWarningResponse = async (shouldContinue) => {
|
const handleWarningResponse = async (shouldContinue: boolean) => {
|
||||||
setWarningMessages([])
|
setWarningMessages([]);
|
||||||
|
|
||||||
if (!shouldContinue) {
|
if (!shouldContinue) {
|
||||||
DriveSelectorService.open()
|
DriveSelectorService.open();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrorMessage(await flashImageToDrive($timeout, $state))
|
setErrorMessage(await flashImageToDrive(goToSuccess));
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleFlashErrorResponse = (shouldRetry) => {
|
const handleFlashErrorResponse = (shouldRetry: boolean) => {
|
||||||
setErrorMessage('')
|
setErrorMessage('');
|
||||||
flashState.resetState()
|
flashState.resetState();
|
||||||
if (shouldRetry) {
|
if (shouldRetry) {
|
||||||
analytics.logEvent('Restart after failure', {
|
analytics.logEvent('Restart after failure', {
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
applicationSessionUuid: (store as any).getState().toJS().applicationSessionUuid,
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid
|
flashingWorkflowUuid: (store as any).getState().toJS().flashingWorkflowUuid,
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
selection.clear()
|
selection.clear();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const tryFlash = async () => {
|
const tryFlash = async () => {
|
||||||
const devices = selection.getSelectedDevices()
|
const devices = selection.getSelectedDevices();
|
||||||
const image = selection.getImage()
|
const image = selection.getImage();
|
||||||
const drives = _.filter(availableDrives.getDrives(), (drive) => {
|
const drives = _.filter(availableDrives.getDrives(), (drive: any) => {
|
||||||
return _.includes(devices, drive.device)
|
return _.includes(devices, drive.device);
|
||||||
})
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line no-magic-numbers
|
// eslint-disable-next-line no-magic-numbers
|
||||||
if (drives.length === 0 || flashState.isFlashing()) {
|
if (drives.length === 0 || flashState.isFlashing()) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasDangerStatus = constraints.hasListDriveImageCompatibilityStatus(drives, image)
|
const hasDangerStatus = constraints.hasListDriveImageCompatibilityStatus(
|
||||||
if (hasDangerStatus) {
|
drives,
|
||||||
setWarningMessages(getWarningMessages(drives, image))
|
image,
|
||||||
return
|
);
|
||||||
}
|
if (hasDangerStatus) {
|
||||||
|
setWarningMessages(getWarningMessages(drives, image));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setErrorMessage(await flashImageToDrive($timeout, $state))
|
setErrorMessage(await flashImageToDrive(goToSuccess));
|
||||||
}
|
};
|
||||||
|
|
||||||
return <React.Fragment>
|
return (
|
||||||
<div className="box text-center">
|
<React.Fragment>
|
||||||
<div className="center-block">
|
<div className="box text-center">
|
||||||
<SvgIcon paths={[ '../../assets/flash.svg' ]} disabled={shouldFlashStepBeDisabled}/>
|
<div className="center-block">
|
||||||
</div>
|
<SvgIcon
|
||||||
|
paths={['../../assets/flash.svg']}
|
||||||
|
disabled={shouldFlashStepBeDisabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-vertical-large">
|
<div className="space-vertical-large">
|
||||||
<ProgressButton
|
<ProgressButton
|
||||||
tabindex="3"
|
tabindex="3"
|
||||||
striped={state.type === 'verifying'}
|
striped={state.type === 'verifying'}
|
||||||
active={isFlashing}
|
active={isFlashing}
|
||||||
percentage={state.percentage}
|
percentage={state.percentage}
|
||||||
label={getProgressButtonLabel()}
|
label={getProgressButtonLabel()}
|
||||||
disabled={Boolean(flashErrorCode) || shouldFlashStepBeDisabled}
|
disabled={Boolean(flashErrorCode) || shouldFlashStepBeDisabled}
|
||||||
callback={tryFlash}>
|
callback={tryFlash}
|
||||||
</ProgressButton>
|
></ProgressButton>
|
||||||
|
|
||||||
{
|
{isFlashing && (
|
||||||
isFlashing && <button className="button button-link button-abort-write" onClick={imageWriter.cancel}>
|
<button
|
||||||
<span className="glyphicon glyphicon-remove-sign"></span>
|
className="button button-link button-abort-write"
|
||||||
</button>
|
onClick={imageWriter.cancel}
|
||||||
}
|
>
|
||||||
{
|
<span className="glyphicon glyphicon-remove-sign"></span>
|
||||||
!_.isNil(state.speed) && state.percentage !== COMPLETED_PERCENTAGE &&
|
</button>
|
||||||
<p className="step-footer step-footer-split">
|
)}
|
||||||
{Boolean(state.speed) && <span >{`${state.speed.toFixed(SPEED_PRECISION)} MB/s`}</span>}
|
{!_.isNil(state.speed) && state.percentage !== COMPLETED_PERCENTAGE && (
|
||||||
{!_.isNil(state.eta) && <span>{`ETA: ${formatSeconds(state.eta)}` }</span>}
|
<p className="step-footer step-footer-split">
|
||||||
</p>
|
{Boolean(state.speed) && (
|
||||||
}
|
<span>{`${state.speed.toFixed(SPEED_PRECISION)} MB/s`}</span>
|
||||||
|
)}
|
||||||
|
{!_.isNil(state.eta) && (
|
||||||
|
<span>{`ETA: ${formatSeconds(state.eta)}`}</span>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
{
|
{Boolean(state.failed) && (
|
||||||
Boolean(state.failed) && <div className="target-status-wrap">
|
<div className="target-status-wrap">
|
||||||
<div className="target-status-line target-status-failed">
|
<div className="target-status-line target-status-failed">
|
||||||
<span className="target-status-dot"></span>
|
<span className="target-status-dot"></span>
|
||||||
<span className="target-status-quantity">{state.failed}</span>
|
<span className="target-status-quantity">{state.failed}</span>
|
||||||
<span className="target-status-message">{progressMessage.failed(state.failed)} </span>
|
<span className="target-status-message">
|
||||||
</div>
|
{progressMessage.failed(state.failed)}{' '}
|
||||||
</div>
|
</span>
|
||||||
}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* eslint-disable-next-line no-magic-numbers */}
|
{/* eslint-disable-next-line no-magic-numbers */}
|
||||||
{warningMessages && warningMessages.length > 0 && <Modal
|
{warningMessages && warningMessages.length > 0 && (
|
||||||
width={400}
|
<Modal
|
||||||
titleElement={'Attention'}
|
width={400}
|
||||||
cancel={() => handleWarningResponse(false)}
|
titleElement={'Attention'}
|
||||||
done={() => handleWarningResponse(true)}
|
cancel={() => handleWarningResponse(false)}
|
||||||
cancelButtonProps={{
|
done={() => handleWarningResponse(true)}
|
||||||
children: 'Change'
|
cancelButtonProps={{
|
||||||
}}
|
children: 'Change',
|
||||||
action={'Continue'}
|
}}
|
||||||
primaryButtonProps={{ primary: false, warning: true }}
|
action={'Continue'}
|
||||||
>
|
primaryButtonProps={{ primary: false, warning: true }}
|
||||||
{
|
>
|
||||||
_.map(warningMessages, (message) => <Txt whitespace="pre-line" mt={2}>{message}</Txt>)
|
{_.map(warningMessages, message => (
|
||||||
}
|
<Txt whitespace="pre-line" mt={2}>
|
||||||
</Modal>
|
{message}
|
||||||
}
|
</Txt>
|
||||||
|
))}
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
|
||||||
{errorMessage && <Modal
|
{errorMessage && (
|
||||||
width={400}
|
<Modal
|
||||||
titleElement={'Attention'}
|
width={400}
|
||||||
cancel={() => handleFlashErrorResponse(false)}
|
titleElement={'Attention'}
|
||||||
done={() => handleFlashErrorResponse(true)}
|
cancel={() => handleFlashErrorResponse(false)}
|
||||||
action={'Retry'}
|
done={() => handleFlashErrorResponse(true)}
|
||||||
>
|
action={'Retry'}
|
||||||
<Txt>{errorMessage}</Txt>
|
>
|
||||||
</Modal>
|
<Txt>{errorMessage}</Txt>
|
||||||
}
|
</Modal>
|
||||||
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
);
|
||||||
|
};
|
||||||
module.exports = Flash
|
|
||||||
|
@ -28,8 +28,8 @@ import * as middleEllipsis from '../../utils/middle-ellipsis';
|
|||||||
import * as messages from '../../../../shared/messages';
|
import * as messages from '../../../../shared/messages';
|
||||||
import { bytesToClosestUnit } from '../../../../shared/units';
|
import { bytesToClosestUnit } from '../../../../shared/units';
|
||||||
|
|
||||||
import * as DriveSelector from './DriveSelector';
|
import { DriveSelector } from './DriveSelector';
|
||||||
import * as Flash from './Flash';
|
import { Flash } from './Flash';
|
||||||
|
|
||||||
const getDrivesTitle = (selection: any) => {
|
const getDrivesTitle = (selection: any) => {
|
||||||
const drives = selection.getSelectedDrives();
|
const drives = selection.getSelectedDrives();
|
||||||
@ -55,7 +55,7 @@ const getImageBasename = (selection: any) => {
|
|||||||
return selectionImageName || imageBasename;
|
return selectionImageName || imageBasename;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MainPage = ({ DriveSelectorService, $timeout, $state }: any) => {
|
const MainPage = ({ DriveSelectorService, $state }: any) => {
|
||||||
const setRefresh = React.useState(false)[1];
|
const setRefresh = React.useState(false)[1];
|
||||||
const [isWebviewShowing, setIsWebviewShowing] = React.useState(false);
|
const [isWebviewShowing, setIsWebviewShowing] = React.useState(false);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@ -127,8 +127,7 @@ const MainPage = ({ DriveSelectorService, $timeout, $state }: any) => {
|
|||||||
<div className="col-xs">
|
<div className="col-xs">
|
||||||
<Flash
|
<Flash
|
||||||
DriveSelectorService={DriveSelectorService}
|
DriveSelectorService={DriveSelectorService}
|
||||||
$timeout={$timeout}
|
goToSuccess={() => $state.go('success')}
|
||||||
$state={$state}
|
|
||||||
shouldFlashStepBeDisabled={shouldFlashStepBeDisabled}
|
shouldFlashStepBeDisabled={shouldFlashStepBeDisabled}
|
||||||
lastFlashErrorCode={lastFlashErrorCode}
|
lastFlashErrorCode={lastFlashErrorCode}
|
||||||
progressMessage={progressMessage}
|
progressMessage={progressMessage}
|
||||||
|
@ -49,7 +49,7 @@ const Main = angular.module(MODULE_NAME, [
|
|||||||
|
|
||||||
Main.component(
|
Main.component(
|
||||||
'mainPage',
|
'mainPage',
|
||||||
react2angular(MainPage, [], ['DriveSelectorService', '$timeout', '$state']),
|
react2angular(MainPage, [], ['DriveSelectorService', '$state']),
|
||||||
);
|
);
|
||||||
|
|
||||||
Main.config(($stateProvider: any) => {
|
Main.config(($stateProvider: any) => {
|
||||||
|
@ -64,28 +64,6 @@ img[disabled] {
|
|||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
%step-border {
|
|
||||||
height: 2px;
|
|
||||||
background-color: $palette-theme-dark-foreground;
|
|
||||||
position: absolute;
|
|
||||||
width: 124px;
|
|
||||||
top: 19px;
|
|
||||||
|
|
||||||
&[disabled] {
|
|
||||||
background-color: $palette-theme-dark-disabled-foreground;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-main .step-border-left {
|
|
||||||
@extend %step-border;
|
|
||||||
left: -67px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-main .step-border-right {
|
|
||||||
@extend %step-border;
|
|
||||||
right: -67px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-main .step-tooltip {
|
.page-main .step-tooltip {
|
||||||
display: block;
|
display: block;
|
||||||
margin: -5px auto -20px;
|
margin: -5px auto -20px;
|
||||||
|
@ -31,6 +31,8 @@ const {
|
|||||||
const { colors } = require('./theme')
|
const { colors } = require('./theme')
|
||||||
|
|
||||||
const theme = {
|
const theme = {
|
||||||
|
// TODO: Standardize how the colors are specified to match with rendition's format.
|
||||||
|
customColors: colors,
|
||||||
button: {
|
button: {
|
||||||
border: {
|
border: {
|
||||||
width: '0',
|
width: '0',
|
||||||
|
@ -6342,21 +6342,6 @@ img[disabled] {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 300; }
|
font-weight: 300; }
|
||||||
|
|
||||||
.page-main .step-border-left, .page-main .step-border-right {
|
|
||||||
height: 2px;
|
|
||||||
background-color: #fff;
|
|
||||||
position: absolute;
|
|
||||||
width: 124px;
|
|
||||||
top: 19px; }
|
|
||||||
.page-main .step-border-left[disabled], .page-main .step-border-right[disabled] {
|
|
||||||
background-color: #787c7f; }
|
|
||||||
|
|
||||||
.page-main .step-border-left {
|
|
||||||
left: -67px; }
|
|
||||||
|
|
||||||
.page-main .step-border-right {
|
|
||||||
right: -67px; }
|
|
||||||
|
|
||||||
.page-main .step-tooltip {
|
.page-main .step-tooltip {
|
||||||
display: block;
|
display: block;
|
||||||
margin: -5px auto -20px;
|
margin: -5px auto -20px;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user