mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-23 11:16:39 +00:00
Use rendition modal for warning and errors when flashing
Change-type: patch Signed-off-by: Stevche Radevski <stevche@balena.io>
This commit is contained in:
parent
00536cba3a
commit
21d9d31a27
@ -87,7 +87,6 @@ const app = angular.module('Etcher', [
|
|||||||
|
|
||||||
// Components
|
// Components
|
||||||
require('./components/svg-icon'),
|
require('./components/svg-icon'),
|
||||||
require('./components/warning-modal/warning-modal'),
|
|
||||||
require('./components/safe-webview'),
|
require('./components/safe-webview'),
|
||||||
require('./components/file-selector'),
|
require('./components/file-selector'),
|
||||||
|
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 resin.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module Etcher.Components.FlashErrorModal
|
|
||||||
*/
|
|
||||||
|
|
||||||
const angular = require('angular')
|
|
||||||
const MODULE_NAME = 'Etcher.Components.FlashErrorModal'
|
|
||||||
const FlashErrorModal = angular.module(MODULE_NAME, [
|
|
||||||
require('../warning-modal/warning-modal')
|
|
||||||
])
|
|
||||||
|
|
||||||
FlashErrorModal.service('FlashErrorModalService', require('./services/flash-error-modal'))
|
|
||||||
|
|
||||||
module.exports = MODULE_NAME
|
|
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 resin.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const flashState = require('../../../models/flash-state')
|
|
||||||
const selectionState = require('../../../models/selection-state')
|
|
||||||
const store = require('../../../models/store')
|
|
||||||
const analytics = require('../../../modules/analytics')
|
|
||||||
|
|
||||||
module.exports = function (WarningModalService) {
|
|
||||||
/**
|
|
||||||
* @summary Open the flash error modal
|
|
||||||
* @function
|
|
||||||
* @public
|
|
||||||
*
|
|
||||||
* @param {String} message - flash error message
|
|
||||||
* @returns {Promise}
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* FlashErrorModalService.show('The drive is not large enough!');
|
|
||||||
*/
|
|
||||||
this.show = (message) => {
|
|
||||||
return WarningModalService.display({
|
|
||||||
confirmationLabel: 'Retry',
|
|
||||||
description: message
|
|
||||||
}).then((confirmed) => {
|
|
||||||
flashState.resetState()
|
|
||||||
|
|
||||||
if (confirmed) {
|
|
||||||
analytics.logEvent('Restart after failure', {
|
|
||||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
selectionState.clear()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 resin.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module Etcher.Components.ProgressButton
|
|
||||||
*/
|
|
||||||
|
|
||||||
const angular = require('angular')
|
|
||||||
const { react2angular } = require('react2angular')
|
|
||||||
|
|
||||||
const MODULE_NAME = 'Etcher.Components.ProgressButton'
|
|
||||||
const ProgressButton = angular.module(MODULE_NAME, [])
|
|
||||||
|
|
||||||
ProgressButton.component(
|
|
||||||
'progressButton',
|
|
||||||
react2angular(require('./progress-button.jsx'))
|
|
||||||
)
|
|
||||||
|
|
||||||
module.exports = MODULE_NAME
|
|
@ -26,7 +26,7 @@ const {
|
|||||||
keyframes
|
keyframes
|
||||||
} = require('styled-components')
|
} = require('styled-components')
|
||||||
|
|
||||||
const { ProgressBar, Provider } = require('rendition')
|
const { ProgressBar } = require('rendition')
|
||||||
|
|
||||||
const { colors } = require('./../../theme')
|
const { colors } = require('./../../theme')
|
||||||
const { StepButton, StepSelection } = require('./../../styled-components')
|
const { StepButton, StepSelection } = require('./../../styled-components')
|
||||||
@ -105,7 +105,6 @@ class ProgressButton extends React.Component {
|
|||||||
if (this.props.active) {
|
if (this.props.active) {
|
||||||
if (this.props.striped) {
|
if (this.props.striped) {
|
||||||
return (
|
return (
|
||||||
<Provider>
|
|
||||||
<StepSelection>
|
<StepSelection>
|
||||||
<FlashProgressBarValidating
|
<FlashProgressBarValidating
|
||||||
primary
|
primary
|
||||||
@ -115,12 +114,10 @@ class ProgressButton extends React.Component {
|
|||||||
{ this.props.label }
|
{ this.props.label }
|
||||||
</FlashProgressBarValidating>
|
</FlashProgressBarValidating>
|
||||||
</StepSelection>
|
</StepSelection>
|
||||||
</Provider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider>
|
|
||||||
<StepSelection>
|
<StepSelection>
|
||||||
<FlashProgressBar
|
<FlashProgressBar
|
||||||
warning
|
warning
|
||||||
@ -130,12 +127,10 @@ class ProgressButton extends React.Component {
|
|||||||
{ this.props.label }
|
{ this.props.label }
|
||||||
</FlashProgressBar>
|
</FlashProgressBar>
|
||||||
</StepSelection>
|
</StepSelection>
|
||||||
</Provider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider>
|
|
||||||
<StepSelection>
|
<StepSelection>
|
||||||
<StepButton
|
<StepButton
|
||||||
onClick= { this.props.callback }
|
onClick= { this.props.callback }
|
||||||
@ -144,7 +139,6 @@ class ProgressButton extends React.Component {
|
|||||||
{this.props.label}
|
{this.props.label}
|
||||||
</StepButton>
|
</StepButton>
|
||||||
</StepSelection>
|
</StepSelection>
|
||||||
</Provider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 resin.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
module.exports = function ($uibModalInstance, options) {
|
|
||||||
/**
|
|
||||||
* @summary Modal options
|
|
||||||
* @type {Object}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
this.options = options
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Reject the warning prompt
|
|
||||||
* @function
|
|
||||||
* @public
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* WarningModalController.reject();
|
|
||||||
*/
|
|
||||||
this.reject = () => {
|
|
||||||
$uibModalInstance.close(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Accept the warning prompt
|
|
||||||
* @function
|
|
||||||
* @public
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* WarningModalController.accept();
|
|
||||||
*/
|
|
||||||
this.accept = () => {
|
|
||||||
$uibModalInstance.close(true)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 resin.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const _ = require('lodash')
|
|
||||||
|
|
||||||
module.exports = function ($sce, ModalService) {
|
|
||||||
/**
|
|
||||||
* @summary Display the warning modal
|
|
||||||
* @function
|
|
||||||
* @public
|
|
||||||
*
|
|
||||||
* @param {Object} options - options
|
|
||||||
* @param {String} options.description - danger message
|
|
||||||
* @param {String} options.confirmationLabel - confirmation button text
|
|
||||||
* @param {String} options.rejectionLabel - rejection button text
|
|
||||||
* @fulfil {Boolean} - whether the user accepted or rejected the warning
|
|
||||||
* @returns {Promise}
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* WarningModalService.display({
|
|
||||||
* description: 'Don\'t do this!',
|
|
||||||
* confirmationLabel: 'Yes, continue!'
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
this.display = (options = {}) => {
|
|
||||||
options.description = $sce.trustAsHtml(options.description)
|
|
||||||
return ModalService.open({
|
|
||||||
name: 'warning',
|
|
||||||
template: require('../templates/warning-modal.tpl.html'),
|
|
||||||
controller: 'WarningModalController as modal',
|
|
||||||
size: 'warning-modal',
|
|
||||||
resolve: {
|
|
||||||
options: _.constant(options)
|
|
||||||
}
|
|
||||||
}).result
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 resin.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.modal-warning-modal .modal-content {
|
|
||||||
width: 350px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-warning-modal .modal-title .glyphicon {
|
|
||||||
color: $palette-theme-danger-background;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-warning-modal .modal-body {
|
|
||||||
max-height: 200px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
<div class="modal-header">
|
|
||||||
<h4 class="modal-title">
|
|
||||||
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
|
||||||
<span>Attention</span>
|
|
||||||
</h4>
|
|
||||||
<button class="close"
|
|
||||||
tabindex="11"
|
|
||||||
ng-click="modal.reject()">×</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-body">
|
|
||||||
<p ng-bind-html="modal.options.description"></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
|
||||||
<div class="modal-menu">
|
|
||||||
<button class="button button-danger button-block"
|
|
||||||
tabindex="13"
|
|
||||||
ng-click="modal.accept()">{{ ::modal.options.confirmationLabel }}</button>
|
|
||||||
<button ng-if="modal.options.rejectionLabel" class="button button-block"
|
|
||||||
tabindex="12"
|
|
||||||
ng-click="modal.reject()">{{ ::modal.options.rejectionLabel }}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 resin.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module Etcher.Components.WarningModal
|
|
||||||
*/
|
|
||||||
|
|
||||||
const angular = require('angular')
|
|
||||||
const MODULE_NAME = 'Etcher.Components.WarningModal'
|
|
||||||
const WarningModal = angular.module(MODULE_NAME, [
|
|
||||||
require('../modal/modal')
|
|
||||||
])
|
|
||||||
|
|
||||||
WarningModal.controller('WarningModalController', require('./controllers/warning-modal'))
|
|
||||||
WarningModal.service('WarningModalService', require('./services/warning-modal'))
|
|
||||||
|
|
||||||
module.exports = MODULE_NAME
|
|
@ -18,6 +18,9 @@
|
|||||||
|
|
||||||
const React = require('react')
|
const React = require('react')
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
|
|
||||||
|
const { Modal, Txt } = require('rendition')
|
||||||
|
const { ThemedProvider } = require('../../styled-components')
|
||||||
const messages = require('../../../../shared/messages')
|
const messages = require('../../../../shared/messages')
|
||||||
const flashState = require('../../models/flash-state')
|
const flashState = require('../../models/flash-state')
|
||||||
const driveScanner = require('../../modules/drive-scanner')
|
const driveScanner = require('../../modules/drive-scanner')
|
||||||
@ -36,49 +39,7 @@ const ProgressButton = require('../../components/progress-button/progress-button
|
|||||||
const COMPLETED_PERCENTAGE = 100
|
const COMPLETED_PERCENTAGE = 100
|
||||||
const SPEED_PRECISION = 2
|
const SPEED_PRECISION = 2
|
||||||
|
|
||||||
/**
|
const getWarningMessages = (drives, image) => {
|
||||||
* @summary Spawn a confirmation warning modal
|
|
||||||
* @function
|
|
||||||
* @public
|
|
||||||
*
|
|
||||||
* @param {Array<String>} warningMessages - warning messages
|
|
||||||
* @param {Object} WarningModalService - warning modal service
|
|
||||||
* @returns {Promise} warning modal promise
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* confirmationWarningModal([ 'Hello, World!' ])
|
|
||||||
*/
|
|
||||||
const confirmationWarningModal = (warningMessages, WarningModalService) => {
|
|
||||||
return WarningModalService.display({
|
|
||||||
confirmationLabel: 'Continue',
|
|
||||||
rejectionLabel: 'Change',
|
|
||||||
description: [
|
|
||||||
warningMessages.join('\n\n'),
|
|
||||||
'Are you sure you want to continue?'
|
|
||||||
].join(' ')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Display warning tailored to the warning of the current drives-image pair
|
|
||||||
* @function
|
|
||||||
* @public
|
|
||||||
*
|
|
||||||
* @param {Array<Object>} drives - list of drive objects
|
|
||||||
* @param {Object} image - image object
|
|
||||||
* @param {Object} WarningModalService - warning modal service
|
|
||||||
* @returns {Promise<Boolean>}
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* displayTailoredWarning(drives, image).then((ok) => {
|
|
||||||
* if (ok) {
|
|
||||||
* console.log('No warning was shown or continue was pressed')
|
|
||||||
* } else {
|
|
||||||
* console.log('Change was pressed')
|
|
||||||
* }
|
|
||||||
* })
|
|
||||||
*/
|
|
||||||
const displayTailoredWarning = async (drives, image, WarningModalService) => {
|
|
||||||
const warningMessages = []
|
const warningMessages = []
|
||||||
for (const drive of drives) {
|
for (const drive of drives) {
|
||||||
if (constraints.isDriveSizeLarge(drive)) {
|
if (constraints.isDriveSizeLarge(drive)) {
|
||||||
@ -90,29 +51,28 @@ const displayTailoredWarning = async (drives, image, WarningModalService) => {
|
|||||||
// TODO(Shou): we should consider adding the same warning dialog for system drives and remove unsafe mode
|
// TODO(Shou): we should consider adding the same warning dialog for system drives and remove unsafe mode
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!warningMessages.length) {
|
return warningMessages
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return confirmationWarningModal(warningMessages, WarningModalService)
|
const getErrorMessageFromCode = (errorCode) => {
|
||||||
|
// 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 ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const flashImageToDrive = async ($timeout, $state) => {
|
||||||
* @summary Flash image to drives
|
|
||||||
* @function
|
|
||||||
* @public
|
|
||||||
*
|
|
||||||
* @param {Object} $timeout - angular's timeout object
|
|
||||||
* @param {Object} $state - angular's state object
|
|
||||||
* @param {Object} WarningModalService - warning modal service
|
|
||||||
* @param {Object} DriveSelectorService - drive selector service
|
|
||||||
* @param {Object} FlashErrorModalService - flash error modal service
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* flashImageToDrive($timeout, $state, WarningModalService, DriveSelectorService, FlashErrorModalService)
|
|
||||||
*/
|
|
||||||
const flashImageToDrive = async ($timeout, $state,
|
|
||||||
WarningModalService, DriveSelectorService, FlashErrorModalService) => {
|
|
||||||
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) => {
|
||||||
@ -120,20 +80,8 @@ const flashImageToDrive = async ($timeout, $state,
|
|||||||
})
|
})
|
||||||
|
|
||||||
// eslint-disable-next-line no-magic-numbers
|
// eslint-disable-next-line no-magic-numbers
|
||||||
if (drives.length === 0) {
|
if (drives.length === 0 || flashState.isFlashing()) {
|
||||||
return
|
return ''
|
||||||
}
|
|
||||||
|
|
||||||
const hasDangerStatus = constraints.hasListDriveImageCompatibilityStatus(drives, image)
|
|
||||||
if (hasDangerStatus) {
|
|
||||||
if (!(await displayTailoredWarning(drives, image, WarningModalService))) {
|
|
||||||
DriveSelectorService.open()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flashState.isFlashing()) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger Angular digests along with store updates, as the flash state
|
// Trigger Angular digests along with store updates, as the flash state
|
||||||
@ -160,7 +108,7 @@ const flashImageToDrive = async ($timeout, $state,
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// When flashing is cancelled before starting above there is no error
|
// When flashing is cancelled before starting above there is no error
|
||||||
if (!error) {
|
if (!error) {
|
||||||
return
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
notification.send('Oops! Looks like the flash failed.', {
|
notification.send('Oops! Looks like the flash failed.', {
|
||||||
@ -168,29 +116,21 @@ const flashImageToDrive = async ($timeout, $state,
|
|||||||
icon: iconPath
|
icon: iconPath
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: All these error codes to messages translations
|
let errorMessage = getErrorMessageFromCode(error.code)
|
||||||
// should go away if the writer emitted user friendly
|
if (!errorMessage) {
|
||||||
// messages on the first place.
|
|
||||||
if (error.code === 'EVALIDATION') {
|
|
||||||
FlashErrorModalService.show(messages.error.validation())
|
|
||||||
} else if (error.code === 'EUNPLUGGED') {
|
|
||||||
FlashErrorModalService.show(messages.error.driveUnplugged())
|
|
||||||
} else if (error.code === 'EIO') {
|
|
||||||
FlashErrorModalService.show(messages.error.inputOutput())
|
|
||||||
} else if (error.code === 'ENOSPC') {
|
|
||||||
FlashErrorModalService.show(messages.error.notEnoughSpaceInDrive())
|
|
||||||
} else if (error.code === 'ECHILDDIED') {
|
|
||||||
FlashErrorModalService.show(messages.error.childWriterDied())
|
|
||||||
} else {
|
|
||||||
FlashErrorModalService.show(messages.error.genericFlashError())
|
|
||||||
error.image = basename
|
error.image = basename
|
||||||
analytics.logException(error)
|
analytics.logException(error)
|
||||||
|
errorMessage = messages.error.genericFlashError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return errorMessage
|
||||||
} finally {
|
} finally {
|
||||||
availableDrives.setDrives([])
|
availableDrives.setDrives([])
|
||||||
driveScanner.start()
|
driveScanner.start()
|
||||||
unsubscribe()
|
unsubscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -225,7 +165,7 @@ const formatSeconds = (totalSeconds) => {
|
|||||||
|
|
||||||
const Flash = ({
|
const Flash = ({
|
||||||
shouldFlashStepBeDisabled, lastFlashErrorCode, progressMessage,
|
shouldFlashStepBeDisabled, lastFlashErrorCode, progressMessage,
|
||||||
$timeout, $state, WarningModalService, DriveSelectorService, FlashErrorModalService
|
$timeout, $state, DriveSelectorService
|
||||||
}) => {
|
}) => {
|
||||||
// This is a hack to re-render the component whenever the global state changes. Remove once we get rid of angular and use redux correctly.
|
// This is a hack to re-render the component whenever the global state changes. Remove once we get rid of angular and use redux correctly.
|
||||||
// eslint-disable-next-line no-magic-numbers
|
// eslint-disable-next-line no-magic-numbers
|
||||||
@ -235,13 +175,62 @@ const Flash = ({
|
|||||||
const isFlashStepDisabled = shouldFlashStepBeDisabled()
|
const isFlashStepDisabled = shouldFlashStepBeDisabled()
|
||||||
const flashErrorCode = lastFlashErrorCode()
|
const flashErrorCode = lastFlashErrorCode()
|
||||||
|
|
||||||
|
const [ warningMessages, setWarningMessages ] = React.useState([])
|
||||||
|
const [ errorMessage, setErrorMessage ] = React.useState('')
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
return store.observe(() => {
|
return store.observe(() => {
|
||||||
setRefresh((ref) => !ref)
|
setRefresh((ref) => !ref)
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return <div className="box text-center">
|
const handleWarningResponse = async (shouldContinue) => {
|
||||||
|
setWarningMessages([])
|
||||||
|
|
||||||
|
if (!shouldContinue) {
|
||||||
|
DriveSelectorService.open()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setErrorMessage(await flashImageToDrive($timeout, $state))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFlashErrorResponse = (shouldRetry) => {
|
||||||
|
setErrorMessage('')
|
||||||
|
flashState.resetState()
|
||||||
|
if (shouldRetry) {
|
||||||
|
analytics.logEvent('Restart after failure', {
|
||||||
|
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
||||||
|
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
selection.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tryFlash = async () => {
|
||||||
|
const devices = selection.getSelectedDevices()
|
||||||
|
const image = selection.getImage()
|
||||||
|
const drives = _.filter(availableDrives.getDrives(), (drive) => {
|
||||||
|
return _.includes(devices, drive.device)
|
||||||
|
})
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-magic-numbers
|
||||||
|
if (drives.length === 0 || flashState.isFlashing()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasDangerStatus = constraints.hasListDriveImageCompatibilityStatus(drives, image)
|
||||||
|
if (hasDangerStatus) {
|
||||||
|
setWarningMessages(getWarningMessages(drives, image))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setErrorMessage(await flashImageToDrive($timeout, $state))
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ThemedProvider>
|
||||||
|
<div className="box text-center">
|
||||||
<div className="center-block">
|
<div className="center-block">
|
||||||
<SvgIcon paths={[ '../../assets/flash.svg' ]} disabled={isFlashStepDisabled}/>
|
<SvgIcon paths={[ '../../assets/flash.svg' ]} disabled={isFlashStepDisabled}/>
|
||||||
</div>
|
</div>
|
||||||
@ -254,8 +243,7 @@ const Flash = ({
|
|||||||
percentage={state.percentage}
|
percentage={state.percentage}
|
||||||
label={getProgressButtonLabel()}
|
label={getProgressButtonLabel()}
|
||||||
disabled={Boolean(flashErrorCode) || isFlashStepDisabled}
|
disabled={Boolean(flashErrorCode) || isFlashStepDisabled}
|
||||||
callback={() =>
|
callback={tryFlash}>
|
||||||
flashImageToDrive($timeout, $state, WarningModalService, DriveSelectorService, FlashErrorModalService)}>
|
|
||||||
</ProgressButton>
|
</ProgressButton>
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -282,6 +270,37 @@ const Flash = ({
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* eslint-disable-next-line no-magic-numbers */}
|
||||||
|
{warningMessages && warningMessages.length > 0 && <Modal
|
||||||
|
width={400}
|
||||||
|
titleElement={'Attention'}
|
||||||
|
cancel={() => handleWarningResponse(false)}
|
||||||
|
done={() => handleWarningResponse(true)}
|
||||||
|
cancelButtonProps={{
|
||||||
|
children: 'Change'
|
||||||
|
}}
|
||||||
|
action={'Continue'}
|
||||||
|
primaryButtonProps={{ primary: false, warning: true }}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
_.map(warningMessages, (message) => <Txt whitespace="pre-line" mt={2}>{message}</Txt>)
|
||||||
|
}
|
||||||
|
</Modal>
|
||||||
|
}
|
||||||
|
|
||||||
|
{errorMessage && <Modal
|
||||||
|
width={400}
|
||||||
|
titleElement={'Attention'}
|
||||||
|
cancel={() => handleFlashErrorResponse(false)}
|
||||||
|
done={() => handleFlashErrorResponse(true)}
|
||||||
|
action={'Retry'}
|
||||||
|
>
|
||||||
|
<Txt>{errorMessage}</Txt>
|
||||||
|
</Modal>
|
||||||
|
}
|
||||||
|
|
||||||
|
</ThemedProvider>
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Flash
|
module.exports = Flash
|
||||||
|
@ -31,10 +31,7 @@ const MainPage = angular.module(MODULE_NAME, [
|
|||||||
|
|
||||||
require('../../components/drive-selector/drive-selector'),
|
require('../../components/drive-selector/drive-selector'),
|
||||||
require('../../components/tooltip-modal/tooltip-modal'),
|
require('../../components/tooltip-modal/tooltip-modal'),
|
||||||
require('../../components/flash-error-modal/flash-error-modal'),
|
|
||||||
require('../../components/progress-button'),
|
|
||||||
require('../../components/image-selector'),
|
require('../../components/image-selector'),
|
||||||
require('../../components/warning-modal/warning-modal'),
|
|
||||||
require('../../components/file-selector'),
|
require('../../components/file-selector'),
|
||||||
require('../../components/featured-project'),
|
require('../../components/featured-project'),
|
||||||
require('../../components/reduced-flashing-infos'),
|
require('../../components/reduced-flashing-infos'),
|
||||||
@ -61,7 +58,7 @@ MainPage.component('driveSelector', react2angular(require('./DriveSelector.jsx')
|
|||||||
))
|
))
|
||||||
MainPage.component('flash', react2angular(require('./Flash.jsx'),
|
MainPage.component('flash', react2angular(require('./Flash.jsx'),
|
||||||
[ 'shouldFlashStepBeDisabled', 'lastFlashErrorCode', 'progressMessage' ],
|
[ 'shouldFlashStepBeDisabled', 'lastFlashErrorCode', 'progressMessage' ],
|
||||||
[ '$timeout', '$state', 'WarningModalService', 'DriveSelectorService', 'FlashErrorModalService' ]))
|
[ '$timeout', '$state', 'DriveSelectorService' ]))
|
||||||
|
|
||||||
MainPage.config(($stateProvider) => {
|
MainPage.config(($stateProvider) => {
|
||||||
$stateProvider
|
$stateProvider
|
||||||
|
@ -35,7 +35,6 @@ $disabled-opacity: 0.2;
|
|||||||
@import "../components/drive-selector/styles/drive-selector";
|
@import "../components/drive-selector/styles/drive-selector";
|
||||||
@import "../components/svg-icon/styles/svg-icon";
|
@import "../components/svg-icon/styles/svg-icon";
|
||||||
@import "../components/tooltip-modal/styles/tooltip-modal";
|
@import "../components/tooltip-modal/styles/tooltip-modal";
|
||||||
@import "../components/warning-modal/styles/warning-modal";
|
|
||||||
@import "../components/file-selector/styles/file-selector";
|
@import "../components/file-selector/styles/file-selector";
|
||||||
@import "../pages/main/styles/main";
|
@import "../pages/main/styles/main";
|
||||||
@import "../pages/finish/styles/finish";
|
@import "../pages/finish/styles/finish";
|
||||||
|
27
npm-shrinkwrap.json
generated
27
npm-shrinkwrap.json
generated
@ -9171,11 +9171,6 @@
|
|||||||
"sinon-chai": "^2.8.0"
|
"sinon-chai": "^2.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"moment": {
|
|
||||||
"version": "2.24.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
|
||||||
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
|
|
||||||
},
|
|
||||||
"moment-mini": {
|
"moment-mini": {
|
||||||
"version": "2.22.1",
|
"version": "2.22.1",
|
||||||
"resolved": "https://registry.npmjs.org/moment-mini/-/moment-mini-2.22.1.tgz",
|
"resolved": "https://registry.npmjs.org/moment-mini/-/moment-mini-2.22.1.tgz",
|
||||||
@ -10781,28 +10776,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-dropzone": {
|
|
||||||
"version": "10.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-10.2.1.tgz",
|
|
||||||
"integrity": "sha512-Me5nOu8hK9/Xyg5easpdfJ6SajwUquqYR/2YTdMotsCUgJ1pHIIwNsv0n+qcIno0tWR2V2rVQtj2r/hXYs2TnQ==",
|
|
||||||
"requires": {
|
|
||||||
"attr-accept": "^2.0.0",
|
|
||||||
"file-selector": "^0.1.12",
|
|
||||||
"prop-types": "^15.7.2"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"prop-types": {
|
|
||||||
"version": "15.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
|
|
||||||
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
|
|
||||||
"requires": {
|
|
||||||
"loose-envify": "^1.4.0",
|
|
||||||
"object-assign": "^4.1.1",
|
|
||||||
"react-is": "^16.8.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"react-google-recaptcha": {
|
"react-google-recaptcha": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-2.0.1.tgz",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user