mirror of
https://github.com/balena-io/etcher.git
synced 2025-04-24 23:37:18 +00:00
258 lines
6.9 KiB
JavaScript
258 lines
6.9 KiB
JavaScript
/*
|
|
* 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 angular = require('angular')
|
|
const _ = require('lodash')
|
|
const Bluebird = require('bluebird')
|
|
const constraints = require('../../../../../shared/drive-constraints')
|
|
const analytics = require('../../../modules/analytics')
|
|
const availableDrives = require('../../../models/available-drives')
|
|
const selectionState = require('../../../models/selection-state')
|
|
const utils = require('../../../../../shared/utils')
|
|
|
|
module.exports = function (
|
|
$q,
|
|
$uibModalInstance,
|
|
ConfirmModalService,
|
|
OSOpenExternalService
|
|
) {
|
|
/**
|
|
* @summary The drive selector state
|
|
* @type {Object}
|
|
* @public
|
|
*/
|
|
this.state = selectionState
|
|
|
|
/**
|
|
* @summary Static methods to check a drive's properties
|
|
* @type {Object}
|
|
* @public
|
|
*/
|
|
this.constraints = constraints
|
|
|
|
/**
|
|
* @summary The drives model
|
|
* @type {Object}
|
|
* @public
|
|
*
|
|
* @description
|
|
* We expose the whole service instead of the `.drives`
|
|
* property, which is the one we're interested in since
|
|
* this allows the property to be automatically updated
|
|
* when `availableDrives` detects a change in the drives.
|
|
*/
|
|
this.drives = availableDrives
|
|
|
|
/**
|
|
* @summary Determine if we can change a drive's selection state
|
|
* @function
|
|
* @private
|
|
*
|
|
* @param {Object} drive - drive
|
|
* @returns {Promise}
|
|
*
|
|
* @example
|
|
* DriveSelectorController.shouldChangeDriveSelectionState(drive)
|
|
* .then((shouldChangeDriveSelectionState) => {
|
|
* if (shouldChangeDriveSelectionState) doSomething();
|
|
* });
|
|
*/
|
|
const shouldChangeDriveSelectionState = (drive) => {
|
|
return $q.resolve(constraints.isDriveValid(drive, selectionState.getImage()))
|
|
}
|
|
|
|
/**
|
|
* @summary Toggle a drive selection
|
|
* @function
|
|
* @public
|
|
*
|
|
* @param {Object} drive - drive
|
|
* @returns {Promise} - resolved promise
|
|
*
|
|
* @example
|
|
* DriveSelectorController.toggleDrive({
|
|
* device: '/dev/disk2',
|
|
* size: 999999999,
|
|
* name: 'Cruzer USB drive'
|
|
* });
|
|
*/
|
|
this.toggleDrive = (drive) => {
|
|
return shouldChangeDriveSelectionState(drive).then((canChangeDriveSelectionState) => {
|
|
if (canChangeDriveSelectionState) {
|
|
analytics.logEvent('Toggle drive', {
|
|
drive,
|
|
previouslySelected: selectionState.isCurrentDrive(drive.device)
|
|
})
|
|
|
|
selectionState.toggleDrive(drive.device)
|
|
}
|
|
|
|
return Bluebird.resolve()
|
|
})
|
|
}
|
|
|
|
/**
|
|
* @summary Prompt the user to install missing usbboot drivers
|
|
* @function
|
|
* @public
|
|
*
|
|
* @param {Object} drive - drive
|
|
* @returns {Promise} - resolved promise
|
|
*
|
|
* @example
|
|
* DriveSelectorController.installMissingDrivers({
|
|
* linkTitle: 'Go to example.com',
|
|
* linkMessage: 'Examples are great, right?',
|
|
* linkCTA: 'Call To Action',
|
|
* link: 'https://example.com'
|
|
* });
|
|
*/
|
|
this.installMissingDrivers = (drive) => {
|
|
if (drive.link) {
|
|
analytics.logEvent('Open driver link modal', {
|
|
url: drive.link
|
|
})
|
|
|
|
return ConfirmModalService.show({
|
|
confirmationLabel: 'Yes, continue',
|
|
rejectionLabel: 'Cancel',
|
|
title: drive.linkTitle,
|
|
confirmButton: 'primary',
|
|
message: drive.linkMessage || `Etcher will open ${drive.link} in your browser`
|
|
}).then((shouldContinue) => {
|
|
if (shouldContinue) {
|
|
OSOpenExternalService.open(drive.link)
|
|
}
|
|
}).catch((error) => {
|
|
analytics.logException(error)
|
|
})
|
|
}
|
|
|
|
return Bluebird.resolve()
|
|
}
|
|
|
|
/**
|
|
* @summary Close the modal and resolve the selected drive
|
|
* @function
|
|
* @public
|
|
*
|
|
* @example
|
|
* DriveSelectorController.closeModal();
|
|
*/
|
|
this.closeModal = () => {
|
|
const selectedDrive = selectionState.getCurrentDrive()
|
|
|
|
// Sanity check to cover the case where a drive is selected,
|
|
// the drive is then unplugged from the computer and the modal
|
|
// is resolved with a non-existent drive.
|
|
if (!selectedDrive || !_.includes(this.drives.getDrives(), selectedDrive)) {
|
|
$uibModalInstance.close()
|
|
} else {
|
|
$uibModalInstance.close(selectedDrive)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @summary Select a drive and close the modal
|
|
* @function
|
|
* @public
|
|
*
|
|
* @param {Object} drive - drive
|
|
* @returns {Promise} - resolved promise
|
|
*
|
|
* @example
|
|
* DriveSelectorController.selectDriveAndClose({
|
|
* device: '/dev/disk2',
|
|
* size: 999999999,
|
|
* name: 'Cruzer USB drive'
|
|
* });
|
|
*/
|
|
this.selectDriveAndClose = (drive) => {
|
|
return shouldChangeDriveSelectionState(drive).then((canChangeDriveSelectionState) => {
|
|
if (canChangeDriveSelectionState) {
|
|
selectionState.selectDrive(drive.device)
|
|
|
|
analytics.logEvent('Drive selected (double click)')
|
|
|
|
this.closeModal()
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* @summary Memoized getDrives function
|
|
* @function
|
|
* @public
|
|
*
|
|
* @returns {Array<Object>} - memoized list of drives
|
|
*
|
|
* @example
|
|
* const drives = DriveSelectorController.getDrives()
|
|
* // Do something with drives
|
|
*/
|
|
this.getDrives = utils.memoize(this.drives.getDrives, angular.equals)
|
|
|
|
/**
|
|
* @summary Get a drive's compatibility status object(s)
|
|
* @function
|
|
* @public
|
|
*
|
|
* @description
|
|
* Given a drive, return its compatibility status with the selected image,
|
|
* containing the status type (ERROR, WARNING), and accompanying
|
|
* status message.
|
|
*
|
|
* @returns {Object[]} list of objects containing statuses
|
|
*
|
|
* @example
|
|
* const statuses = DriveSelectorController.getDriveStatuses(drive);
|
|
*
|
|
* for ({ type, message } of statuses) {
|
|
* // do something
|
|
* }
|
|
*/
|
|
this.getDriveStatuses = utils.memoize((drive) => {
|
|
return this.constraints.getDriveImageCompatibilityStatuses(drive, this.state.getImage())
|
|
}, angular.equals)
|
|
|
|
/**
|
|
* @summary Keyboard event drive toggling
|
|
* @function
|
|
* @public
|
|
*
|
|
* @description
|
|
* Keyboard-event specific entry to the toggleDrive function.
|
|
*
|
|
* @param {Object} drive - drive
|
|
* @param {Object} $event - event
|
|
*
|
|
* @example
|
|
* <div tabindex="1" ng-keypress="this.keyboardToggleDrive(drive, $event)">
|
|
* Tab-select me and press enter or space!
|
|
* </div>
|
|
*/
|
|
this.keyboardToggleDrive = (drive, $event) => {
|
|
console.log($event.keyCode)
|
|
const ENTER = 13
|
|
const SPACE = 32
|
|
if (_.includes([ ENTER, SPACE ], $event.keyCode)) {
|
|
this.toggleDrive(drive)
|
|
}
|
|
}
|
|
}
|