Move burn state to ImageWriterService

Previously, the burn state lived in the controller, however if the user
moved to another page (the settings page for example) and then returned,
the progress state would be lost, leading to a broken progress bar.

Fixes: https://github.com/resin-io/etcher/issues/190
This commit is contained in:
Juan Cruz Viotti 2016-03-10 11:46:41 -04:00
parent 6367dd8a57
commit 793001e133
4 changed files with 81 additions and 26 deletions

View File

@ -35,6 +35,7 @@ require('./browser/modules/settings');
require('./browser/modules/drive-scanner'); require('./browser/modules/drive-scanner');
require('./browser/modules/image-writer'); require('./browser/modules/image-writer');
require('./browser/modules/path'); require('./browser/modules/path');
require('./browser/modules/notifier');
require('./browser/modules/analytics'); require('./browser/modules/analytics');
const app = angular.module('Etcher', [ const app = angular.module('Etcher', [
@ -47,6 +48,7 @@ const app = angular.module('Etcher', [
'Etcher.settings', 'Etcher.settings',
'Etcher.drive-scanner', 'Etcher.drive-scanner',
'Etcher.image-writer', 'Etcher.image-writer',
'Etcher.notifier',
'Etcher.analytics' 'Etcher.analytics'
]); ]);
@ -74,6 +76,8 @@ app.config(function($stateProvider, $urlRouterProvider) {
app.controller('AppController', function( app.controller('AppController', function(
$q, $q,
$state, $state,
$scope,
NotifierService,
DriveScannerService, DriveScannerService,
SelectionStateService, SelectionStateService,
ImageWriterService, ImageWriterService,
@ -87,12 +91,16 @@ app.controller('AppController', function(
AnalyticsService.logEvent('Restart'); AnalyticsService.logEvent('Restart');
if (!this.writer.isBurning()) { if (!this.writer.isBurning()) {
this.state = { this.writer.resetState();
progress: 0,
percentage: 0
};
} }
NotifierService.subscribe($scope, 'image-writer:state', function(state) {
AnalyticsService.log(`Progress: ${state.progress}% at ${state.speed} MB/s`);
// Show progress inline in operating system task bar
currentWindow.setProgressBar(state.progress / 100);
});
this.scanner.start(2000).on('scan', function(drives) { this.scanner.start(2000).on('scan', function(drives) {
// Notice we only autoselect the drive if there is an image, // Notice we only autoselect the drive if there is an image,
@ -183,14 +191,7 @@ app.controller('AppController', function(
device: drive.device device: drive.device
}); });
return self.writer.burn(image, drive, function(state) { return self.writer.burn(image, drive).then(function() {
self.state = state;
AnalyticsService.log(`Progress: ${self.state.progress}% at ${self.state.speed} MB/s`);
// Show progress inline in operating system task bar
currentWindow.setProgressBar(self.state.progress / 100);
}).then(function() {
AnalyticsService.logEvent('Done'); AnalyticsService.logEvent('Done');
$state.go('success'); $state.go('success');
}).catch(dialog.showError).finally(function() { }).catch(dialog.showError).finally(function() {

View File

@ -30,14 +30,39 @@ if (window.mocha) {
} }
require('./settings'); require('./settings');
require('./notifier');
const imageWriter = angular.module('Etcher.image-writer', [ const imageWriter = angular.module('Etcher.image-writer', [
'Etcher.settings' 'Etcher.settings',
'Etcher.notifier'
]); ]);
imageWriter.service('ImageWriterService', function($q, $timeout, SettingsService) { imageWriter.service('ImageWriterService', function($q, $timeout, SettingsService, NotifierService) {
let self = this; let self = this;
let burning = false; let burning = false;
/**
* @summary Reset burn state
* @function
* @public
*
* @example
* ImageWriterService.resetState();
*/
this.resetState = function() {
self.state = {
progress: 0,
speed: 0
};
};
/**
* @summary Burn progress state
* @type Object
* @public
*/
this.state = {};
this.resetState();
/** /**
* @summary Check if currently burning * @summary Check if currently burning
* @function * @function
@ -102,11 +127,10 @@ imageWriter.service('ImageWriterService', function($q, $timeout, SettingsService
* @public * @public
* *
* @description * @description
* This function will update `state.progress` with the current writing percentage. * This function will update `ImageWriterService.state` with the current writing state.
* *
* @param {String} image - image path * @param {String} image - image path
* @param {Object} drive - drive * @param {Object} drive - drive
* @param {Function} onProgress - in progress callback (state)
* *
* @returns {Promise} * @returns {Promise}
* *
@ -117,7 +141,7 @@ imageWriter.service('ImageWriterService', function($q, $timeout, SettingsService
* console.log('Write completed!'); * console.log('Write completed!');
* }); * });
*/ */
this.burn = function(image, drive, onProgress) { this.burn = function(image, drive) {
if (self.isBurning()) { if (self.isBurning()) {
return $q.reject(new Error('There is already a burn in progress')); return $q.reject(new Error('There is already a burn in progress'));
} }
@ -129,13 +153,14 @@ imageWriter.service('ImageWriterService', function($q, $timeout, SettingsService
// Safely bring the state to the world of Angular // Safely bring the state to the world of Angular
$timeout(function() { $timeout(function() {
return onProgress({ self.state = {
progress: Math.floor(state.percentage), progress: Math.floor(state.percentage),
// Transform bytes to megabytes preserving only two decimal places // Transform bytes to megabytes preserving only two decimal places
speed: Math.floor(state.speed / 1e+6 * 100) / 100 || 0 speed: Math.floor(state.speed / 1e+6 * 100) / 100 || 0
};
}); NotifierService.emit('image-writer:state', self.state);
}); });
}).finally(function() { }).finally(function() {

View File

@ -56,17 +56,17 @@
<hero-badge class="block space-vertical-medium" ng-disabled="!app.selection.hasImage() || !app.selection.hasDrive()">3</hero-badge> <hero-badge class="block space-vertical-medium" ng-disabled="!app.selection.hasImage() || !app.selection.hasDrive()">3</hero-badge>
<div class="space-vertical-large"> <div class="space-vertical-large">
<hero-progress-button percentage="{{ app.state.progress }}" ng-attr-active="{{ app.writer.isBurning() }}" <hero-progress-button percentage="{{ app.writer.state.progress }}" ng-attr-active="{{ app.writer.isBurning() }}"
ng-click="app.burn(app.selection.getImage(), app.selection.getDrive())" ng-click="app.burn(app.selection.getImage(), app.selection.getDrive())"
ng-disabled="!app.selection.hasImage() || !app.selection.hasDrive()"> ng-disabled="!app.selection.hasImage() || !app.selection.hasDrive()">
<span ng-show="app.state.progress == 100 && app.writer.isBurning()">Finishing...</span> <span ng-show="app.writer.state.progress == 100 && app.writer.isBurning()">Finishing...</span>
<span ng-show="app.state.progress == 0 && !app.writer.isBurning()">Burn!</span> <span ng-show="app.writer.state.progress == 0 && !app.writer.isBurning()">Burn!</span>
<span ng-show="app.state.progress == 0 && app.writer.isBurning() && !app.state.speed">Starting...</span> <span ng-show="app.writer.state.progress == 0 && app.writer.isBurning() && !app.writer.state.speed">Starting...</span>
<span ng-show="app.state.speed && app.state.progress != 100" <span ng-show="app.writer.state.speed && app.writer.state.progress != 100"
ng-bind="app.state.progress + '% '"></span> ng-bind="app.writer.state.progress + '% '"></span>
</hero-progress-button> </hero-progress-button>
<p class="step-footer" ng-bind="app.state.speed.toFixed(2) + ' MB/s'" ng-show="app.state.speed && app.state.progress != 100"></p> <p class="step-footer" ng-bind="app.writer.state.speed.toFixed(2) + ' MB/s'" ng-show="app.writer.state.speed && app.writer.state.progress != 100"></p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -23,6 +23,35 @@ describe('Browser: ImageWriter', function() {
ImageWriterService = _ImageWriterService_; ImageWriterService = _ImageWriterService_;
})); }));
describe('.state', function() {
it('should be reset by default', function() {
m.chai.expect(ImageWriterService.state).to.deep.equal({
progress: 0,
speed: 0
});
});
});
describe('.resetState()', function() {
it('should be able to reset the state', function() {
ImageWriterService.state = {
progress: 50,
speed: 3
};
ImageWriterService.resetState();
m.chai.expect(ImageWriterService.state).to.deep.equal({
progress: 0,
speed: 0
});
});
});
describe('.isBurning()', function() { describe('.isBurning()', function() {
it('should return false by default', function() { it('should return false by default', function() {