Skip to content

Commit

Permalink
Merge pull request #57 from ekonstantinidis/native-notifications
Browse files Browse the repository at this point in the history
Native Notifications
  • Loading branch information
ekonstantinidis committed Jun 14, 2015
2 parents 5e78c37 + eca42dd commit ec15749
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 39 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
"watch": "grunt build && npm build && npm run watch-js | grunt watch",
"start": "electron .",
"dist": "rm -rf Gitify.app/ && electron-packager . Gitify --platform=darwin --arch=x64 --version=0.27.2 --icon=images/app-icon.icns --prune --ignore=src",
"test": "jsxhint --reporter node_modules/jshint-stylish/stylish.js 'src/**/*.js', 'index.js' --exclude 'Gruntfile.js' && jscs 'src/js/' && jest",
"coveralls": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js"
"test": "jsxhint --reporter node_modules/jshint-stylish/stylish.js 'src/**/*.js', 'index.js' --exclude 'Gruntfile.js' && jscs 'src/js/' && jest"
},
"jshintConfig": {
"browserify": true,
Expand Down Expand Up @@ -76,7 +75,8 @@
"src/js/stores/auth.js": true,
"src/js/stores/notifications.js": true,
"src/js/stores/search.js": true,
"src/js/stores/settings.js": true
"src/js/stores/settings.js": true,
"src/js/stores/sound-notification.js": true
},
"unmockedModulePathPatterns": [
"node_modules/react",
Expand Down
3 changes: 3 additions & 0 deletions src/js/__tests__/components/notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ describe('Test for Notification Component', function () {
item: false,
getItem: function () {
return this.item;
},
setItem: function (item) {
this.item = item;
}
};

Expand Down
3 changes: 3 additions & 0 deletions src/js/__tests__/components/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ describe('Test for Notifications Component', function () {
item: false,
getItem: function () {
return this.item;
},
setItem: function (item) {
this.item = item;
}
};

Expand Down
3 changes: 3 additions & 0 deletions src/js/__tests__/components/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ describe('Test for Settings Component', function () {
item: false,
getItem: function () {
return this.item;
},
setItem: function (item) {
this.item = item;
}
};

Expand Down
13 changes: 11 additions & 2 deletions src/js/__tests__/stores/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,26 @@ describe('Tests for NotificationsStore', function () {
item: false,
getItem: function () {
return this.item;
},
setItem: function (item) {
this.item = item;
}
};

// Mock Audio
window.Audio = function (src) {
console.log('Loading Audio: ' + src);
window.Audio = function () {
return {
play: function () {}
};
};

// Mock Notifications
window.Notification = function () {
return {
onClick: function () {}
};
};

Actions = require('../../actions/actions.js');
apiRequests = require('../../utils/api-requests.js');
NotificationsStore = require('../../stores/notifications.js');
Expand Down
77 changes: 77 additions & 0 deletions src/js/__tests__/stores/sound-notification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*global jest, describe, it, expect, spyOn, beforeEach */

'use strict';

jest.dontMock('reflux');
jest.dontMock('../../stores/sound-notification.js');
jest.dontMock('../../utils/api-requests.js');
jest.dontMock('../../actions/actions.js');

describe('Tests for SoundNotificationStore', function () {

var SoundNotificationStore, Actions;

beforeEach(function () {

// Mock Electron's window.require
window.require = function () {
return {
sendChannel: function () {
return;
}
};
};

// Mock localStorage
window.localStorage = {
item: false,
getItem: function () {
return this.item;
},
setItem: function (item) {
this.item = item;
}
};

// Mock Audio
window.Audio = function () {
return {
play: function () {}
};
};

// Mock Notifications
window.Notification = function () {
return {
onClick: function () {}
};
};

Actions = require('../../actions/actions.js');
SoundNotificationStore = require('../../stores/sound-notification.js');
});

it('should get a payload and check if it should play sound & show notification.', function () {

spyOn(SoundNotificationStore, 'showNotification');

var payload = [{
'id': '1',
'repository': {
'id': 1296269,
'full_name': 'octocat/Hello-World',
'description': 'This your first repo!'
},
'subject': {
'title': 'Greetings',
'url': 'https://api.github.com/repos/octokit/octokit.rb/issues/123'
}
}];

SoundNotificationStore.onIsNewNotification(payload);

expect(SoundNotificationStore.showNotification).toHaveBeenCalled();

});

});
1 change: 1 addition & 0 deletions src/js/actions/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var Actions = Reflux.createActions({
'login': {},
'logout': {},
'getNotifications': {asyncResult: true},
'isNewNotification': {},
'updateSearchTerm': {},
'clearSearchTerm': {},
'setSetting': {}
Expand Down
11 changes: 10 additions & 1 deletion src/js/components/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ var SettingsPage = React.createClass({
var settings = SettingsStore.getSettings();
return {
participating: settings.participating,
playSound: settings.playSound
playSound: settings.playSound,
showNotifications: settings.showNotifications
};
},

Expand Down Expand Up @@ -42,6 +43,14 @@ var SettingsPage = React.createClass({
onChange={this.toggleSetting.bind(this, 'playSound')} />
</div>
</div>
<div className='row'>
<div className='col-xs-8'>Show notifications</div>
<div className='col-xs-4'>
<Toggle
defaultChecked={this.state.showNotifications}
onChange={this.toggleSetting.bind(this, 'showNotifications')} />
</div>
</div>
<div className='row'>
<button
className='btn btn-block btn-danger btn-close'
Expand Down
34 changes: 3 additions & 31 deletions src/js/stores/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ var Actions = require('../actions/actions');
var apiRequests = require('../utils/api-requests');
var SettingsStore = require('../stores/settings');

require('../stores/sound-notification');

var NotificationsStore = Reflux.createStore({
listenables: Actions,

init: function () {
this._notifications = [];
this._previousNotifications = [];
},

updateTrayIcon: function (notifications) {
Expand All @@ -22,35 +23,6 @@ var NotificationsStore = Reflux.createStore({
}
},

isNewNotification: function (response) {
var self = this;
var playSound = SettingsStore.getSettings().playSound;

if (!playSound) { return; }

// Check if notification is already in the store.
var isNew = false;
_.map(response, function (obj) {
if (!_.contains(self._previousNotifications, obj.id)) {
isNew = true;
}
});

// Play Sound.
if (isNew) {
if (playSound) {
var audio = new Audio('sounds/digi.wav');
audio.play();
}
}

// Now Reset the previousNotifications array.
self._previousNotifications = [];
_.map(response, function (obj) {
self._previousNotifications.push(obj.id);
});
},

onGetNotifications: function () {
var self = this;
var participating = SettingsStore.getSettings().participating;
Expand All @@ -63,7 +35,7 @@ var NotificationsStore = Reflux.createStore({
// Success - Do Something.
Actions.getNotifications.completed(response.body);
self.updateTrayIcon(response.body);
self.isNewNotification(response.body);
Actions.isNewNotification(response.body);
} else {
// Error - Show messages.
Actions.getNotifications.failed(err);
Expand Down
16 changes: 14 additions & 2 deletions src/js/stores/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,28 @@ var SettingsStore = Reflux.createStore({

if (!settings) {
settings = {
'participating': false,
'playSound': true
participating: false,
playSound: true,
showNotifications: true
};
}

if (settings[0] === '{') {
settings = JSON.parse(settings);
}

if (!settings.participating) {
settings.participating = false;
}
if (!settings.playSound) {
settings.playSound = true;
}
if (!settings.showNotifications) {
settings.showNotifications = true;
}

this._settings = settings;
window.localStorage.setItem('settings', JSON.stringify(this._settings));
},

getSettings: function () {
Expand Down
68 changes: 68 additions & 0 deletions src/js/stores/sound-notification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
var ipc = window.require('ipc');
var Reflux = require('reflux');
var _ = require('underscore');

var Actions = require('../actions/actions');
var SettingsStore = require('../stores/settings');

var SoundNotificationStore = Reflux.createStore({
listenables: Actions,

init: function () {
this._previousNotifications = [];
},

playSound: function () {
var audio = new Audio('sounds/digi.wav');
audio.play();
},

showNotification: function (countNew, response, latestNotification) {
var title = (countNew == 1 ?
'Gitify - ' + latestNotification.full_name :
'Gitify');
var body = (countNew == 1 ?
latestNotification.subject :
'You\'ve got ' + countNew + ' notifications.');
var nativeNotification = new Notification(title, {
body: body
});
nativeNotification.onclick = function () {
ipc.sendChannel('reopen-window');
};
},

onIsNewNotification: function (response) {
var self = this;
var playSound = SettingsStore.getSettings().playSound;
var showNotifications = SettingsStore.getSettings().showNotifications;

if (!playSound && !showNotifications) { return; }

// Check if notification is already in the store.
var newNotifications = _.filter(response, function (obj) {
return !_.contains(self._previousNotifications, obj.id);
});

// Play Sound / Show Notification.
if (newNotifications && newNotifications.length) {
if (playSound) {
self.playSound();
}
if (showNotifications) {
this.showNotification(newNotifications.length, response, {
full_name: newNotifications[0].repository.full_name,
subject: newNotifications[0].subject.title
});
}
}

// Now Reset the previousNotifications array.
self._previousNotifications = _.map(response, function (obj) {
return obj.id;
});
}

});

module.exports = SoundNotificationStore;

0 comments on commit ec15749

Please sign in to comment.