mirror of
https://github.com/balena-io/etcher.git
synced 2025-04-24 07:17:18 +00:00
feat(GUI): move drive selector warning to flash step (#1917)
We move the drive selector warning to the flash step, and concatenate warning messages when more than one needs to be displayed at once. Change-Type: patch Changelog-Entry: Move the drive selector warning dialog to the flash step.
This commit is contained in:
parent
9601daedb7
commit
c17247da58
@ -18,7 +18,6 @@
|
||||
|
||||
const angular = require('angular')
|
||||
const _ = require('lodash')
|
||||
const messages = require('../../../../../shared/messages')
|
||||
const constraints = require('../../../../../shared/drive-constraints')
|
||||
const analytics = require('../../../modules/analytics')
|
||||
const availableDrives = require('../../../../../shared/models/available-drives')
|
||||
@ -27,8 +26,7 @@ const utils = require('../../../../../shared/utils')
|
||||
|
||||
module.exports = function (
|
||||
$q,
|
||||
$uibModalInstance,
|
||||
WarningModalService
|
||||
$uibModalInstance
|
||||
) {
|
||||
/**
|
||||
* @summary The drive selector state
|
||||
@ -72,28 +70,7 @@ module.exports = function (
|
||||
* });
|
||||
*/
|
||||
const shouldChangeDriveSelectionState = (drive) => {
|
||||
if (!constraints.isDriveValid(drive, selectionState.getImage())) {
|
||||
return $q.resolve(false)
|
||||
}
|
||||
|
||||
if (!constraints.isDriveSizeRecommended(drive, selectionState.getImage())) {
|
||||
return WarningModalService.display({
|
||||
confirmationLabel: 'Yes, continue',
|
||||
description: [
|
||||
messages.warning.unrecommendedDriveSize(selectionState.getImage(), drive),
|
||||
'Are you sure you want to continue?'
|
||||
].join(' ')
|
||||
})
|
||||
}
|
||||
|
||||
if (constraints.isDriveSizeLarge(drive)) {
|
||||
return WarningModalService.display({
|
||||
confirmationLabel: 'Yes, continue',
|
||||
description: messages.warning.largeDriveSize(drive)
|
||||
})
|
||||
}
|
||||
|
||||
return $q.resolve(true)
|
||||
return $q.resolve(constraints.isDriveValid(drive, selectionState.getImage()))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,7 +24,6 @@ const angular = require('angular')
|
||||
const MODULE_NAME = 'Etcher.Components.DriveSelector'
|
||||
const DriveSelector = angular.module(MODULE_NAME, [
|
||||
require('../modal/modal'),
|
||||
require('../warning-modal/warning-modal'),
|
||||
require('../../utils/byte-size/byte-size')
|
||||
])
|
||||
|
||||
|
@ -21,3 +21,8 @@
|
||||
.modal-warning-modal .modal-title .glyphicon {
|
||||
color: $palette-theme-danger-background;
|
||||
}
|
||||
|
||||
.modal-warning-modal .modal-body {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
'use strict'
|
||||
|
||||
const _ = require('lodash')
|
||||
const messages = require('../../../../../shared/messages')
|
||||
const flashState = require('../../../../../shared/models/flash-state')
|
||||
const driveScanner = require('../../../modules/drive-scanner')
|
||||
@ -25,19 +26,88 @@ const exceptionReporter = require('../../../modules/exception-reporter')
|
||||
const imageWriter = require('../../../modules/image-writer')
|
||||
const path = require('path')
|
||||
const store = require('../../../../../shared/store')
|
||||
const constraints = require('../../../../../shared/drive-constraints')
|
||||
const availableDrives = require('../../../../../shared/models/available-drives')
|
||||
|
||||
module.exports = function (
|
||||
$q,
|
||||
$state,
|
||||
$timeout,
|
||||
FlashErrorModalService
|
||||
FlashErrorModalService,
|
||||
WarningModalService,
|
||||
DriveSelectorService
|
||||
) {
|
||||
/**
|
||||
* @summary Spawn a confirmation warning modal
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {Array<String>} warningMessages - warning messages
|
||||
* @returns {Promise} warning modal promise
|
||||
*
|
||||
* @example
|
||||
* confirmationWarningModal([ 'Hello, World!' ])
|
||||
*/
|
||||
const confirmationWarningModal = (warningMessages) => {
|
||||
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
|
||||
* @returns {Promise}
|
||||
*
|
||||
* @example
|
||||
* displayTailoredWarning(drives, image).then(() => {
|
||||
* console.log('Continue pressed')
|
||||
* }).catch(() => {
|
||||
* console.log('Change pressed')
|
||||
* })
|
||||
*/
|
||||
const displayTailoredWarning = (drives, image) => {
|
||||
const warningMessages = _.reduce(drives, (accumMessages, drive) => {
|
||||
if (constraints.isDriveSizeLarge(drive)) {
|
||||
return accumMessages.concat(messages.warning.largeDriveSize(drive))
|
||||
} else if (!constraints.isDriveSizeRecommended(drive, image)) {
|
||||
return accumMessages.concat(messages.warning.unrecommendedDriveSize(image, drive))
|
||||
}
|
||||
|
||||
return accumMessages
|
||||
}, [])
|
||||
|
||||
if (!warningMessages.length) {
|
||||
// TODO(Shou): we should consider adding the same warning dialog for system drives and remove unsafe mode
|
||||
return $q.resolve()
|
||||
}
|
||||
|
||||
return confirmationWarningModal(warningMessages).then((value) => {
|
||||
if (!value) {
|
||||
DriveSelectorService.open()
|
||||
return $q.reject()
|
||||
}
|
||||
|
||||
return $q.resolve()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Flash image to drives
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {Object} image - image
|
||||
* @param {Array<Object>} drives - drives
|
||||
* @param {Array<String>} devices - list of drive device paths
|
||||
*
|
||||
* @example
|
||||
* FlashController.flashImageToDrive({
|
||||
@ -49,22 +119,23 @@ module.exports = function (
|
||||
* value: 1000000000
|
||||
* }
|
||||
* }
|
||||
* }, {
|
||||
* device: '/dev/disk2',
|
||||
* description: 'Foo',
|
||||
* size: 99999,
|
||||
* mountpoint: '/mnt/foo',
|
||||
* system: false
|
||||
* })
|
||||
* }, [
|
||||
* '/dev/disk2',
|
||||
* '/dev/disk5'
|
||||
* ])
|
||||
*/
|
||||
this.flashImageToDrive = (image, drives) => {
|
||||
this.flashImageToDrive = (image, devices) => {
|
||||
if (flashState.isFlashing()) {
|
||||
return
|
||||
}
|
||||
|
||||
// Stop scanning drives when flashing
|
||||
// otherwise Windows throws EPERM
|
||||
driveScanner.stop()
|
||||
const drives = _.filter(availableDrives.getDrives(), (drive) => {
|
||||
return _.includes(devices, drive.device)
|
||||
})
|
||||
|
||||
const hasDangerStatus = constraints.hasListDriveImageCompatibilityStatus(drives, image)
|
||||
|
||||
const userConfirm = hasDangerStatus ? _.partial(displayTailoredWarning, drives, image) : $q.resolve
|
||||
|
||||
// Trigger Angular digests along with store updates, as the flash state
|
||||
// updates. Without this there is essentially no progress to watch.
|
||||
@ -72,7 +143,13 @@ module.exports = function (
|
||||
|
||||
const iconPath = '../../../assets/icon.png'
|
||||
|
||||
imageWriter.flash(image.path, drives).then(() => {
|
||||
userConfirm().then(() => {
|
||||
// Stop scanning drives when flashing
|
||||
// otherwise Windows throws EPERM
|
||||
driveScanner.stop()
|
||||
|
||||
return imageWriter.flash(image.path, devices)
|
||||
}).then(() => {
|
||||
if (!flashState.wasLastFlashCancelled()) {
|
||||
notification.send('Success!', {
|
||||
body: messages.info.flashComplete(path.basename(image.path), drives),
|
||||
@ -81,6 +158,11 @@ module.exports = function (
|
||||
$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.', {
|
||||
body: messages.error.flashFailure(path.basename(image.path), drives),
|
||||
icon: iconPath
|
||||
|
@ -37,6 +37,7 @@ const MainPage = angular.module(MODULE_NAME, [
|
||||
require('../../components/tooltip-modal/tooltip-modal'),
|
||||
require('../../components/flash-error-modal/flash-error-modal'),
|
||||
require('../../components/progress-button/progress-button'),
|
||||
require('../../components/warning-modal/warning-modal'),
|
||||
|
||||
require('../../os/open-external/open-external'),
|
||||
require('../../os/dropzone/dropzone'),
|
||||
|
@ -6483,6 +6483,10 @@ svg-icon {
|
||||
.modal-warning-modal .modal-title .glyphicon, .modal-warning-modal .modal-title .tick {
|
||||
color: #d9534f; }
|
||||
|
||||
.modal-warning-modal .modal-body {
|
||||
max-height: 200px;
|
||||
overflow-y: auto; }
|
||||
|
||||
/*
|
||||
* Copyright 2016 resin.io
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user