feat(GUI): env var toggle autoselecting all valid drives (#2306)

We introduce an environment variable
`ETCHER_DISABLE_EXPLICIT_DRIVE_SELECTION` that both enables
autoselection of drives and disables explicit drive selection by hiding
the buttons allowing this. All valid drives are autoselected, i.e. any
drive-image pair that does not result in an error, however warnings are
accepted.

Closes: https://github.com/resin-io/etcher/issues/2262
Change-Type: patch
Changelog-Entry: Introduce env var to toggle autoselection of all
drives.
This commit is contained in:
Benedict Aas 2018-05-10 18:53:53 +01:00 committed by GitHub
parent f5fd2f2be3
commit 21cb7a4847
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 92 additions and 29 deletions

View File

@ -18,6 +18,7 @@
const _ = require('lodash') const _ = require('lodash')
const angular = require('angular') const angular = require('angular')
const prettyBytes = require('pretty-bytes')
const settings = require('../../../models/settings') const settings = require('../../../models/settings')
const selectionState = require('../../../../../shared/models/selection-state') const selectionState = require('../../../../../shared/models/selection-state')
const analytics = require('../../../modules/analytics') const analytics = require('../../../modules/analytics')
@ -44,9 +45,35 @@ module.exports = function (DriveSelectorService) {
return _.head(drives).description || 'Untitled Device' return _.head(drives).description || 'Untitled Device'
} }
// eslint-disable-next-line no-magic-numbers
if (drives.length === 0) {
return 'No targets found'
}
return `${drives.length} Devices` return `${drives.length} Devices`
} }
/**
* @summary Get drive subtitle
* @function
* @public
*
* @returns {String} - drives subtitle
*
* @example
* console.log(DriveSelectionController.getDrivesSubtitle())
* > '32 GB'
*/
this.getDrivesSubtitle = () => {
const drive = selectionState.getCurrentDrive()
if (drive) {
return prettyBytes(drive.size)
}
return 'Please insert at least one target device'
}
/** /**
* @summary Get drive list label * @summary Get drive list label
* @function * @function
@ -109,4 +136,18 @@ module.exports = function (DriveSelectorService) {
* DriveSelectionController.getMemoizedSelectedDrives() * DriveSelectionController.getMemoizedSelectedDrives()
*/ */
this.getMemoizedSelectedDrives = utils.memoize(selectionState.getSelectedDrives, angular.equals) this.getMemoizedSelectedDrives = utils.memoize(selectionState.getSelectedDrives, angular.equals)
/**
* @summary Should the drive selection button be shown
* @function
* @public
*
* @returns {Boolean}
*
* @example
* DriveSelectionController.shouldShowDrivesButton()
*/
this.shouldShowDrivesButton = () => {
return !process.env.ETCHER_DISABLE_EXPLICIT_DRIVE_SELECTION
}
} }

View File

@ -33,6 +33,10 @@ svg-icon > img[disabled] {
color: $palette-theme-dark-disabled-foreground; color: $palette-theme-dark-disabled-foreground;
} }
.page-main .step-drive.text-warning {
color: $palette-theme-warning-background;
}
.page-main .relative { .page-main .relative {
position: relative; position: relative;
} }
@ -123,8 +127,13 @@ svg-icon > img[disabled] {
} }
.page-main .step-name { .page-main .step-name {
display: flex;
justify-content: center;
align-items: center;
height: 39px;
width: 100%; width: 100%;
margin-right: 4.5px; margin-right: 4.5px;
margin-bottom: 10px;
font-weight: bold; font-weight: bold;
color: $palette-theme-primary-foreground; color: $palette-theme-primary-foreground;
} }

View File

@ -49,7 +49,7 @@
</div> </div>
<div class="space-vertical-large"> <div class="space-vertical-large">
<div ng-if="!main.selection.hasDrive()"> <div ng-if="!main.selection.hasDrive() && drive.shouldShowDrivesButton()">
<div> <div>
<button class="button button-primary button-brick" <button class="button button-primary button-brick"
@ -59,21 +59,24 @@
</div> </div>
</div> </div>
<div ng-if="main.selection.hasDrive()"> <div ng-if="main.selection.hasDrive() || !drive.shouldShowDrivesButton()">
<div class="step-selection-text" <div class="step-selection-text"
ng-class="{ ng-class="{
'text-disabled': main.shouldDriveStepBeDisabled() 'text-disabled': main.shouldDriveStepBeDisabled()
}"> }">
<span class="drive-step step-name" <span class="step-drive step-name"
ng-class="{
'text-warning': !main.selection.getSelectedDevices().length
}"
uib-tooltip="{{ drive.getDriveListLabel() }}"> uib-tooltip="{{ drive.getDriveListLabel() }}">
<!-- middleEllipsis errors on undefined, therefore fallback to empty string --> <!-- middleEllipsis errors on undefined, therefore fallback to empty string -->
{{ drive.getDrivesTitle() | middleEllipsis:20 }} {{ drive.getDrivesTitle() | middleEllipsis:20 }}
</span> </span>
<span <span
ng-if="main.selection.getSelectedDevices().length === 1" ng-if="main.selection.getSelectedDevices().length <= 1"
class="step-drive step-size"> class="step-drive step-size">
{{ main.selection.getCurrentDrive().size | closestUnit }} {{ drive.getDrivesSubtitle() }}
</span> </span>
<span class="step-drive step-warning glyphicon glyphicon-exclamation-sign" <span class="step-drive step-warning glyphicon glyphicon-exclamation-sign"
uib-tooltip="{{ main.constraints.getListDriveImageCompatibilityStatuses(main.selection.getSelectedDrives(), main.selection.getImage())[0].message }}" uib-tooltip="{{ main.constraints.getListDriveImageCompatibilityStatuses(main.selection.getSelectedDrives(), main.selection.getImage())[0].message }}"
@ -81,8 +84,8 @@
</div> </div>
<button class="button button-link step-footer" <button class="button button-link step-footer"
tabindex="{{ main.selection.hasDrive() ? 2 : -1 }}" tabindex="{{ main.selection.hasDrive() ? 2 : -1 }}"
ng-click="drive.reselectDrive()" ng-hide="main.state.isFlashing() || !drive.shouldShowDrivesButton()"
ng-hide="main.state.isFlashing()">Change</button> ng-click="drive.reselectDrive()">Change</button>
</div> </div>
<div ng-if="main.selection.getSelectedDevices().length > 1" <div ng-if="main.selection.getSelectedDevices().length > 1"
class="step-drive step-list"> class="step-drive step-list">

View File

@ -6517,6 +6517,9 @@ svg-icon > img[disabled] {
.page-main .text-disabled > span { .page-main .text-disabled > span {
color: #787c7f; } color: #787c7f; }
.page-main .step-drive.text-warning {
color: #ff912f; }
.page-main .relative { .page-main .relative {
position: relative; } position: relative; }
@ -6586,8 +6589,13 @@ svg-icon > img[disabled] {
vertical-align: text-top; } vertical-align: text-top; }
.page-main .step-name { .page-main .step-name {
display: flex;
justify-content: center;
align-items: center;
height: 39px;
width: 100%; width: 100%;
margin-right: 4.5px; margin-right: 4.5px;
margin-bottom: 10px;
font-weight: bold; font-weight: bold;
color: #fff; } color: #fff; }

View File

@ -197,41 +197,43 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
return accState return accState
}, newState) }, newState)
const shouldAutoselectAll = Boolean(process.env.ETCHER_DISABLE_EXPLICIT_DRIVE_SELECTION)
const AUTOSELECT_DRIVE_COUNT = 1 const AUTOSELECT_DRIVE_COUNT = 1
const numberOfDrives = drives.length
const nonStaleSelectedDevices = nonStaleNewState.getIn([ 'selection', 'devices' ]).toJS() const nonStaleSelectedDevices = nonStaleNewState.getIn([ 'selection', 'devices' ]).toJS()
const hasSelectedDevices = nonStaleSelectedDevices.length >= AUTOSELECT_DRIVE_COUNT const hasSelectedDevices = nonStaleSelectedDevices.length >= AUTOSELECT_DRIVE_COUNT
if (numberOfDrives === AUTOSELECT_DRIVE_COUNT && !hasSelectedDevices) { const shouldAutoselectOne = drives.length === AUTOSELECT_DRIVE_COUNT && !hasSelectedDevices
const [ drive ] = drives
if (shouldAutoselectOne || shouldAutoselectAll) {
// Even if there's no image selected, we need to call several // Even if there's no image selected, we need to call several
// drive/image related checks, and `{}` works fine with them // drive/image related checks, and `{}` works fine with them
const image = state.getIn([ 'selection', 'image' ], Immutable.fromJS({})).toJS() const image = state.getIn([ 'selection', 'image' ], Immutable.fromJS({})).toJS()
if (_.every([ return _.reduce(drives, (accState, drive) => {
constraints.isDriveValid(drive, image), if (_.every([
constraints.isDriveSizeRecommended(drive, image), constraints.isDriveValid(drive, image),
constraints.isDriveSizeRecommended(drive, image),
// We don't want to auto-select large drives // We don't want to auto-select large drives
!constraints.isDriveSizeLarge(drive), !constraints.isDriveSizeLarge(drive),
// We don't want to auto-select system drives, // We don't want to auto-select system drives,
// even when "unsafe mode" is enabled // even when "unsafe mode" is enabled
!constraints.isSystemDrive(drive) !constraints.isSystemDrive(drive)
])) { ]) || (shouldAutoselectAll && constraints.isDriveValid(drive, image))) {
// Auto-select this drive // Auto-select this drive
return storeReducer(nonStaleNewState, { return storeReducer(accState, {
type: ACTIONS.SELECT_DRIVE, type: ACTIONS.SELECT_DRIVE,
data: drive.device
})
}
// Deselect this drive in case it still is selected
return storeReducer(accState, {
type: ACTIONS.DESELECT_DRIVE,
data: drive.device data: drive.device
}) })
} }, nonStaleNewState)
// Deselect this drive in case it still is selected
return storeReducer(nonStaleNewState, {
type: ACTIONS.DESELECT_DRIVE,
data: drive.device
})
} }
return nonStaleNewState return nonStaleNewState