mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-24 19:56:37 +00:00
Implement Etcher.OS.Dialog module (#381)
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
This commit is contained in:
parent
d373d32472
commit
ad758cf391
@ -22,8 +22,6 @@
|
|||||||
|
|
||||||
var angular = require('angular');
|
var angular = require('angular');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const electron = require('electron');
|
|
||||||
const dialog = electron.remote.require('./gui/dialog');
|
|
||||||
|
|
||||||
const app = angular.module('Etcher', [
|
const app = angular.module('Etcher', [
|
||||||
require('angular-ui-router'),
|
require('angular-ui-router'),
|
||||||
@ -51,6 +49,7 @@ const app = angular.module('Etcher', [
|
|||||||
require('./os/window-progress/window-progress'),
|
require('./os/window-progress/window-progress'),
|
||||||
require('./os/open-external/open-external'),
|
require('./os/open-external/open-external'),
|
||||||
require('./os/dropzone/dropzone'),
|
require('./os/dropzone/dropzone'),
|
||||||
|
require('./os/dialog/dialog'),
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
require('./utils/if-state/if-state'),
|
require('./utils/if-state/if-state'),
|
||||||
@ -76,7 +75,6 @@ app.config(function($stateProvider, $urlRouterProvider) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.controller('AppController', function(
|
app.controller('AppController', function(
|
||||||
$q,
|
|
||||||
$state,
|
$state,
|
||||||
$scope,
|
$scope,
|
||||||
NotifierService,
|
NotifierService,
|
||||||
@ -87,7 +85,8 @@ app.controller('AppController', function(
|
|||||||
AnalyticsService,
|
AnalyticsService,
|
||||||
DriveSelectorService,
|
DriveSelectorService,
|
||||||
OSWindowProgressService,
|
OSWindowProgressService,
|
||||||
OSNotificationService
|
OSNotificationService,
|
||||||
|
OSDialogService
|
||||||
) {
|
) {
|
||||||
let self = this;
|
let self = this;
|
||||||
this.selection = SelectionStateModel;
|
this.selection = SelectionStateModel;
|
||||||
@ -117,7 +116,7 @@ app.controller('AppController', function(
|
|||||||
OSWindowProgressService.set(state.progress);
|
OSWindowProgressService.set(state.progress);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.scanner.start(2000).on('error', dialog.showError).on('scan', function(drives) {
|
this.scanner.start(2000).on('error', OSDialogService.showError).on('scan', function(drives) {
|
||||||
|
|
||||||
// Cover the case where you select a drive, but then eject it.
|
// Cover the case where you select a drive, but then eject it.
|
||||||
if (self.selection.hasDrive() && !_.find(drives, self.selection.isCurrentDrive)) {
|
if (self.selection.hasDrive() && !_.find(drives, self.selection.isCurrentDrive)) {
|
||||||
@ -167,7 +166,7 @@ app.controller('AppController', function(
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.openImageSelector = function() {
|
this.openImageSelector = function() {
|
||||||
return $q.when(dialog.selectImage()).then(function(image) {
|
return OSDialogService.selectImage().then(function(image) {
|
||||||
|
|
||||||
// Avoid analytics and selection state changes
|
// Avoid analytics and selection state changes
|
||||||
// if no file was resolved from the dialog.
|
// if no file was resolved from the dialog.
|
||||||
@ -260,7 +259,7 @@ app.controller('AppController', function(
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.writer.resetState();
|
self.writer.resetState();
|
||||||
dialog.showError(error);
|
OSDialogService.showError(error);
|
||||||
})
|
})
|
||||||
.finally(OSWindowProgressService.clear);
|
.finally(OSWindowProgressService.clear);
|
||||||
};
|
};
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 electron = require('electron');
|
|
||||||
const Bluebird = require('bluebird');
|
|
||||||
const zipImage = require('resin-zip-image');
|
|
||||||
const packageJSON = require('../../package.json');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Open an image selection dialog
|
|
||||||
* @function
|
|
||||||
* @public
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Notice that by image, we mean *.img/*.iso/*.zip files.
|
|
||||||
*
|
|
||||||
* If the user selects an invalid zip image, an error alert
|
|
||||||
* is shown, and the promise resolves `undefined`.
|
|
||||||
*
|
|
||||||
* @fulfil {String} - selected image
|
|
||||||
* @returns {Promise};
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* dialog.selectImage().then(function(image) {
|
|
||||||
* console.log('The selected image is', image);
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
exports.selectImage = function() {
|
|
||||||
return new Bluebird(function(resolve) {
|
|
||||||
electron.dialog.showOpenDialog({
|
|
||||||
properties: [ 'openFile' ],
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
name: 'IMG/ISO/ZIP',
|
|
||||||
extensions: [
|
|
||||||
'zip',
|
|
||||||
'img',
|
|
||||||
'iso'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}, function(files) {
|
|
||||||
return resolve(files || []);
|
|
||||||
});
|
|
||||||
}).get(0).then(function(file) {
|
|
||||||
if (file && zipImage.isZip(file) && !zipImage.isValidZipImage(file)) {
|
|
||||||
electron.dialog.showErrorBox(
|
|
||||||
'Invalid zip image',
|
|
||||||
`${packageJSON.displayName} can only open Zip archives that contain exactly one image file inside.`
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return file;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Show error dialog for an Error instance
|
|
||||||
* @function
|
|
||||||
* @public
|
|
||||||
*
|
|
||||||
* @param {Error} error - error
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* dialog.showError(new Error('Foo Bar'));
|
|
||||||
*/
|
|
||||||
exports.showError = function(error) {
|
|
||||||
error = error || {};
|
|
||||||
electron.dialog.showErrorBox(error.message || 'An error ocurred!', error.stack || '');
|
|
||||||
throw error;
|
|
||||||
};
|
|
31
lib/gui/os/dialog/dialog.js
Normal file
31
lib/gui/os/dialog/dialog.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module Etcher.OS.Dialog
|
||||||
|
*
|
||||||
|
* The purpose of this module is to provide an easy way
|
||||||
|
* to interact with OS dialogs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const angular = require('angular');
|
||||||
|
const MODULE_NAME = 'Etcher.OS.Dialog';
|
||||||
|
const OSDialog = angular.module(MODULE_NAME, []);
|
||||||
|
OSDialog.service('OSDialogService', require('./services/dialog'));
|
||||||
|
|
||||||
|
module.exports = MODULE_NAME;
|
84
lib/gui/os/dialog/services/dialog.js
Normal file
84
lib/gui/os/dialog/services/dialog.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* 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 _ = require('lodash');
|
||||||
|
const electron = require('electron');
|
||||||
|
const imageStream = require('etcher-image-stream');
|
||||||
|
|
||||||
|
module.exports = function($q) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Open an image selection dialog
|
||||||
|
* @function
|
||||||
|
* @public
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Notice that by image, we mean *.img/*.iso/*.zip/etc files.
|
||||||
|
*
|
||||||
|
* @fulfil {String} - selected image
|
||||||
|
* @returns {Promise};
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* OSDialogService.selectImage().then(function(image) {
|
||||||
|
* console.log('The selected image is', image);
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
this.selectImage = function() {
|
||||||
|
return $q(function(resolve) {
|
||||||
|
electron.remote.dialog.showOpenDialog({
|
||||||
|
properties: [
|
||||||
|
'openFile'
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: 'OS Images',
|
||||||
|
extensions: imageStream.supportedFileTypes
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, function(files) {
|
||||||
|
|
||||||
|
// `_.first` is smart enough to not throw and return `undefined`
|
||||||
|
// if we pass it an `undefined` value (e.g: when the selection
|
||||||
|
// dialog was cancelled).
|
||||||
|
return resolve(_.first(files));
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Show error dialog for an Error instance
|
||||||
|
* @function
|
||||||
|
* @public
|
||||||
|
*
|
||||||
|
* @param {Error} error - error
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* OSDialogService.showError(new Error('Foo Bar'));
|
||||||
|
*/
|
||||||
|
this.showError = function(error) {
|
||||||
|
error = error || {};
|
||||||
|
electron.remote.dialog.showErrorBox(error.message || 'An error ocurred!', error.stack || '');
|
||||||
|
|
||||||
|
// Also throw it so it gets displayed in DevTools
|
||||||
|
// and its reported by TrackJS.
|
||||||
|
throw error;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
@ -1,49 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const m = require('mochainon');
|
|
||||||
const electron = require('electron');
|
|
||||||
const dialog = require('../../lib/gui/dialog');
|
|
||||||
|
|
||||||
describe('Dialog:', function() {
|
|
||||||
|
|
||||||
describe('.selectImage()', function() {
|
|
||||||
|
|
||||||
describe('given the user does not select anything', function() {
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
this.showOpenDialogStub = m.sinon.stub(electron.dialog, 'showOpenDialog');
|
|
||||||
this.showOpenDialogStub.yields(undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
this.showOpenDialogStub.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should eventually be undefined', function() {
|
|
||||||
const promise = dialog.selectImage();
|
|
||||||
m.chai.expect(promise).to.eventually.be.undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('given the users performs a selection', function() {
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
this.showOpenDialogStub = m.sinon.stub(electron.dialog, 'showOpenDialog');
|
|
||||||
this.showOpenDialogStub.yields([ 'foo/bar' ]);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
this.showOpenDialogStub.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should eventually equal the file', function() {
|
|
||||||
const promise = dialog.selectImage();
|
|
||||||
m.chai.expect(promise).to.eventually.equal('foo/bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
Loading…
x
Reference in New Issue
Block a user