mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-23 19:26:33 +00:00
Remove remaining angular
Change-type: patch
This commit is contained in:
parent
26d0e46367
commit
d5eb679cf0
@ -20,12 +20,6 @@
|
|||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
/* eslint-disable no-var */
|
|
||||||
|
|
||||||
var angular = require('angular')
|
|
||||||
|
|
||||||
/* eslint-enable no-var */
|
|
||||||
|
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const sdk = require('etcher-sdk')
|
const sdk = require('etcher-sdk')
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
@ -79,68 +73,51 @@ store.dispatch({
|
|||||||
const applicationSessionUuid = store.getState().toJS().applicationSessionUuid
|
const applicationSessionUuid = store.getState().toJS().applicationSessionUuid
|
||||||
const flashingWorkflowUuid = store.getState().toJS().flashingWorkflowUuid
|
const flashingWorkflowUuid = store.getState().toJS().flashingWorkflowUuid
|
||||||
|
|
||||||
const app = angular.module('Etcher', [
|
console.log([
|
||||||
require('angular-ui-router'),
|
' _____ _ _',
|
||||||
|
'| ___| | | |',
|
||||||
|
'| |__ | |_ ___| |__ ___ _ __',
|
||||||
|
'| __|| __/ __| \'_ \\ / _ \\ \'__|',
|
||||||
|
'| |___| || (__| | | | __/ |',
|
||||||
|
'\\____/ \\__\\___|_| |_|\\___|_|',
|
||||||
|
'',
|
||||||
|
'Interested in joining the Etcher team?',
|
||||||
|
'Drop us a line at join+etcher@balena.io',
|
||||||
|
'',
|
||||||
|
`Version = ${packageJSON.version}, Type = ${packageJSON.packageType}`
|
||||||
|
].join('\n'))
|
||||||
|
|
||||||
// Components
|
const currentVersion = packageJSON.version
|
||||||
require('./components/safe-webview').MODULE_NAME,
|
|
||||||
|
|
||||||
// Pages
|
analytics.logEvent('Application start', {
|
||||||
require('./pages/main/main.ts').MODULE_NAME,
|
packageType: packageJSON.packageType,
|
||||||
require('./components/finish/index.ts').MODULE_NAME
|
version: currentVersion,
|
||||||
])
|
applicationSessionUuid
|
||||||
|
|
||||||
app.run(() => {
|
|
||||||
console.log([
|
|
||||||
' _____ _ _',
|
|
||||||
'| ___| | | |',
|
|
||||||
'| |__ | |_ ___| |__ ___ _ __',
|
|
||||||
'| __|| __/ __| \'_ \\ / _ \\ \'__|',
|
|
||||||
'| |___| || (__| | | | __/ |',
|
|
||||||
'\\____/ \\__\\___|_| |_|\\___|_|',
|
|
||||||
'',
|
|
||||||
'Interested in joining the Etcher team?',
|
|
||||||
'Drop us a line at join+etcher@balena.io',
|
|
||||||
'',
|
|
||||||
`Version = ${packageJSON.version}, Type = ${packageJSON.packageType}`
|
|
||||||
].join('\n'))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
app.run(() => {
|
store.observe(() => {
|
||||||
const currentVersion = packageJSON.version
|
if (!flashState.isFlashing()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
analytics.logEvent('Application start', {
|
const currentFlashState = flashState.getFlashState()
|
||||||
packageType: packageJSON.packageType,
|
const stateType = !currentFlashState.flashing && currentFlashState.verifying
|
||||||
version: currentVersion,
|
? `Verifying ${currentFlashState.verifying}`
|
||||||
applicationSessionUuid
|
: `Flashing ${currentFlashState.flashing}`
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
app.run(() => {
|
// NOTE: There is usually a short time period between the `isFlashing()`
|
||||||
store.observe(() => {
|
// property being set, and the flashing actually starting, which
|
||||||
if (!flashState.isFlashing()) {
|
// might cause some non-sense flashing state logs including
|
||||||
return
|
// `undefined` values.
|
||||||
}
|
analytics.logDebug(
|
||||||
|
`${stateType} devices, ` +
|
||||||
|
`${currentFlashState.percentage}% at ${currentFlashState.speed} MB/s ` +
|
||||||
|
`(total ${currentFlashState.totalSpeed} MB/s) ` +
|
||||||
|
`eta in ${currentFlashState.eta}s ` +
|
||||||
|
`with ${currentFlashState.failed} failed devices`
|
||||||
|
)
|
||||||
|
|
||||||
const currentFlashState = flashState.getFlashState()
|
windowProgress.set(currentFlashState)
|
||||||
const stateType = !currentFlashState.flashing && currentFlashState.verifying
|
|
||||||
? `Verifying ${currentFlashState.verifying}`
|
|
||||||
: `Flashing ${currentFlashState.flashing}`
|
|
||||||
|
|
||||||
// NOTE: There is usually a short time period between the `isFlashing()`
|
|
||||||
// property being set, and the flashing actually starting, which
|
|
||||||
// might cause some non-sense flashing state logs including
|
|
||||||
// `undefined` values.
|
|
||||||
analytics.logDebug(
|
|
||||||
`${stateType} devices, ` +
|
|
||||||
`${currentFlashState.percentage}% at ${currentFlashState.speed} MB/s ` +
|
|
||||||
`(total ${currentFlashState.totalSpeed} MB/s) ` +
|
|
||||||
`eta in ${currentFlashState.eta}s ` +
|
|
||||||
`with ${currentFlashState.failed} failed devices`
|
|
||||||
)
|
|
||||||
|
|
||||||
windowProgress.set(currentFlashState)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -197,242 +174,171 @@ const COMPUTE_MODULE_DESCRIPTIONS = {
|
|||||||
[USB_PRODUCT_ID_BCM2710_BOOT]: 'Compute Module 3'
|
[USB_PRODUCT_ID_BCM2710_BOOT]: 'Compute Module 3'
|
||||||
}
|
}
|
||||||
|
|
||||||
app.run(($timeout) => {
|
const BLACKLISTED_DRIVES = settings.has('driveBlacklist')
|
||||||
const BLACKLISTED_DRIVES = settings.has('driveBlacklist')
|
? settings.get('driveBlacklist').split(',')
|
||||||
? settings.get('driveBlacklist').split(',')
|
: []
|
||||||
: []
|
|
||||||
|
|
||||||
// eslint-disable-next-line require-jsdoc
|
// eslint-disable-next-line require-jsdoc
|
||||||
const driveIsAllowed = (drive) => {
|
const driveIsAllowed = (drive) => {
|
||||||
return !(
|
return !(
|
||||||
BLACKLISTED_DRIVES.includes(drive.devicePath) ||
|
BLACKLISTED_DRIVES.includes(drive.devicePath) ||
|
||||||
BLACKLISTED_DRIVES.includes(drive.device) ||
|
BLACKLISTED_DRIVES.includes(drive.device) ||
|
||||||
BLACKLISTED_DRIVES.includes(drive.raw)
|
BLACKLISTED_DRIVES.includes(drive.raw)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line require-jsdoc,consistent-return
|
// eslint-disable-next-line require-jsdoc,consistent-return
|
||||||
const prepareDrive = (drive) => {
|
const prepareDrive = (drive) => {
|
||||||
if (drive instanceof sdk.sourceDestination.BlockDevice) {
|
if (drive instanceof sdk.sourceDestination.BlockDevice) {
|
||||||
return drive.drive
|
return drive.drive
|
||||||
} else if (drive instanceof sdk.sourceDestination.UsbbootDrive) {
|
} else if (drive instanceof sdk.sourceDestination.UsbbootDrive) {
|
||||||
// This is a workaround etcher expecting a device string and a size
|
// This is a workaround etcher expecting a device string and a size
|
||||||
drive.device = drive.usbDevice.portId
|
drive.device = drive.usbDevice.portId
|
||||||
drive.size = null
|
drive.size = null
|
||||||
drive.progress = 0
|
drive.progress = 0
|
||||||
drive.disabled = true
|
drive.disabled = true
|
||||||
drive.on('progress', (progress) => {
|
drive.on('progress', (progress) => {
|
||||||
updateDriveProgress(drive, progress)
|
updateDriveProgress(drive, progress)
|
||||||
})
|
})
|
||||||
return drive
|
return drive
|
||||||
} else if (drive instanceof sdk.sourceDestination.DriverlessDevice) {
|
} else if (drive instanceof sdk.sourceDestination.DriverlessDevice) {
|
||||||
const description = COMPUTE_MODULE_DESCRIPTIONS[drive.deviceDescriptor.idProduct] || 'Compute Module'
|
const description = COMPUTE_MODULE_DESCRIPTIONS[drive.deviceDescriptor.idProduct] || 'Compute Module'
|
||||||
return {
|
return {
|
||||||
device: `${usbIdToString(drive.deviceDescriptor.idVendor)}:${usbIdToString(drive.deviceDescriptor.idProduct)}`,
|
device: `${usbIdToString(drive.deviceDescriptor.idVendor)}:${usbIdToString(drive.deviceDescriptor.idProduct)}`,
|
||||||
displayName: 'Missing drivers',
|
displayName: 'Missing drivers',
|
||||||
description,
|
description,
|
||||||
mountpoints: [],
|
mountpoints: [],
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
isSystem: false,
|
isSystem: false,
|
||||||
disabled: true,
|
disabled: true,
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
size: null,
|
size: null,
|
||||||
link: 'https://www.raspberrypi.org/documentation/hardware/computemodule/cm-emmc-flashing.md',
|
link: 'https://www.raspberrypi.org/documentation/hardware/computemodule/cm-emmc-flashing.md',
|
||||||
linkCTA: 'Install',
|
linkCTA: 'Install',
|
||||||
linkTitle: 'Install missing drivers',
|
linkTitle: 'Install missing drivers',
|
||||||
linkMessage: [
|
linkMessage: [
|
||||||
'Would you like to download the necessary drivers from the Raspberry Pi Foundation?',
|
'Would you like to download the necessary drivers from the Raspberry Pi Foundation?',
|
||||||
'This will open your browser.\n\n',
|
'This will open your browser.\n\n',
|
||||||
'Once opened, download and run the installer from the "Windows Installer" section to install the drivers.'
|
'Once opened, download and run the installer from the "Windows Installer" section to install the drivers.'
|
||||||
].join(' ')
|
].join(' ')
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line require-jsdoc
|
// eslint-disable-next-line require-jsdoc
|
||||||
const setDrives = (drives) => {
|
const setDrives = (drives) => {
|
||||||
availableDrives.setDrives(_.values(drives))
|
availableDrives.setDrives(_.values(drives))
|
||||||
|
}
|
||||||
|
|
||||||
// Safely trigger a digest cycle.
|
// eslint-disable-next-line require-jsdoc
|
||||||
// In some cases, AngularJS doesn't acknowledge that the
|
const getDrives = () => {
|
||||||
// available drives list has changed, and incorrectly
|
return _.keyBy(availableDrives.getDrives() || [], 'device')
|
||||||
// keeps asking the user to "Connect a drive".
|
}
|
||||||
$timeout()
|
|
||||||
|
// eslint-disable-next-line require-jsdoc
|
||||||
|
const addDrive = (drive) => {
|
||||||
|
const preparedDrive = prepareDrive(drive)
|
||||||
|
if (!driveIsAllowed(preparedDrive)) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
const drives = getDrives()
|
||||||
|
drives[preparedDrive.device] = preparedDrive
|
||||||
|
setDrives(drives)
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line require-jsdoc
|
// eslint-disable-next-line require-jsdoc
|
||||||
const getDrives = () => {
|
const removeDrive = (drive) => {
|
||||||
return _.keyBy(availableDrives.getDrives() || [], 'device')
|
const preparedDrive = prepareDrive(drive)
|
||||||
}
|
const drives = getDrives()
|
||||||
|
// eslint-disable-next-line prefer-reflect
|
||||||
|
delete drives[preparedDrive.device]
|
||||||
|
setDrives(drives)
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line require-jsdoc
|
// eslint-disable-next-line require-jsdoc
|
||||||
const addDrive = (drive) => {
|
const updateDriveProgress = (drive, progress) => {
|
||||||
const preparedDrive = prepareDrive(drive)
|
const drives = getDrives()
|
||||||
if (!driveIsAllowed(preparedDrive)) {
|
const driveInMap = drives[drive.device]
|
||||||
return
|
if (driveInMap) {
|
||||||
}
|
driveInMap.progress = progress
|
||||||
const drives = getDrives()
|
|
||||||
drives[preparedDrive.device] = preparedDrive
|
|
||||||
setDrives(drives)
|
setDrives(drives)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line require-jsdoc
|
driveScanner.on('attach', addDrive)
|
||||||
const removeDrive = (drive) => {
|
driveScanner.on('detach', removeDrive)
|
||||||
const preparedDrive = prepareDrive(drive)
|
|
||||||
const drives = getDrives()
|
|
||||||
// eslint-disable-next-line prefer-reflect
|
|
||||||
delete drives[preparedDrive.device]
|
|
||||||
setDrives(drives)
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line require-jsdoc
|
driveScanner.on('error', (error) => {
|
||||||
const updateDriveProgress = (drive, progress) => {
|
// Stop the drive scanning loop in case of errors,
|
||||||
const drives = getDrives()
|
// otherwise we risk presenting the same error over
|
||||||
const driveInMap = drives[drive.device]
|
// and over again to the user, while also heavily
|
||||||
if (driveInMap) {
|
// spamming our error reporting service.
|
||||||
driveInMap.progress = progress
|
driveScanner.stop()
|
||||||
setDrives(drives)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
driveScanner.on('attach', addDrive)
|
return exceptionReporter.report(error)
|
||||||
driveScanner.on('detach', removeDrive)
|
|
||||||
|
|
||||||
driveScanner.on('error', (error) => {
|
|
||||||
// Stop the drive scanning loop in case of errors,
|
|
||||||
// otherwise we risk presenting the same error over
|
|
||||||
// and over again to the user, while also heavily
|
|
||||||
// spamming our error reporting service.
|
|
||||||
driveScanner.stop()
|
|
||||||
|
|
||||||
return exceptionReporter.report(error)
|
|
||||||
})
|
|
||||||
|
|
||||||
driveScanner.start()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
app.run(($window) => {
|
driveScanner.start()
|
||||||
let popupExists = false
|
|
||||||
|
|
||||||
$window.addEventListener('beforeunload', (event) => {
|
let popupExists = false
|
||||||
if (!flashState.isFlashing() || popupExists) {
|
|
||||||
analytics.logEvent('Close application', {
|
|
||||||
isFlashing: flashState.isFlashing(),
|
|
||||||
applicationSessionUuid
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't close window while flashing
|
window.addEventListener('beforeunload', (event) => {
|
||||||
event.returnValue = false
|
if (!flashState.isFlashing() || popupExists) {
|
||||||
|
analytics.logEvent('Close application', {
|
||||||
// Don't open any more popups
|
isFlashing: flashState.isFlashing(),
|
||||||
popupExists = true
|
|
||||||
|
|
||||||
analytics.logEvent('Close attempt while flashing', { applicationSessionUuid, flashingWorkflowUuid })
|
|
||||||
|
|
||||||
osDialog.showWarning({
|
|
||||||
confirmationLabel: 'Yes, quit',
|
|
||||||
rejectionLabel: 'Cancel',
|
|
||||||
title: 'Are you sure you want to close Etcher?',
|
|
||||||
description: messages.warning.exitWhileFlashing()
|
|
||||||
}).then((confirmed) => {
|
|
||||||
if (confirmed) {
|
|
||||||
analytics.logEvent('Close confirmed while flashing', {
|
|
||||||
flashInstanceUuid: flashState.getFlashUuid(),
|
|
||||||
applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid
|
|
||||||
})
|
|
||||||
|
|
||||||
// This circumvents the 'beforeunload' event unlike
|
|
||||||
// electron.remote.app.quit() which does not.
|
|
||||||
electron.remote.process.exit(EXIT_CODES.SUCCESS)
|
|
||||||
}
|
|
||||||
|
|
||||||
analytics.logEvent('Close rejected while flashing', { applicationSessionUuid, flashingWorkflowUuid })
|
|
||||||
popupExists = false
|
|
||||||
}).catch(exceptionReporter.report)
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Helper fn for events
|
|
||||||
* @function
|
|
||||||
* @private
|
|
||||||
* @example
|
|
||||||
* window.addEventListener('click', extendLock)
|
|
||||||
*/
|
|
||||||
const extendLock = () => {
|
|
||||||
updateLock.extend()
|
|
||||||
}
|
|
||||||
|
|
||||||
$window.addEventListener('click', extendLock)
|
|
||||||
$window.addEventListener('touchstart', extendLock)
|
|
||||||
|
|
||||||
// Initial update lock acquisition
|
|
||||||
extendLock()
|
|
||||||
})
|
|
||||||
|
|
||||||
app.run(($rootScope) => {
|
|
||||||
$rootScope.$on('$stateChangeSuccess', (event, toState, toParams, fromState) => {
|
|
||||||
// Ignore first navigation
|
|
||||||
if (!fromState.name) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
analytics.logEvent('Navigate', {
|
|
||||||
to: toState.name,
|
|
||||||
from: fromState.name,
|
|
||||||
applicationSessionUuid
|
applicationSessionUuid
|
||||||
})
|
})
|
||||||
})
|
return
|
||||||
})
|
}
|
||||||
|
|
||||||
app.config(($urlRouterProvider) => {
|
// Don't close window while flashing
|
||||||
$urlRouterProvider.otherwise('/main')
|
event.returnValue = false
|
||||||
})
|
|
||||||
|
|
||||||
app.config(($provide) => {
|
// Don't open any more popups
|
||||||
$provide.decorator('$exceptionHandler', ($delegate) => {
|
popupExists = true
|
||||||
return (exception, cause) => {
|
|
||||||
exceptionReporter.report(exception)
|
analytics.logEvent('Close attempt while flashing', { applicationSessionUuid, flashingWorkflowUuid })
|
||||||
$delegate(exception, cause)
|
|
||||||
|
osDialog.showWarning({
|
||||||
|
confirmationLabel: 'Yes, quit',
|
||||||
|
rejectionLabel: 'Cancel',
|
||||||
|
title: 'Are you sure you want to close Etcher?',
|
||||||
|
description: messages.warning.exitWhileFlashing()
|
||||||
|
}).then((confirmed) => {
|
||||||
|
if (confirmed) {
|
||||||
|
analytics.logEvent('Close confirmed while flashing', {
|
||||||
|
flashInstanceUuid: flashState.getFlashUuid(),
|
||||||
|
applicationSessionUuid,
|
||||||
|
flashingWorkflowUuid
|
||||||
|
})
|
||||||
|
|
||||||
|
// This circumvents the 'beforeunload' event unlike
|
||||||
|
// electron.remote.app.quit() which does not.
|
||||||
|
electron.remote.process.exit(EXIT_CODES.SUCCESS)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
app.config(($locationProvider) => {
|
analytics.logEvent('Close rejected while flashing', { applicationSessionUuid, flashingWorkflowUuid })
|
||||||
// NOTE(Shou): this seems to invoke a minor perf decrease when set to true
|
popupExists = false
|
||||||
$locationProvider.html5Mode({
|
|
||||||
rewriteLinks: false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
app.controller('StateController', function ($rootScope, $scope) {
|
|
||||||
const unregisterStateChange = $rootScope.$on('$stateChangeSuccess', (event, toState, toParams, fromState) => {
|
|
||||||
this.currentName = toState.name
|
|
||||||
})
|
|
||||||
|
|
||||||
$scope.$on('$destroy', unregisterStateChange)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Get the current state name
|
|
||||||
* @function
|
|
||||||
* @public
|
|
||||||
*
|
|
||||||
* @returns {String} current state name
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* if (StateController.currentName === 'main') {
|
|
||||||
* console.log('We are on the main screen!');
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
this.currentName = null
|
|
||||||
})
|
|
||||||
|
|
||||||
// Ensure user settings are loaded before
|
|
||||||
// we bootstrap the Angular.js application
|
|
||||||
angular.element(document).ready(() => {
|
|
||||||
settings.load().then(() => {
|
|
||||||
angular.bootstrap(document, [ 'Etcher' ])
|
|
||||||
}).catch(exceptionReporter.report)
|
}).catch(exceptionReporter.report)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Helper fn for events
|
||||||
|
* @function
|
||||||
|
* @private
|
||||||
|
* @example
|
||||||
|
* window.addEventListener('click', extendLock)
|
||||||
|
*/
|
||||||
|
const extendLock = () => {
|
||||||
|
updateLock.extend()
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('click', extendLock)
|
||||||
|
window.addEventListener('touchstart', extendLock)
|
||||||
|
|
||||||
|
// Initial update lock acquisition
|
||||||
|
extendLock()
|
||||||
|
|
||||||
|
settings.load().catch(exceptionReporter.report)
|
||||||
|
|
||||||
|
require('./tsapp.tsx')
|
||||||
|
@ -29,7 +29,7 @@ import { FlashAnother } from '../flash-another/flash-another';
|
|||||||
import { FlashResults } from '../flash-results/flash-results';
|
import { FlashResults } from '../flash-results/flash-results';
|
||||||
import * as SVGIcon from '../svg-icon/svg-icon';
|
import * as SVGIcon from '../svg-icon/svg-icon';
|
||||||
|
|
||||||
const restart = (options: any, $state: any) => {
|
const restart = (options: any, goToMain: () => void) => {
|
||||||
const {
|
const {
|
||||||
applicationSessionUuid,
|
applicationSessionUuid,
|
||||||
flashingWorkflowUuid,
|
flashingWorkflowUuid,
|
||||||
@ -54,7 +54,7 @@ const restart = (options: any, $state: any) => {
|
|||||||
data: uuidV4(),
|
data: uuidV4(),
|
||||||
});
|
});
|
||||||
|
|
||||||
$state.go('main');
|
goToMain();
|
||||||
};
|
};
|
||||||
|
|
||||||
const formattedErrors = () => {
|
const formattedErrors = () => {
|
||||||
@ -67,7 +67,7 @@ const formattedErrors = () => {
|
|||||||
return errors.join('\n');
|
return errors.join('\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
function FinishPage({ $state }: any) {
|
function FinishPage({ goToMain }: { goToMain: () => void }) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const results = flashState.getFlashResults().results || {};
|
const results = flashState.getFlashResults().results || {};
|
||||||
const progressMessage = messages.progress;
|
const progressMessage = messages.progress;
|
||||||
@ -82,7 +82,7 @@ function FinishPage({ $state }: any) {
|
|||||||
></FlashResults>
|
></FlashResults>
|
||||||
|
|
||||||
<FlashAnother
|
<FlashAnother
|
||||||
onClick={(options: any) => restart(options, $state)}
|
onClick={(options: any) => restart(options, goToMain)}
|
||||||
></FlashAnother>
|
></FlashAnother>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019 balena.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module Etcher.Pages.Finish
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as angular from 'angular';
|
|
||||||
import { react2angular } from 'react2angular';
|
|
||||||
import FinishPage from './finish';
|
|
||||||
|
|
||||||
export const MODULE_NAME = 'Etcher.Pages.Finish';
|
|
||||||
const Finish = angular.module(MODULE_NAME, []);
|
|
||||||
|
|
||||||
Finish.component('finish', react2angular(FinishPage, [], ['$state']));
|
|
||||||
|
|
||||||
Finish.config(($stateProvider: any) => {
|
|
||||||
$stateProvider.state('success', {
|
|
||||||
url: '/success',
|
|
||||||
template: '<finish style="width:100%"></finish>',
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 balena.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module Etcher.Components.SafeWebview
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as angular from 'angular';
|
|
||||||
import { react2angular } from 'react2angular';
|
|
||||||
import * as SafeWebview from './safe-webview';
|
|
||||||
|
|
||||||
export const MODULE_NAME = 'Etcher.Components.SafeWebview';
|
|
||||||
const AngularSafeWebview = angular.module(MODULE_NAME, []);
|
|
||||||
|
|
||||||
AngularSafeWebview.component('safeWebview', react2angular(SafeWebview));
|
|
@ -6,21 +6,9 @@
|
|||||||
<link rel="stylesheet" type="text/css" href="../../../node_modules/flexboxgrid/dist/flexboxgrid.css">
|
<link rel="stylesheet" type="text/css" href="../../../node_modules/flexboxgrid/dist/flexboxgrid.css">
|
||||||
<link rel="stylesheet" type="text/css" href="../css/main.css">
|
<link rel="stylesheet" type="text/css" href="../css/main.css">
|
||||||
<link rel="stylesheet" type="text/css" href="../css/desktop.css">
|
<link rel="stylesheet" type="text/css" href="../css/desktop.css">
|
||||||
<link rel="stylesheet" type="text/css" href="../css/angular.css">
|
|
||||||
<script src="../../../generated/gui.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<main id="main"></main>
|
||||||
<main ui-view></main>
|
<script src="../../../generated/gui.js"></script>
|
||||||
|
|
||||||
<div class="section-loader"
|
|
||||||
ng-controller="StateController as state"
|
|
||||||
ng-class="{
|
|
||||||
isFinish: state.currentName === 'success'
|
|
||||||
}">
|
|
||||||
<safe-webview src="'https://www.balena.io/etcher/success-banner/'">
|
|
||||||
</safe-webview>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -112,8 +112,7 @@ const ACTIONS = _.fromPairs(_.map([
|
|||||||
'DESELECT_DRIVE',
|
'DESELECT_DRIVE',
|
||||||
'DESELECT_IMAGE',
|
'DESELECT_IMAGE',
|
||||||
'SET_APPLICATION_SESSION_UUID',
|
'SET_APPLICATION_SESSION_UUID',
|
||||||
'SET_FLASHING_WORKFLOW_UUID',
|
'SET_FLASHING_WORKFLOW_UUID'
|
||||||
'SET_WEBVIEW_SHOWING_STATUS'
|
|
||||||
], (message) => {
|
], (message) => {
|
||||||
return [ message, message ]
|
return [ message, message ]
|
||||||
}))
|
}))
|
||||||
@ -507,10 +506,6 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
|
|||||||
return state.set('flashingWorkflowUuid', action.data)
|
return state.set('flashingWorkflowUuid', action.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
case ACTIONS.SET_WEBVIEW_SHOWING_STATUS: {
|
|
||||||
return state.set('isWebviewShowing', action.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
@ -160,15 +160,10 @@ const formatSeconds = (totalSeconds: number) => {
|
|||||||
return `${minutes}m${seconds}s`;
|
return `${minutes}m${seconds}s`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Flash = ({
|
export const Flash = ({ shouldFlashStepBeDisabled, goToSuccess }: any) => {
|
||||||
shouldFlashStepBeDisabled,
|
|
||||||
lastFlashErrorCode,
|
|
||||||
progressMessage,
|
|
||||||
goToSuccess,
|
|
||||||
}: any) => {
|
|
||||||
const state: any = flashState.getFlashState();
|
const state: any = flashState.getFlashState();
|
||||||
const isFlashing = flashState.isFlashing();
|
const isFlashing = flashState.isFlashing();
|
||||||
const flashErrorCode = lastFlashErrorCode();
|
const flashErrorCode = flashState.getLastFlashErrorCode();
|
||||||
|
|
||||||
const [warningMessages, setWarningMessages] = React.useState<string[]>([]);
|
const [warningMessages, setWarningMessages] = React.useState<string[]>([]);
|
||||||
const [errorMessage, setErrorMessage] = React.useState('');
|
const [errorMessage, setErrorMessage] = React.useState('');
|
||||||
@ -272,7 +267,7 @@ export const Flash = ({
|
|||||||
<span className="target-status-dot"></span>
|
<span className="target-status-dot"></span>
|
||||||
<span className="target-status-quantity">{state.failed}</span>
|
<span className="target-status-quantity">{state.failed}</span>
|
||||||
<span className="target-status-message">
|
<span className="target-status-message">
|
||||||
{progressMessage.failed(state.failed)}{' '}
|
{messages.progress.failed(state.failed)}{' '}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,8 +21,10 @@ import * as React from 'react';
|
|||||||
import { Button } from 'rendition';
|
import { Button } from 'rendition';
|
||||||
|
|
||||||
import * as FeaturedProject from '../../components/featured-project/featured-project';
|
import * as FeaturedProject from '../../components/featured-project/featured-project';
|
||||||
|
import FinishPage from '../../components/finish/finish';
|
||||||
import * as ImageSelector from '../../components/image-selector/image-selector';
|
import * as ImageSelector from '../../components/image-selector/image-selector';
|
||||||
import * as ReducedFlashingInfos from '../../components/reduced-flashing-infos/reduced-flashing-infos';
|
import * as ReducedFlashingInfos from '../../components/reduced-flashing-infos/reduced-flashing-infos';
|
||||||
|
import * as SafeWebview from '../../components/safe-webview/safe-webview';
|
||||||
import { SettingsModal } from '../../components/settings/settings';
|
import { SettingsModal } from '../../components/settings/settings';
|
||||||
import * as SvgIcon from '../../components/svg-icon/svg-icon.jsx';
|
import * as SvgIcon from '../../components/svg-icon/svg-icon.jsx';
|
||||||
import * as flashState from '../../models/flash-state';
|
import * as flashState from '../../models/flash-state';
|
||||||
@ -34,19 +36,16 @@ import { ThemedProvider } from '../../styled-components';
|
|||||||
import { colors } from '../../theme';
|
import { colors } from '../../theme';
|
||||||
import * as middleEllipsis from '../../utils/middle-ellipsis';
|
import * as middleEllipsis from '../../utils/middle-ellipsis';
|
||||||
|
|
||||||
import * as messages from '../../../../shared/messages';
|
|
||||||
import { bytesToClosestUnit } from '../../../../shared/units';
|
import { bytesToClosestUnit } from '../../../../shared/units';
|
||||||
|
|
||||||
import { DriveSelector } from './DriveSelector';
|
import { DriveSelector } from './DriveSelector';
|
||||||
import { Flash } from './Flash';
|
import { Flash } from './Flash';
|
||||||
|
|
||||||
const DEFAULT_SUPPORT_URL =
|
function getDrivesTitle() {
|
||||||
'https://github.com/balena-io/etcher/blob/master/SUPPORT.md';
|
const drives = selectionState.getSelectedDrives();
|
||||||
|
|
||||||
const getDrivesTitle = (selection: any) => {
|
|
||||||
const drives = selection.getSelectedDrives();
|
|
||||||
|
|
||||||
if (drives.length === 1) {
|
if (drives.length === 1) {
|
||||||
|
// @ts-ignore
|
||||||
return drives[0].description || 'Untitled Device';
|
return drives[0].description || 'Untitled Device';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,162 +54,204 @@ const getDrivesTitle = (selection: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return `${drives.length} Targets`;
|
return `${drives.length} Targets`;
|
||||||
};
|
}
|
||||||
|
|
||||||
const getImageBasename = (selection: any) => {
|
function getImageBasename() {
|
||||||
if (!selection.hasImage()) {
|
if (!selectionState.hasImage()) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectionImageName = selection.getImageName();
|
const selectionImageName = selectionState.getImageName();
|
||||||
const imageBasename = path.basename(selection.getImagePath());
|
const imageBasename = path.basename(selectionState.getImagePath());
|
||||||
return selectionImageName || imageBasename;
|
return selectionImageName || imageBasename;
|
||||||
};
|
}
|
||||||
|
|
||||||
const MainPage = ({ $state }: any) => {
|
interface MainPageStateFromStore {
|
||||||
const setRefresh = React.useState(false)[1];
|
isFlashing: boolean;
|
||||||
const [isWebviewShowing, setIsWebviewShowing] = React.useState(false);
|
hasImage: boolean;
|
||||||
const [hideSettings, setHideSettings] = React.useState(true);
|
hasDrive: boolean;
|
||||||
React.useEffect(() => {
|
imageLogo: string;
|
||||||
return (store as any).observe(() => {
|
imageSize: number;
|
||||||
setRefresh(ref => !ref);
|
imageName: string;
|
||||||
|
driveTitle: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MainPageState {
|
||||||
|
current: 'main' | 'success';
|
||||||
|
isWebviewShowing: boolean;
|
||||||
|
hideSettings: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MainPage extends React.Component<
|
||||||
|
{},
|
||||||
|
MainPageState & MainPageStateFromStore
|
||||||
|
> {
|
||||||
|
constructor(props: {}) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
current: 'main',
|
||||||
|
isWebviewShowing: false,
|
||||||
|
hideSettings: true,
|
||||||
|
...this.stateHelper(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private stateHelper(): MainPageStateFromStore {
|
||||||
|
return {
|
||||||
|
isFlashing: flashState.isFlashing(),
|
||||||
|
hasImage: selectionState.hasImage(),
|
||||||
|
hasDrive: selectionState.hasDrive(),
|
||||||
|
imageLogo: selectionState.getImageLogo(),
|
||||||
|
imageSize: selectionState.getImageSize(),
|
||||||
|
imageName: getImageBasename(),
|
||||||
|
driveTitle: getDrivesTitle(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount() {
|
||||||
|
(store as any).observe(() => {
|
||||||
|
this.setState(this.stateHelper());
|
||||||
});
|
});
|
||||||
}, []);
|
}
|
||||||
|
|
||||||
const setWebviewShowing = (isShowing: boolean) => {
|
public render() {
|
||||||
setIsWebviewShowing(isShowing);
|
const shouldDriveStepBeDisabled = !this.state.hasImage;
|
||||||
store.dispatch({
|
const shouldFlashStepBeDisabled =
|
||||||
type: 'SET_WEBVIEW_SHOWING_STATUS',
|
!this.state.hasImage || !this.state.hasDrive;
|
||||||
data: Boolean(isShowing),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const isFlashing = flashState.isFlashing();
|
if (this.state.current === 'main') {
|
||||||
const shouldDriveStepBeDisabled = !selectionState.hasImage();
|
return (
|
||||||
const shouldFlashStepBeDisabled =
|
<ThemedProvider style={{ height: '100%', width: '100%' }}>
|
||||||
!selectionState.hasDrive() || shouldDriveStepBeDisabled;
|
<header
|
||||||
const hasDrive = selectionState.hasDrive();
|
id="app-header"
|
||||||
const imageLogo = selectionState.getImageLogo();
|
style={{
|
||||||
const imageSize = bytesToClosestUnit(selectionState.getImageSize());
|
width: '100%',
|
||||||
const imageName = middleEllipsis(getImageBasename(selectionState), 16);
|
padding: '13px 14px',
|
||||||
const driveTitle = middleEllipsis(getDrivesTitle(selectionState), 16);
|
textAlign: 'center',
|
||||||
const shouldShowFlashingInfos = isFlashing && isWebviewShowing;
|
}}
|
||||||
const lastFlashErrorCode = flashState.getLastFlashErrorCode;
|
>
|
||||||
const progressMessage = messages.progress;
|
<span
|
||||||
|
style={{
|
||||||
return (
|
cursor: 'pointer',
|
||||||
<ThemedProvider style={{ height: '100%' }}>
|
}}
|
||||||
<header
|
|
||||||
id="app-header"
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
padding: '13px 14px',
|
|
||||||
textAlign: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
cursor: 'pointer',
|
|
||||||
}}
|
|
||||||
onClick={() =>
|
|
||||||
openExternal('https://www.balena.io/etcher?ref=etcher_footer')
|
|
||||||
}
|
|
||||||
tabIndex={100}
|
|
||||||
>
|
|
||||||
<SvgIcon
|
|
||||||
paths={['../../assets/etcher.svg']}
|
|
||||||
width="123px"
|
|
||||||
height="22px"
|
|
||||||
></SvgIcon>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
float: 'right',
|
|
||||||
position: 'absolute',
|
|
||||||
right: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
icon={<FontAwesomeIcon icon={faCog} />}
|
|
||||||
color={colors.secondary.background}
|
|
||||||
fontSize={24}
|
|
||||||
style={{ width: '30px' }}
|
|
||||||
plain
|
|
||||||
onClick={() => setHideSettings(false)}
|
|
||||||
tabIndex={5}
|
|
||||||
/>
|
|
||||||
{!settings.get('disableExternalLinks') && (
|
|
||||||
<Button
|
|
||||||
icon={<FontAwesomeIcon icon={faQuestionCircle} />}
|
|
||||||
color={colors.secondary.background}
|
|
||||||
fontSize={24}
|
|
||||||
style={{ width: '30px' }}
|
|
||||||
plain
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
openExternal(
|
openExternal('https://www.balena.io/etcher?ref=etcher_footer')
|
||||||
selectionState.getImageSupportUrl() || DEFAULT_SUPPORT_URL,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
tabIndex={5}
|
tabIndex={100}
|
||||||
|
>
|
||||||
|
<SvgIcon
|
||||||
|
paths={['../../assets/etcher.svg']}
|
||||||
|
width="123px"
|
||||||
|
height="22px"
|
||||||
|
></SvgIcon>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
float: 'right',
|
||||||
|
position: 'absolute',
|
||||||
|
right: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
icon={<FontAwesomeIcon icon={faCog} />}
|
||||||
|
color={colors.secondary.background}
|
||||||
|
fontSize={24}
|
||||||
|
style={{ width: '30px' }}
|
||||||
|
plain
|
||||||
|
onClick={() => this.setState({ hideSettings: false })}
|
||||||
|
tabIndex={5}
|
||||||
|
/>
|
||||||
|
{!settings.get('disableExternalLinks') && (
|
||||||
|
<Button
|
||||||
|
icon={<FontAwesomeIcon icon={faQuestionCircle} />}
|
||||||
|
color={colors.secondary.background}
|
||||||
|
fontSize={24}
|
||||||
|
style={{ width: '30px' }}
|
||||||
|
plain
|
||||||
|
onClick={() =>
|
||||||
|
openExternal(
|
||||||
|
selectionState.getImageSupportUrl() ||
|
||||||
|
'https://github.com/balena-io/etcher/blob/master/SUPPORT.md',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
tabIndex={5}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</header>
|
||||||
|
{this.state.hideSettings ? null : (
|
||||||
|
<SettingsModal
|
||||||
|
toggleModal={(value: boolean) => {
|
||||||
|
this.setState({ hideSettings: !value });
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</span>
|
|
||||||
</header>
|
|
||||||
{hideSettings ? null : (
|
|
||||||
<SettingsModal
|
|
||||||
toggleModal={(value: boolean) => {
|
|
||||||
setHideSettings(!value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="page-main row around-xs" style={{ margin: '110px 50px' }}>
|
|
||||||
<div className="col-xs">
|
|
||||||
<ImageSelector flashing={isFlashing} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-xs">
|
|
||||||
<DriveSelector
|
|
||||||
webviewShowing={isWebviewShowing}
|
|
||||||
disabled={shouldDriveStepBeDisabled}
|
|
||||||
nextStepDisabled={shouldFlashStepBeDisabled}
|
|
||||||
hasDrive={hasDrive}
|
|
||||||
flashing={isFlashing}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isFlashing && (
|
|
||||||
<div
|
<div
|
||||||
className={`featured-project ${
|
className="page-main row around-xs"
|
||||||
isFlashing && isWebviewShowing ? 'fp-visible' : ''
|
style={{ margin: '110px 50px' }}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<FeaturedProject onWebviewShow={setWebviewShowing} />
|
<div className="col-xs">
|
||||||
|
<ImageSelector flashing={this.state.isFlashing} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-xs">
|
||||||
|
<DriveSelector
|
||||||
|
webviewShowing={this.state.isWebviewShowing}
|
||||||
|
disabled={shouldDriveStepBeDisabled}
|
||||||
|
nextStepDisabled={shouldFlashStepBeDisabled}
|
||||||
|
hasDrive={this.state.hasDrive}
|
||||||
|
flashing={this.state.isFlashing}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{this.state.isFlashing && (
|
||||||
|
<div
|
||||||
|
className={`featured-project ${
|
||||||
|
this.state.isFlashing && this.state.isWebviewShowing
|
||||||
|
? 'fp-visible'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<FeaturedProject
|
||||||
|
onWebviewShow={(isWebviewShowing: boolean) => {
|
||||||
|
this.setState({ isWebviewShowing });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<ReducedFlashingInfos
|
||||||
|
imageLogo={this.state.imageLogo}
|
||||||
|
imageName={middleEllipsis(this.state.imageName, 16)}
|
||||||
|
imageSize={bytesToClosestUnit(this.state.imageSize)}
|
||||||
|
driveTitle={middleEllipsis(this.state.driveTitle, 16)}
|
||||||
|
shouldShow={
|
||||||
|
this.state.isFlashing && this.state.isWebviewShowing
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-xs">
|
||||||
|
<Flash
|
||||||
|
goToSuccess={() => this.setState({ current: 'success' })}
|
||||||
|
shouldFlashStepBeDisabled={shouldFlashStepBeDisabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</ThemedProvider>
|
||||||
|
);
|
||||||
<div>
|
} else if (this.state.current === 'success') {
|
||||||
<ReducedFlashingInfos
|
return (
|
||||||
imageLogo={imageLogo}
|
<div className="section-loader isFinish">
|
||||||
imageName={imageName}
|
<FinishPage goToMain={() => this.setState({ current: 'main' })} />
|
||||||
imageSize={imageSize}
|
<SafeWebview src="https://www.balena.io/etcher/success-banner/" />
|
||||||
driveTitle={driveTitle}
|
|
||||||
shouldShow={shouldShowFlashingInfos}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
<div className="col-xs">
|
}
|
||||||
<Flash
|
}
|
||||||
goToSuccess={() => $state.go('success')}
|
}
|
||||||
shouldFlashStepBeDisabled={shouldFlashStepBeDisabled}
|
|
||||||
lastFlashErrorCode={lastFlashErrorCode}
|
|
||||||
progressMessage={progressMessage}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ThemedProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MainPage;
|
export default MainPage;
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019 balena.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This page represents the application main page.
|
|
||||||
*
|
|
||||||
* @module Etcher.Pages.Main
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as angular from 'angular';
|
|
||||||
// @ts-ignore
|
|
||||||
import * as angularRouter from 'angular-ui-router';
|
|
||||||
import { react2angular } from 'react2angular';
|
|
||||||
import MainPage from './MainPage';
|
|
||||||
|
|
||||||
export const MODULE_NAME = 'Etcher.Pages.Main';
|
|
||||||
|
|
||||||
const Main = angular.module(MODULE_NAME, [angularRouter]);
|
|
||||||
|
|
||||||
Main.component('mainPage', react2angular(MainPage, [], ['$state']));
|
|
||||||
|
|
||||||
Main.config(($stateProvider: any) => {
|
|
||||||
$stateProvider.state('main', {
|
|
||||||
url: '/main',
|
|
||||||
template: '<main-page style="width:100%"></main-page>',
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 balena.io
|
* Copyright 2020 balena.io
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -14,7 +14,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[ng-click] {
|
import * as React from 'react';
|
||||||
cursor: pointer;
|
import * as ReactDOM from 'react-dom';
|
||||||
-webkit-app-region: no-drag;
|
|
||||||
}
|
import MainPage from './pages/main/MainPage';
|
||||||
|
|
||||||
|
ReactDOM.render(<MainPage />, document.getElementById('main'));
|
59
npm-shrinkwrap.json
generated
59
npm-shrinkwrap.json
generated
@ -1092,11 +1092,6 @@
|
|||||||
"defer-to-connect": "^1.0.1"
|
"defer-to-connect": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/angular": {
|
|
||||||
"version": "1.6.56",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/angular/-/angular-1.6.56.tgz",
|
|
||||||
"integrity": "sha512-HxtqilvklZ7i6XOaiP7uIJIrFXEVEhfbSY45nfv2DeBRngncI58Y4ZOUMiUkcT8sqgLL1ablmbfylChUg7A3GA=="
|
|
||||||
},
|
|
||||||
"@types/bluebird": {
|
"@types/bluebird": {
|
||||||
"version": "3.5.28",
|
"version": "3.5.28",
|
||||||
"resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.28.tgz",
|
"resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.28.tgz",
|
||||||
@ -1186,14 +1181,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.144.tgz",
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.144.tgz",
|
||||||
"integrity": "sha512-ogI4g9W5qIQQUhXAclq6zhqgqNUr7UlFaqDHbch7WLSLeeM/7d3CRaw7GLajxvyFvhJqw4Rpcz5bhoaYtIx6Tg=="
|
"integrity": "sha512-ogI4g9W5qIQQUhXAclq6zhqgqNUr7UlFaqDHbch7WLSLeeM/7d3CRaw7GLajxvyFvhJqw4Rpcz5bhoaYtIx6Tg=="
|
||||||
},
|
},
|
||||||
"@types/lodash.frompairs": {
|
|
||||||
"version": "4.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/lodash.frompairs/-/lodash.frompairs-4.0.6.tgz",
|
|
||||||
"integrity": "sha512-rwCUf4NMKhXpiVjL/RXP8YOk+rd02/J4tACADEgaMXRVnzDbSSlBMKFZoX/ARmHVLg3Qc98Um4PErGv8FbxU7w==",
|
|
||||||
"requires": {
|
|
||||||
"@types/lodash": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/marked": {
|
"@types/marked": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-0.3.0.tgz",
|
||||||
@ -1597,25 +1584,6 @@
|
|||||||
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
|
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"angular": {
|
|
||||||
"version": "1.7.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/angular/-/angular-1.7.6.tgz",
|
|
||||||
"integrity": "sha512-QELpvuMIe1FTGniAkRz93O6A+di0yu88niDwcdzrSqtUHNtZMgtgFS4f7W/6Gugbuwej8Kyswlmymwdp8iPCWg=="
|
|
||||||
},
|
|
||||||
"angular-mocks": {
|
|
||||||
"version": "1.7.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.7.6.tgz",
|
|
||||||
"integrity": "sha512-t3eQmuAZczdOVdOQj7muCBwH+MBNwd+/FaAsV1SNp+597EQVWABQwxI6KXE0k0ZlyJ5JbtkNIKU8kGAj1znxhw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"angular-ui-router": {
|
|
||||||
"version": "0.4.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/angular-ui-router/-/angular-ui-router-0.4.3.tgz",
|
|
||||||
"integrity": "sha512-EGBG7G7ArFVkJPM+ZIgPKuMYuT16UQrr3zj3BEiXHKwxss867bGt3u7QD9g4BxR+K2qQOSWok6JGvgTWXAko3A==",
|
|
||||||
"requires": {
|
|
||||||
"angular": "^1.0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ansi-align": {
|
"ansi-align": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz",
|
||||||
@ -9081,11 +9049,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
|
||||||
"integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c="
|
"integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c="
|
||||||
},
|
},
|
||||||
"lodash.frompairs": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.frompairs/-/lodash.frompairs-4.0.1.tgz",
|
|
||||||
"integrity": "sha1-vE5SB/onV8E25XNhTpZkUGsrG9I="
|
|
||||||
},
|
|
||||||
"lodash.isequal": {
|
"lodash.isequal": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||||
@ -10115,17 +10078,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
|
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
|
||||||
},
|
},
|
||||||
"ngcomponent": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ngcomponent/-/ngcomponent-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-cGL3iVoqMWTpCfaIwgRKhdaGqiy2Z+CCG0cVfjlBvdqE8saj8xap9B4OTf+qwObxLVZmDTJPDgx3bN6Q/lZ7BQ==",
|
|
||||||
"requires": {
|
|
||||||
"@types/angular": "^1.6.39",
|
|
||||||
"@types/lodash": "^4.14.85",
|
|
||||||
"angular": ">=1.5.0",
|
|
||||||
"lodash": "^4.17.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nice-try": {
|
"nice-try": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||||
@ -11569,17 +11521,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||||
},
|
},
|
||||||
"react2angular": {
|
|
||||||
"version": "4.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/react2angular/-/react2angular-4.0.6.tgz",
|
|
||||||
"integrity": "sha512-MDl2WRoTyu7Gyh4+FAIlmsM2mxIa/DjSz6G/d90L1tK8ZRubqVEayKF6IPyAruC5DMhGDVJ7tlAIcu/gMNDjXg==",
|
|
||||||
"requires": {
|
|
||||||
"@types/lodash.frompairs": "^4.0.5",
|
|
||||||
"angular": ">=1.5",
|
|
||||||
"lodash.frompairs": "^4.0.1",
|
|
||||||
"ngcomponent": "^4.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"read-config-file": {
|
"read-config-file": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-5.0.0.tgz",
|
||||||
|
@ -56,8 +56,6 @@
|
|||||||
"@fortawesome/free-brands-svg-icons": "^5.11.2",
|
"@fortawesome/free-brands-svg-icons": "^5.11.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.11.2",
|
"@fortawesome/free-solid-svg-icons": "^5.11.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.7",
|
"@fortawesome/react-fontawesome": "^0.1.7",
|
||||||
"angular": "1.7.6",
|
|
||||||
"angular-ui-router": "^0.4.2",
|
|
||||||
"bindings": "^1.3.0",
|
"bindings": "^1.3.0",
|
||||||
"bluebird": "^3.5.3",
|
"bluebird": "^3.5.3",
|
||||||
"bootstrap-sass": "^3.3.6",
|
"bootstrap-sass": "^3.3.6",
|
||||||
@ -79,7 +77,6 @@
|
|||||||
"react": "^16.8.5",
|
"react": "^16.8.5",
|
||||||
"react-dom": "^16.8.5",
|
"react-dom": "^16.8.5",
|
||||||
"react-dropzone": "^10.2.1",
|
"react-dropzone": "^10.2.1",
|
||||||
"react2angular": "^4.0.2",
|
|
||||||
"redux": "^3.5.2",
|
"redux": "^3.5.2",
|
||||||
"rendition": "^11.24.0",
|
"rendition": "^11.24.0",
|
||||||
"request": "^2.81.0",
|
"request": "^2.81.0",
|
||||||
@ -98,7 +95,6 @@
|
|||||||
"@babel/preset-env": "^7.6.0",
|
"@babel/preset-env": "^7.6.0",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
"@types/react-dom": "^16.8.4",
|
"@types/react-dom": "^16.8.4",
|
||||||
"angular-mocks": "1.7.6",
|
|
||||||
"babel-loader": "^8.0.4",
|
"babel-loader": "^8.0.4",
|
||||||
"chalk": "^1.1.3",
|
"chalk": "^1.1.3",
|
||||||
"electron": "6.1.4",
|
"electron": "6.1.4",
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const _ = require('lodash')
|
||||||
const m = require('mochainon')
|
const m = require('mochainon')
|
||||||
const ipc = require('node-ipc')
|
const ipc = require('node-ipc')
|
||||||
const angular = require('angular')
|
|
||||||
const Bluebird = require('bluebird')
|
const Bluebird = require('bluebird')
|
||||||
const flashState = require('../../../lib/gui/app/models/flash-state')
|
const flashState = require('../../../lib/gui/app/models/flash-state')
|
||||||
const imageWriter = require('../../../lib/gui/app/modules/image-writer')
|
const imageWriter = require('../../../lib/gui/app/modules/image-writer')
|
||||||
require('angular-mocks')
|
|
||||||
|
|
||||||
describe('Browser: imageWriter', () => {
|
describe('Browser: imageWriter', () => {
|
||||||
describe('.flash()', () => {
|
describe('.flash()', () => {
|
||||||
@ -41,7 +40,7 @@ describe('Browser: imageWriter', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const writing = imageWriter.flash('foo.img', [ '/dev/disk2' ])
|
const writing = imageWriter.flash('foo.img', [ '/dev/disk2' ])
|
||||||
imageWriter.flash('foo.img', [ '/dev/disk2' ]).catch(angular.noop)
|
imageWriter.flash('foo.img', [ '/dev/disk2' ]).catch(_.noop)
|
||||||
writing.finally(() => {
|
writing.finally(() => {
|
||||||
m.chai.expect(this.performWriteStub).to.have.been.calledOnce
|
m.chai.expect(this.performWriteStub).to.have.been.calledOnce
|
||||||
})
|
})
|
||||||
@ -73,13 +72,13 @@ describe('Browser: imageWriter', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should set flashing to false when done', () => {
|
it('should set flashing to false when done', () => {
|
||||||
imageWriter.flash('foo.img', [ '/dev/disk2' ]).catch(angular.noop).finally(() => {
|
imageWriter.flash('foo.img', [ '/dev/disk2' ]).catch(_.noop).finally(() => {
|
||||||
m.chai.expect(flashState.isFlashing()).to.be.false
|
m.chai.expect(flashState.isFlashing()).to.be.false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should set the error code in the flash results', () => {
|
it('should set the error code in the flash results', () => {
|
||||||
imageWriter.flash('foo.img', [ '/dev/disk2' ]).catch(angular.noop).finally(() => {
|
imageWriter.flash('foo.img', [ '/dev/disk2' ]).catch(_.noop).finally(() => {
|
||||||
const flashResults = flashState.getFlashResults()
|
const flashResults = flashState.getFlashResults()
|
||||||
m.chai.expect(flashResults.errorCode).to.equal('FOO')
|
m.chai.expect(flashResults.errorCode).to.equal('FOO')
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user