Reorganize utilities and desktop integration modules (#316)

- Rename `Etcher.Utils.Dropzone` to `Etcher.OS.Dropzone`
- Rename `Etcher.Utils.OpenExternal` to `Etcher.OS.OpenExternal`
- Rename `Etcher.Utils.WindowProgress` to `Etcher.OS.WindowProgress`
- Rename `Etcher.notification` to `Etcher.OS.Notification`
- Rename `Etcher.notifier` to `Etcher.Utils.Notifier`
- Rename `Etcher.path` to `Etcher.Utils.Path`

Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
This commit is contained in:
Juan Cruz Viotti 2016-04-13 13:34:31 -04:00
parent cda27982ef
commit 42db5992fa
22 changed files with 170 additions and 104 deletions

View File

@ -30,30 +30,27 @@ require('angular-ui-router');
require('./browser/models/selection-state');
require('./browser/modules/drive-scanner');
require('./browser/modules/image-writer');
require('./browser/modules/path');
require('./browser/modules/notifier');
require('./browser/modules/analytics');
require('./browser/modules/notification');
require('./browser/components/progress-button/progress-button');
require('./browser/components/drive-selector/drive-selector');
require('./browser/pages/finish/finish');
require('./browser/pages/settings/settings');
require('./browser/utils/window-progress/window-progress');
require('./browser/utils/open-external/open-external');
require('./browser/os/notification/notification');
require('./browser/os/window-progress/window-progress');
require('./browser/os/open-external/open-external');
require('./browser/os/dropzone/dropzone');
require('./browser/utils/if-state/if-state');
require('./browser/utils/dropzone/dropzone');
require('./browser/utils/notifier/notifier');
require('./browser/utils/path/path');
const app = angular.module('Etcher', [
'ui.router',
'ui.bootstrap',
// Etcher modules
'Etcher.path',
'Etcher.drive-scanner',
'Etcher.image-writer',
'Etcher.notifier',
'Etcher.analytics',
'Etcher.notification',
// Models
'Etcher.Models.SelectionState',
@ -67,11 +64,16 @@ const app = angular.module('Etcher', [
'Etcher.Pages.Finish',
'Etcher.Pages.Settings',
// OS
'Etcher.OS.WindowProgress',
'Etcher.OS.OpenExternal',
'Etcher.OS.Dropzone',
'Etcher.OS.Notification',
// Utils
'Etcher.Utils.WindowProgress',
'Etcher.Utils.OpenExternal',
'Etcher.Utils.IfState',
'Etcher.Utils.Dropzone'
'Etcher.Utils.Notifier',
'Etcher.Utils.Path'
]);
app.run(function(AnalyticsService) {
@ -100,8 +102,8 @@ app.controller('AppController', function(
ImageWriterService,
AnalyticsService,
DriveSelectorService,
WindowProgressService,
NotificationService
OSWindowProgressService,
OSNotificationService
) {
let self = this;
this.selection = SelectionStateModel;
@ -128,7 +130,7 @@ app.controller('AppController', function(
NotifierService.subscribe($scope, 'image-writer:state', function(state) {
AnalyticsService.log(`Progress (${state.type}): ${state.progress}% at ${state.speed} MB/s`);
WindowProgressService.set(state.progress);
OSWindowProgressService.set(state.progress);
});
this.scanner.start(2000).on('error', dialog.showError).on('scan', function(drives) {
@ -257,15 +259,15 @@ app.controller('AppController', function(
self.success = success;
if (self.success) {
NotificationService.send('Success!', 'Your flash is complete');
OSNotificationService.send('Success!', 'Your flash is complete');
AnalyticsService.logEvent('Done');
$state.go('success');
} else {
NotificationService.send('Oops!', 'Looks like your flash has failed');
OSNotificationService.send('Oops!', 'Looks like your flash has failed');
AnalyticsService.logEvent('Flash error');
}
})
.catch(dialog.showError)
.finally(WindowProgressService.clear);
.finally(OSWindowProgressService.clear);
};
});

View File

@ -30,10 +30,10 @@ if (window.mocha) {
}
require('../models/settings');
require('./notifier');
require('../utils/notifier/notifier');
const imageWriter = angular.module('Etcher.image-writer', [
'Etcher.Models.Settings',
'Etcher.notifier'
'Etcher.Utils.Notifier'
]);
imageWriter.service('ImageWriterService', function($q, $timeout, SettingsModel, NotifierService) {

View File

@ -31,13 +31,13 @@ const _ = require('lodash');
* @returns {Object} directive
*
* @example
* <div dropzone="doSomething($file)">Drag a file here</div>
* <div os-dropzone="doSomething($file)">Drag a file here</div>
*/
module.exports = function($timeout) {
return {
restrict: 'A',
scope: {
dropzone: '&'
osDropzone: '&'
},
link: function(scope, element) {
const domElement = element[0];
@ -55,7 +55,7 @@ module.exports = function($timeout) {
// Safely bring this to the word of Angular
$timeout(function() {
scope.dropzone({
scope.osDropzone({
// Pass the filename as a named
// parameter called `$file`

View File

@ -17,9 +17,9 @@
'use strict';
/**
* @module Etcher.Utils.Dropzone
* @module Etcher.OS.Dropzone
*/
const angular = require('angular');
const Dropzone = angular.module('Etcher.Utils.Dropzone', []);
Dropzone.directive('dropzone', require('./directives/dropzone'));
const OSDropzone = angular.module('Etcher.OS.Dropzone', []);
OSDropzone.directive('osDropzone', require('./directives/dropzone'));

View File

@ -0,0 +1,28 @@
/*
* 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.Notification
*
* The purpose of this module is to provide an easy way
* to send desktop notifications.
*/
const angular = require('angular');
const OSNotification = angular.module('Etcher.OS.Notification', []);
OSNotification.service('OSNotificationService', require('./services/notification'));

View File

@ -16,16 +16,9 @@
'use strict';
/**
* @module Etcher.notification
*/
const angular = require('angular');
const electron = require('electron');
const app = electron.remote.app;
const notification = angular.module('Etcher.notification', []);
notification.service('NotificationService', function() {
module.exports = function() {
/**
* @summary Send a notification
@ -42,7 +35,7 @@ notification.service('NotificationService', function() {
* @returns {Object} HTML5 notification object
*
* @example
* const notification = NotificationService.send('Hello', 'Foo Bar Bar');
* const notification = OSNotificationService.send('Hello', 'Foo Bar Bar');
* notification.onclick = function() {
* console.log('The notification has been clicked');
* };
@ -50,8 +43,8 @@ notification.service('NotificationService', function() {
this.send = function(title, body) {
// `app.dock` is only defined in OS X
if (app.dock) {
app.dock.bounce();
if (electron.remote.app.dock) {
electron.remote.app.dock.bounce();
}
return new Notification(title, {
@ -59,4 +52,4 @@ notification.service('NotificationService', function() {
});
};
});
};

View File

@ -27,14 +27,14 @@ const nodeOpen = require('open');
*
* Example:
*
* <button open-external="https://resin.io">Resin.io</button>
* <button os-open-external="https://resin.io">Resin.io</button>
*/
module.exports = function() {
return {
restrict: 'A',
scope: {
openExternal: '@'
osOpenExternal: '@'
},
link: function(scope, element) {
@ -59,10 +59,10 @@ module.exports = function() {
//
// See https://github.com/electron/electron/issues/5039
if (os.platform() === 'linux') {
return nodeOpen(scope.openExternal);
return nodeOpen(scope.osOpenExternal);
}
shell.openExternal(scope.openExternal);
shell.openExternal(scope.osOpenExternal);
});
}
};

View File

@ -17,9 +17,9 @@
'use strict';
/**
* @module Etcher.Utils.OpenExternal
* @module Etcher.OS.OpenExternal
*/
const angular = require('angular');
const OpenExternal = angular.module('Etcher.Utils.OpenExternal', []);
OpenExternal.directive('openExternal', require('./directives/open-external'));
const OSOpenExternal = angular.module('Etcher.OS.OpenExternal', []);
OSOpenExternal.directive('osOpenExternal', require('./directives/open-external'));

View File

@ -45,7 +45,7 @@ module.exports = function() {
* @param {Number} percentage - percentage
*
* @example
* WindowProgressService.set(85);
* OSWindowProgressService.set(85);
*/
this.set = function(percentage) {
if (percentage > 100 || percentage < 0) {
@ -61,7 +61,7 @@ module.exports = function() {
* @public
*
* @example
* WindowProgressService.clear();
* OSWindowProgressService.clear();
*/
this.clear = function() {

View File

@ -17,7 +17,7 @@
'use strict';
/**
* @module Etcher.Utils.WindowProgress
* @module Etcher.OS.WindowProgress
*
* The purpose of this module is to provide an easy way
* to interact with the operating system's window progress
@ -25,5 +25,5 @@
*/
const angular = require('angular');
const WindowProgress = angular.module('Etcher.Utils.WindowProgress', []);
WindowProgress.service('WindowProgressService', require('./services/window-progress'));
const OSWindowProgress = angular.module('Etcher.OS.WindowProgress', []);
OSWindowProgress.service('OSWindowProgressService', require('./services/window-progress'));

View File

@ -0,0 +1,28 @@
/*
* 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.Utils.Notifier
*
* The purpose of this module is to provide a safe way
* to emit and receive events from the $rootScope.
*/
const angular = require('angular');
const Notifier = angular.module('Etcher.Utils.Notifier', []);
Notifier.service('NotifierService', require('./services/notifier'));

View File

@ -16,19 +16,12 @@
'use strict';
/**
* @module Etcher.notifier
*/
const angular = require('angular');
const notifier = angular.module('Etcher.notifier', []);
/*
* Based on:
* http://www.codelord.net/2015/05/04/angularjs-notifying-about-changes-from-services-to-controllers/
*/
notifier.service('NotifierService', function($rootScope) {
module.exports = function($rootScope) {
/**
* @summary Safely subscribe to an event
@ -71,4 +64,4 @@ notifier.service('NotifierService', function($rootScope) {
$rootScope.$emit(name, data);
};
});
};

View File

@ -16,16 +16,9 @@
'use strict';
/**
* @module Etcher.path
*/
const angular = require('angular');
const path = require('path');
const pathModule = angular.module('Etcher.path', []);
pathModule.filter('basename', function() {
module.exports = function() {
/**
* @summary Get the basename of a path
@ -39,4 +32,5 @@ pathModule.filter('basename', function() {
* {{ '/foo/bar/baz' | basename }}
*/
return path.basename;
});
};

View File

@ -0,0 +1,28 @@
/*
* 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.Utils.Path
*
* The purpose of this module is to provide utilities
* to work with file paths.
*/
const angular = require('angular');
const Path = angular.module('Etcher.Utils.Path', []);
Path.filter('basename', require('./filters/basename'));

View File

@ -21,7 +21,7 @@
</head>
<body ng-app="Etcher" style="display: none">
<header class="section-header">
<button class="btn btn-link" open-external="https://github.com/resin-io/etcher/blob/master/SUPPORT.md">
<button class="btn btn-link" os-open-external="https://github.com/resin-io/etcher/blob/master/SUPPORT.md">
<span class="glyphicon glyphicon-question-sign"></span> Need Help?
</button>
@ -40,16 +40,16 @@
<hero-icon path="../assets/images/etcher.svg"
width="83px"
height="13px"
open-external="https://etcher.io"></hero-icon>
os-open-external="https://etcher.io"></hero-icon>
<span class="caption">
IS <span open-external="https://github.com/resin-io/etcher">AN OPEN SOURCE PROJECT</span> BY
IS <span os-open-external="https://github.com/resin-io/etcher">AN OPEN SOURCE PROJECT</span> BY
</span>
<hero-icon path="../assets/images/resin.svg"
width="79px"
height="23px"
open-external="https://resin.io"></hero-icon>
os-open-external="https://resin.io"></hero-icon>
</footer>
</body>
</html>

View File

@ -1,6 +1,6 @@
<div class="row around-xs">
<div class="col-xs">
<div class="box text-center" dropzone="app.selectImage($file)">
<div class="box text-center" os-dropzone="app.selectImage($file)">
<hero-icon path="../assets/images/image.svg" label="SELECT IMAGE"></hero-icon>
<span class="badge space-top-medium">1</span>

View File

@ -99,7 +99,7 @@ body {
margin: 0 13px;
}
[open-external]:hover {
[os-open-external]:hover {
color: lighten($color-disabled, 5%);
text-decoration: underline;
}

View File

@ -19,13 +19,13 @@
const m = require('mochainon');
const angular = require('angular');
require('angular-mocks');
require('../../../lib/browser/utils/dropzone/dropzone');
require('../../../lib/browser/os/dropzone/dropzone');
describe('Browser: Dropzone', function() {
describe('Browser: OSDropzone', function() {
beforeEach(angular.mock.module('Etcher.Utils.Dropzone'));
beforeEach(angular.mock.module('Etcher.OS.Dropzone'));
describe('dropzone', function() {
describe('osDropzone', function() {
let $compile;
let $rootScope;
@ -43,7 +43,7 @@ describe('Browser: Dropzone', function() {
done();
};
const element = $compile('<div dropzone="onDropZone($file)">Drop a file here</div>')($rootScope);
const element = $compile('<div os-dropzone="onDropZone($file)">Drop a file here</div>')($rootScope);
$rootScope.$digest();
element[0].ondrop({
@ -67,7 +67,7 @@ describe('Browser: Dropzone', function() {
done();
};
const element = $compile('<div dropzone="onDropZone()">Drop a file here</div>')($rootScope);
const element = $compile('<div os-dropzone="onDropZone()">Drop a file here</div>')($rootScope);
$rootScope.$digest();
element[0].ondrop({

View File

@ -22,13 +22,13 @@ const angular = require('angular');
const electron = require('electron');
const shell = electron.remote.require('shell');
require('angular-mocks');
require('../../../lib/browser/utils/open-external/open-external');
require('../../../lib/browser/os/open-external/open-external');
describe('Browser: OpenExternal', function() {
describe('Browser: OSOpenExternal', function() {
beforeEach(angular.mock.module('Etcher.Utils.OpenExternal'));
beforeEach(angular.mock.module('Etcher.OS.OpenExternal'));
describe('openExternal', function() {
describe('osOpenExternal', function() {
let $compile;
let $rootScope;
@ -39,7 +39,7 @@ describe('Browser: OpenExternal', function() {
}));
it('should set the element cursor to pointer', function() {
const element = $compile('<span open-external="https://resin.io">Resin.io</span>')($rootScope);
const element = $compile('<span os-open-external="https://resin.io">Resin.io</span>')($rootScope);
$rootScope.$digest();
m.chai.expect(element.css('cursor')).to.equal('pointer');
});
@ -57,7 +57,7 @@ describe('Browser: OpenExternal', function() {
it('should call Electron shell.openExternal with the attribute value', function() {
const shellExternalStub = m.sinon.stub(shell, 'openExternal');
const element = $compile('<span open-external="https://resin.io">Resin.io</span>')($rootScope);
const element = $compile('<span os-open-external="https://resin.io">Resin.io</span>')($rootScope);
element.triggerHandler('click');
$rootScope.$digest();
m.chai.expect(shellExternalStub).to.have.been.calledWith('https://resin.io');

View File

@ -19,18 +19,18 @@
const m = require('mochainon');
const angular = require('angular');
require('angular-mocks');
require('../../../lib/browser/utils/window-progress/window-progress');
require('../../../lib/browser/os/window-progress/window-progress');
describe('Browser: WindowProgress', function() {
describe('Browser: OSWindowProgress', function() {
beforeEach(angular.mock.module('Etcher.Utils.WindowProgress'));
beforeEach(angular.mock.module('Etcher.OS.WindowProgress'));
describe('WindowProgressService', function() {
describe('OSWindowProgressService', function() {
let WindowProgressService;
let OSWindowProgressService;
beforeEach(angular.mock.inject(function(_WindowProgressService_) {
WindowProgressService = _WindowProgressService_;
beforeEach(angular.mock.inject(function(_OSWindowProgressService_) {
OSWindowProgressService = _OSWindowProgressService_;
}));
describe('given a stubbed current window', function() {
@ -38,7 +38,7 @@ describe('Browser: WindowProgress', function() {
beforeEach(function() {
this.setProgressBarSpy = m.sinon.spy();
WindowProgressService.currentWindow = {
OSWindowProgressService.currentWindow = {
setProgressBar: this.setProgressBarSpy
};
});
@ -46,29 +46,29 @@ describe('Browser: WindowProgress', function() {
describe('.set()', function() {
it('should translate 0-100 percentages to 0-1 ranges', function() {
WindowProgressService.set(85);
OSWindowProgressService.set(85);
m.chai.expect(this.setProgressBarSpy).to.have.been.calledWith(0.85);
});
it('should set 0 given 0', function() {
WindowProgressService.set(0);
OSWindowProgressService.set(0);
m.chai.expect(this.setProgressBarSpy).to.have.been.calledWith(0);
});
it('should set 1 given 100', function() {
WindowProgressService.set(100);
OSWindowProgressService.set(100);
m.chai.expect(this.setProgressBarSpy).to.have.been.calledWith(1);
});
it('should throw if given a percentage higher than 100', function() {
m.chai.expect(function() {
WindowProgressService.set(101);
OSWindowProgressService.set(101);
}).to.throw('Invalid window progress percentage: 101');
});
it('should throw if given a percentage less than 0', function() {
m.chai.expect(function() {
WindowProgressService.set(-1);
OSWindowProgressService.set(-1);
}).to.throw('Invalid window progress percentage: -1');
});
@ -77,7 +77,7 @@ describe('Browser: WindowProgress', function() {
describe('.clear()', function() {
it('should set -1', function() {
WindowProgressService.clear();
OSWindowProgressService.clear();
m.chai.expect(this.setProgressBarSpy).to.have.been.calledWith(-1);
});

View File

@ -3,11 +3,11 @@
const m = require('mochainon');
const angular = require('angular');
require('angular-mocks');
require('../../../lib/browser/modules/notifier');
require('../../../lib/browser/utils/notifier/notifier');
describe('Browser: Notifier', function() {
beforeEach(angular.mock.module('Etcher.notifier'));
beforeEach(angular.mock.module('Etcher.Utils.Notifier'));
describe('NotifierService', function() {

View File

@ -4,11 +4,11 @@ const m = require('mochainon');
const angular = require('angular');
const os = require('os');
require('angular-mocks');
require('../../../lib/browser/modules/path');
require('../../../lib/browser/utils/path/path');
describe('Browser: Path', function() {
beforeEach(angular.mock.module('Etcher.path'));
beforeEach(angular.mock.module('Etcher.Utils.Path'));
describe('BasenameFilter', function() {