feat(GUI): warn the user on large drive selection (#2045)

We warn the user when they select a large drive to confirm they want to
flash in case the device is important.

Fixes: https://github.com/resin-io/etcher/issues/1916
Change-Type: patch
Changelog-Entry: Warn the user on selection of large drives.
This commit is contained in:
Benedict Aas 2018-02-19 19:12:48 +00:00 committed by GitHub
parent b1b82301d9
commit 91719435d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 141 additions and 10 deletions

View File

@ -75,17 +75,24 @@ module.exports = function (
return $q.resolve(false)
}
if (constraints.isDriveSizeRecommended(drive, selectionState.getImage())) {
return $q.resolve(true)
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(' ')
})
}
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)
}
/**

View File

@ -276,6 +276,29 @@ exports.isDriveSizeRecommended = (drive, image) => {
return _.get(drive, [ 'size' ], UNKNOWN_SIZE) >= _.get(image, [ 'recommendedDriveSize' ], UNKNOWN_SIZE)
}
/**
* @summary 64GB
* @private
* @constant
*/
exports.LARGE_DRIVE_SIZE = 64e9
/**
* @summary Check whether a drive's size is 'large'
* @public
*
* @param {Object} drive - drive
* @returns {Boolean} whether drive size is large
*
* @example
* if (constraints.isDriveSizeLarge(drive)) {
* console.log('Impressive')
* }
*/
exports.isDriveSizeLarge = (drive) => {
return _.get(drive, [ 'size' ], UNKNOWN_SIZE) > exports.LARGE_DRIVE_SIZE
}
/**
* @summary Drive/image compatibility status messages.
* @public
@ -330,7 +353,16 @@ exports.COMPATIBILITY_STATUS_MESSAGES = {
* @description
* The drive contains the image and therefore cannot be written to.
*/
CONTAINS_IMAGE: 'Drive Contains Image'
CONTAINS_IMAGE: 'Drive Contains Image',
/**
* @property {String} LARGE_DRIVE
* @memberof COMPATIBILITY_STATUS_MESSAGES
*
* @description
* The drive is large and therefore likely not a medium you want to write to.
*/
LARGE_DRIVE: 'Large Drive'
}
/**
@ -416,6 +448,13 @@ exports.getDriveImageCompatibilityStatuses = (drive, image) => {
})
}
if (exports.isDriveSizeLarge(drive)) {
statusList.push({
type: exports.COMPATIBILITY_STATUS_TYPES.WARNING,
message: exports.COMPATIBILITY_STATUS_MESSAGES.LARGE_DRIVE
})
}
if (!_.isNil(drive) && !exports.isDriveSizeRecommended(drive, image)) {
statusList.push({
type: exports.COMPATIBILITY_STATUS_TYPES.WARNING,

View File

@ -78,6 +78,13 @@ module.exports = {
'The image does not appear to contain a partition table,',
'and might not be recognized or bootable by your device.'
].join(' ')
},
largeDriveSize: (drive) => {
return [
`Drive ${drive.description} (${drive.device}) is unusually large for an SD card or USB stick.`,
'\n\nAre you sure you want to flash this drive?'
].join(' ')
}
},

View File

@ -144,6 +144,9 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
constraints.isDriveValid(drive, image),
constraints.isDriveSizeRecommended(drive, image),
// We don't want to auto-select large drives
!constraints.isDriveSizeLarge(drive),
// We don't want to auto-select system drives,
// even when "unsafe mode" is enabled
!constraints.isSystemDrive(drive)

View File

@ -946,6 +946,48 @@ describe('Shared: DriveConstraints', function () {
})
})
describe('.isDriveSizeLarge()', function () {
beforeEach(function () {
this.drive = {
device: '/dev/disk2',
name: 'My Drive',
isReadonly: false,
isSystem: false,
disabled: false,
mountpoints: [
{
path: this.mountpoint
}
],
size: constraints.LARGE_DRIVE_SIZE + 1
}
this.image = {
path: path.join(__dirname, 'rpi.img'),
size: {
original: this.drive.size - 1,
final: {
estimation: false,
value: this.drive.size - 1
}
}
}
})
describe('given a drive bigger than the unusually large drive size', function () {
it('should return true', function () {
m.chai.expect(constraints.isDriveSizeLarge(this.drive)).to.be.true
})
})
describe('given a drive smaller than the unusually large drive size', function () {
it('should return false', function () {
this.drive.size = constraints.LARGE_DRIVE_SIZE - 1
m.chai.expect(constraints.isDriveSizeLarge(this.drive)).to.be.false
})
})
})
describe('.getDriveImageCompatibilityStatuses', function () {
beforeEach(function () {
if (process.platform === 'win32') {
@ -1080,6 +1122,17 @@ describe('Shared: DriveConstraints', function () {
})
})
describe('given the drive is unusually large', function () {
it('should return the large drive size warning', function () {
this.drive.size = constraints.LARGE_DRIVE_SIZE + 1
const result = constraints.getDriveImageCompatibilityStatuses(this.drive, this.image)
const expectedTuples = [ [ 'WARNING', 'LARGE_DRIVE' ] ]
expectStatusTypesAndMessagesToBe(result, expectedTuples)
})
})
describe('given the image is null', () => {
it('should return an empty list', function () {
const result = constraints.getDriveImageCompatibilityStatuses(this.drive, null)

View File

@ -20,6 +20,7 @@ const m = require('mochainon')
const path = require('path')
const availableDrives = require('../../../lib/shared/models/available-drives')
const selectionState = require('../../../lib/shared/models/selection-state')
const constraints = require('../../../lib/shared/drive-constraints')
describe('Model: availableDrives', function () {
describe('availableDrives', function () {
@ -347,6 +348,27 @@ describe('Model: availableDrives', function () {
m.chai.expect(selectionState.hasDrive()).to.be.false
})
it('should not auto-select a single large size drive', function () {
m.chai.expect(selectionState.hasDrive()).to.be.false
availableDrives.setDrives([
{
device: '/dev/sdb',
name: 'Foo',
size: constraints.LARGE_DRIVE_SIZE + 1,
mountpoints: [
{
path: '/mnt/foo'
}
],
system: false,
protected: false
}
])
m.chai.expect(selectionState.hasDrive()).to.be.false
})
})
})
})