aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Stefan Malzner <stefan@adlk.io>2017-10-22 21:40:43 +0200
committerLibravatar GitHub <noreply@github.com>2017-10-22 21:40:43 +0200
commita714bb5cc95c5908fbba89f9d91afca4f11aa8b5 (patch)
treeeea6d8e440d7f323163f5fe6f4ea8f06ef3a5748 /src
parentAdded polish translation (diff)
parentfeature(Shortcuts): Add tab cycling with ctrl[+shift]+tab or ctrl+[pageup|pag... (diff)
downloadferdium-app-a714bb5cc95c5908fbba89f9d91afca4f11aa8b5.tar.gz
ferdium-app-a714bb5cc95c5908fbba89f9d91afca4f11aa8b5.tar.zst
ferdium-app-a714bb5cc95c5908fbba89f9d91afca4f11aa8b5.zip
Merge branch 'develop' into master
Diffstat (limited to 'src')
-rw-r--r--src/actions/service.js2
-rw-r--r--src/components/settings/settings/EditSettingsForm.js1
-rw-r--r--src/config.js10
-rw-r--r--src/containers/settings/EditSettingsScreen.js23
-rw-r--r--src/electron/Settings.js14
-rw-r--r--src/electron/ipc-api/appIndicator.js45
-rw-r--r--src/electron/ipc-api/settings.js9
-rw-r--r--src/electron/ipc-api/tray.js48
-rw-r--r--src/i18n/locales/en-US.json1
-rw-r--r--src/index.js26
-rw-r--r--src/lib/Tray.js64
-rw-r--r--src/stores/AppStore.js12
-rw-r--r--src/stores/ServicesStore.js24
13 files changed, 178 insertions, 101 deletions
diff --git a/src/actions/service.js b/src/actions/service.js
index 7f429ca32..cdd4bbf16 100644
--- a/src/actions/service.js
+++ b/src/actions/service.js
@@ -4,6 +4,8 @@ export default {
4 setActive: { 4 setActive: {
5 serviceId: PropTypes.string.isRequired, 5 serviceId: PropTypes.string.isRequired,
6 }, 6 },
7 setActiveNext: {},
8 setActivePrev: {},
7 showAddServiceInterface: { 9 showAddServiceInterface: {
8 recipeId: PropTypes.string.isRequired, 10 recipeId: PropTypes.string.isRequired,
9 }, 11 },
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js
index 02736dbb9..5675fecf4 100644
--- a/src/components/settings/settings/EditSettingsForm.js
+++ b/src/components/settings/settings/EditSettingsForm.js
@@ -115,6 +115,7 @@ export default class EditSettingsForm extends Component {
115 <h2>{intl.formatMessage(messages.headlineGeneral)}</h2> 115 <h2>{intl.formatMessage(messages.headlineGeneral)}</h2>
116 <Toggle field={form.$('autoLaunchOnStart')} /> 116 <Toggle field={form.$('autoLaunchOnStart')} />
117 <Toggle field={form.$('runInBackground')} /> 117 <Toggle field={form.$('runInBackground')} />
118 <Toggle field={form.$('enableSystemTray')} />
118 {process.platform === 'win32' && ( 119 {process.platform === 'win32' && (
119 <Toggle field={form.$('minimizeToSystemTray')} /> 120 <Toggle field={form.$('minimizeToSystemTray')} />
120 )} 121 )}
diff --git a/src/config.js b/src/config.js
index acbf57f3c..0a4856ece 100644
--- a/src/config.js
+++ b/src/config.js
@@ -3,3 +3,13 @@ export const LOCAL_API = 'http://localhost:3000';
3export const DEV_API = 'https://dev.franzinfra.com'; 3export const DEV_API = 'https://dev.franzinfra.com';
4export const LIVE_API = 'https://api.franzinfra.com'; 4export const LIVE_API = 'https://api.franzinfra.com';
5export const GA_ID = 'UA-74126766-6'; 5export const GA_ID = 'UA-74126766-6';
6
7export const DEFAULT_APP_SETTINGS = {
8 autoLaunchOnStart: true,
9 autoLaunchInBackground: false,
10 runInBackground: false,
11 enableSystemTray: true,
12 minimizeToSystemTray: false,
13 locale: 'en-us', // TODO: Replace with proper solution once translations are in
14 beta: false,
15};
diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js
index 0e17cafce..6dc2175e1 100644
--- a/src/containers/settings/EditSettingsScreen.js
+++ b/src/containers/settings/EditSettingsScreen.js
@@ -9,6 +9,7 @@ import UserStore from '../../stores/UserStore';
9import Form from '../../lib/Form'; 9import Form from '../../lib/Form';
10import languages from '../../i18n/languages'; 10import languages from '../../i18n/languages';
11import { gaPage } from '../../lib/analytics'; 11import { gaPage } from '../../lib/analytics';
12import { DEFAULT_APP_SETTINGS } from '../../config';
12 13
13 14
14import EditSettingsForm from '../../components/settings/settings/EditSettingsForm'; 15import EditSettingsForm from '../../components/settings/settings/EditSettingsForm';
@@ -26,6 +27,10 @@ const messages = defineMessages({
26 id: 'settings.app.form.runInBackground', 27 id: 'settings.app.form.runInBackground',
27 defaultMessage: '!!!Keep Franz in background when closing the window', 28 defaultMessage: '!!!Keep Franz in background when closing the window',
28 }, 29 },
30 enableSystemTray: {
31 id: 'settings.app.form.enableSystemTray',
32 defaultMessage: '!!!Show Franz in system tray',
33 },
29 minimizeToSystemTray: { 34 minimizeToSystemTray: {
30 id: 'settings.app.form.minimizeToSystemTray', 35 id: 'settings.app.form.minimizeToSystemTray',
31 defaultMessage: '!!!Minimize Franz to system tray', 36 defaultMessage: '!!!Minimize Franz to system tray',
@@ -61,6 +66,7 @@ export default class EditSettingsScreen extends Component {
61 settings.update({ 66 settings.update({
62 settings: { 67 settings: {
63 runInBackground: settingsData.runInBackground, 68 runInBackground: settingsData.runInBackground,
69 enableSystemTray: settingsData.enableSystemTray,
64 minimizeToSystemTray: settingsData.minimizeToSystemTray, 70 minimizeToSystemTray: settingsData.minimizeToSystemTray,
65 locale: settingsData.locale, 71 locale: settingsData.locale,
66 beta: settingsData.beta, 72 beta: settingsData.beta,
@@ -91,33 +97,38 @@ export default class EditSettingsScreen extends Component {
91 autoLaunchOnStart: { 97 autoLaunchOnStart: {
92 label: intl.formatMessage(messages.autoLaunchOnStart), 98 label: intl.formatMessage(messages.autoLaunchOnStart),
93 value: app.autoLaunchOnStart, 99 value: app.autoLaunchOnStart,
94 default: true, 100 default: DEFAULT_APP_SETTINGS.autoLaunchOnStart,
95 }, 101 },
96 autoLaunchInBackground: { 102 autoLaunchInBackground: {
97 label: intl.formatMessage(messages.autoLaunchInBackground), 103 label: intl.formatMessage(messages.autoLaunchInBackground),
98 value: app.launchInBackground, 104 value: app.launchInBackground,
99 default: false, 105 default: DEFAULT_APP_SETTINGS.autoLaunchInBackground,
100 }, 106 },
101 runInBackground: { 107 runInBackground: {
102 label: intl.formatMessage(messages.runInBackground), 108 label: intl.formatMessage(messages.runInBackground),
103 value: settings.all.runInBackground, 109 value: settings.all.runInBackground,
104 default: true, 110 default: DEFAULT_APP_SETTINGS.runInBackground,
111 },
112 enableSystemTray: {
113 label: intl.formatMessage(messages.enableSystemTray),
114 value: settings.all.enableSystemTray,
115 default: DEFAULT_APP_SETTINGS.enableSystemTray,
105 }, 116 },
106 minimizeToSystemTray: { 117 minimizeToSystemTray: {
107 label: intl.formatMessage(messages.minimizeToSystemTray), 118 label: intl.formatMessage(messages.minimizeToSystemTray),
108 value: settings.all.minimizeToSystemTray, 119 value: settings.all.minimizeToSystemTray,
109 default: false, 120 default: DEFAULT_APP_SETTINGS.minimizeToSystemTray,
110 }, 121 },
111 locale: { 122 locale: {
112 label: intl.formatMessage(messages.language), 123 label: intl.formatMessage(messages.language),
113 value: app.locale, 124 value: app.locale,
114 options, 125 options,
115 default: 'en-US', 126 default: DEFAULT_APP_SETTINGS.locale,
116 }, 127 },
117 beta: { 128 beta: {
118 label: intl.formatMessage(messages.beta), 129 label: intl.formatMessage(messages.beta),
119 value: user.data.beta, 130 value: user.data.beta,
120 default: false, 131 default: DEFAULT_APP_SETTINGS.beta,
121 }, 132 },
122 }, 133 },
123 }; 134 };
diff --git a/src/electron/Settings.js b/src/electron/Settings.js
index 049a08296..824b4c20c 100644
--- a/src/electron/Settings.js
+++ b/src/electron/Settings.js
@@ -1,5 +1,17 @@
1import { observable } from 'mobx';
2
3import { DEFAULT_APP_SETTINGS } from '../config';
4
1export default class Settings { 5export default class Settings {
2 store = {}; 6 @observable store = {
7 autoLaunchOnStart: DEFAULT_APP_SETTINGS.autoLaunchOnStart,
8 autoLaunchInBackground: DEFAULT_APP_SETTINGS.autoLaunchInBackground,
9 runInBackground: DEFAULT_APP_SETTINGS.runInBackground,
10 enableSystemTray: DEFAULT_APP_SETTINGS.enableSystemTray,
11 minimizeToSystemTray: DEFAULT_APP_SETTINGS.minimizeToSystemTray,
12 locale: DEFAULT_APP_SETTINGS.locale,
13 beta: DEFAULT_APP_SETTINGS.beta,
14 };
3 15
4 set(settings) { 16 set(settings) {
5 this.store = Object.assign(this.store, settings); 17 this.store = Object.assign(this.store, settings);
diff --git a/src/electron/ipc-api/appIndicator.js b/src/electron/ipc-api/appIndicator.js
index 576234d25..d31819068 100644
--- a/src/electron/ipc-api/appIndicator.js
+++ b/src/electron/ipc-api/appIndicator.js
@@ -1,12 +1,11 @@
1import { app, ipcMain, Tray, Menu } from 'electron'; 1import { app, ipcMain } from 'electron';
2import path from 'path'; 2import path from 'path';
3import { autorun } from 'mobx';
3 4
4const INDICATOR_TRAY_PLAIN = 'tray';
5const INDICATOR_TRAY_UNREAD = 'tray-unread';
6const INDICATOR_TASKBAR = 'taskbar'; 5const INDICATOR_TASKBAR = 'taskbar';
7
8const FILE_EXTENSION = process.platform === 'win32' ? 'ico' : 'png'; 6const FILE_EXTENSION = process.platform === 'win32' ? 'ico' : 'png';
9let trayIcon; 7
8let isTrayIconEnabled;
10 9
11function getAsset(type, asset) { 10function getAsset(type, asset) {
12 return path.join( 11 return path.join(
@@ -15,26 +14,14 @@ function getAsset(type, asset) {
15} 14}
16 15
17export default (params) => { 16export default (params) => {
18 trayIcon = new Tray(getAsset('tray', INDICATOR_TRAY_PLAIN)); 17 autorun(() => {
19 const trayMenuTemplate = [ 18 isTrayIconEnabled = params.settings.get('enableSystemTray');
20 {
21 label: 'Show Franz',
22 click() {
23 params.mainWindow.show();
24 },
25 }, {
26 label: 'Quit Franz',
27 click() {
28 app.quit();
29 },
30 },
31 ];
32
33 const trayMenu = Menu.buildFromTemplate(trayMenuTemplate);
34 trayIcon.setContextMenu(trayMenu);
35 19
36 trayIcon.on('click', () => { 20 if (!isTrayIconEnabled) {
37 params.mainWindow.show(); 21 params.trayIcon.hide();
22 } else if (isTrayIconEnabled) {
23 params.trayIcon.show();
24 }
38 }); 25 });
39 26
40 ipcMain.on('updateAppIndicator', (event, args) => { 27 ipcMain.on('updateAppIndicator', (event, args) => {
@@ -68,13 +55,7 @@ export default (params) => {
68 } 55 }
69 } 56 }
70 57
71 // Update system tray 58 // Update Tray
72 trayIcon.setImage(getAsset('tray', args.indicator !== 0 ? INDICATOR_TRAY_UNREAD : INDICATOR_TRAY_PLAIN)); 59 params.trayIcon.setIndicator(args.indicator);
73
74 if (process.platform === 'darwin') {
75 trayIcon.setPressedImage(
76 getAsset('tray', `${args.indicator !== 0 ? INDICATOR_TRAY_UNREAD : INDICATOR_TRAY_PLAIN}-active`),
77 );
78 }
79 }); 60 });
80}; 61};
diff --git a/src/electron/ipc-api/settings.js b/src/electron/ipc-api/settings.js
index 1d7eafa6c..995b28fbd 100644
--- a/src/electron/ipc-api/settings.js
+++ b/src/electron/ipc-api/settings.js
@@ -1,10 +1,7 @@
1import { ipcMain } from 'electron'; 1import { ipcMain } from 'electron';
2 2
3export default (params) => { 3export default (params) => {
4 if (process.platform === 'darwin' || process.platform === 'win32') { 4 ipcMain.on('settings', (event, args) => {
5 // eslint-disable-next-line 5 params.settings.set(args);
6 ipcMain.on('settings', (event, args) => { 6 });
7 params.settings.set(args);
8 });
9 }
10}; 7};
diff --git a/src/electron/ipc-api/tray.js b/src/electron/ipc-api/tray.js
deleted file mode 100644
index 43364c0ed..000000000
--- a/src/electron/ipc-api/tray.js
+++ /dev/null
@@ -1,48 +0,0 @@
1import { Tray, Menu, ipcMain } from 'electron';
2import path from 'path';
3
4const INDICATOR_PLAIN = 'franz-taskbar';
5const INDICATOR_UNREAD = 'franz-taskbar-unread';
6
7const FILE_EXTENSION = process.platform === 'win32' ? 'ico' : 'png';
8
9let trayIcon;
10
11function getAsset(asset) {
12 return path.join(
13 __dirname, '..', '..', 'assets', 'images', 'tray', process.platform, `${asset}.${FILE_EXTENSION}`,
14 );
15}
16
17export default (params) => {
18 // if (process.platform === 'win32' || process.platform === 'linux') {
19 trayIcon = new Tray(getAsset(INDICATOR_PLAIN));
20 const trayMenuTemplate = [
21 {
22 label: 'Show Franz',
23 click() {
24 params.mainWindow.show();
25 },
26 }, {
27 label: 'Quit Franz',
28 click() {
29 params.app.quit();
30 },
31 },
32 ];
33
34 const trayMenu = Menu.buildFromTemplate(trayMenuTemplate);
35 trayIcon.setContextMenu(trayMenu);
36
37 trayIcon.on('click', () => {
38 params.mainWindow.show();
39 });
40
41 ipcMain.on('updateTrayIconIndicator', (event, args) => {
42 trayIcon.setImage(getAsset(args.count !== 0 ? INDICATOR_UNREAD : INDICATOR_PLAIN));
43
44 if (process.platform === 'darwin') {
45 trayIcon.setPressedImage(getAsset(`${args.count !== 0 ? INDICATOR_UNREAD : INDICATOR_PLAIN}-active`));
46 }
47 });
48};
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 194b8047c..b9ed51b83 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -126,6 +126,7 @@
126 "settings.app.updateStatusUpToDate": "You are using the latest version of Franz", 126 "settings.app.updateStatusUpToDate": "You are using the latest version of Franz",
127 "settings.app.form.autoLaunchOnStart": "Launch Franz on start", 127 "settings.app.form.autoLaunchOnStart": "Launch Franz on start",
128 "settings.app.form.autoLaunchInBackground": "Open in background", 128 "settings.app.form.autoLaunchInBackground": "Open in background",
129 "settings.app.form.enableSystemTray": "Show Franz in system tray",
129 "settings.app.form.minimizeToSystemTray": "Minimize Franz to system tray", 130 "settings.app.form.minimizeToSystemTray": "Minimize Franz to system tray",
130 "settings.app.form.runInBackground": "Keep Franz in background when closing the window", 131 "settings.app.form.runInBackground": "Keep Franz in background when closing the window",
131 "settings.app.form.language": "Language", 132 "settings.app.form.language": "Language",
diff --git a/src/index.js b/src/index.js
index 3244c44ad..e7fa7da6d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -2,16 +2,18 @@ import { app, BrowserWindow, shell } from 'electron';
2import fs from 'fs-extra'; 2import fs from 'fs-extra';
3import path from 'path'; 3import path from 'path';
4 4
5// eslint-disable-next-line 5/* eslint-disable */
6if (require('electron-squirrel-startup')) app.quit(); 6if (require('electron-squirrel-startup')) app.quit();
7 7
8import windowStateKeeper from 'electron-window-state'; // eslint-disable-line 8import windowStateKeeper from 'electron-window-state';
9 9
10import { isDevMode, isWindows } from './environment'; // eslint-disable-line 10import { isDevMode, isWindows } from './environment';
11import ipcApi from './electron/ipc-api'; // eslint-disable-line 11import ipcApi from './electron/ipc-api';
12import Settings from './electron/Settings'; // eslint-disable-line 12import Tray from './lib/Tray';
13import { appId } from './package.json'; // eslint-disable-line 13import Settings from './electron/Settings';
14import './electron/exception'; // eslint-disable-line 14import { appId } from './package.json';
15import './electron/exception';
16/* eslint-enable */
15 17
16// Keep a global reference of the window object, if you don't, the window will 18// Keep a global reference of the window object, if you don't, the window will
17// be closed automatically when the JavaScript object is garbage collected. 19// be closed automatically when the JavaScript object is garbage collected.
@@ -47,8 +49,11 @@ const createWindow = async () => {
47 autoHideMenuBar: true, 49 autoHideMenuBar: true,
48 }); 50 });
49 51
52 // Initialize System Tray
53 const trayIcon = new Tray(mainWindow);
54
50 // Initialize ipcApi 55 // Initialize ipcApi
51 ipcApi({ mainWindow, settings }); 56 ipcApi({ mainWindow, settings, trayIcon });
52 57
53 // Manage Window State 58 // Manage Window State
54 mainWindowState.manage(mainWindow); 59 mainWindowState.manage(mainWindow);
@@ -85,6 +90,7 @@ const createWindow = async () => {
85 90
86 if (settings.get('minimizeToSystemTray')) { 91 if (settings.get('minimizeToSystemTray')) {
87 mainWindow.setSkipTaskbar(true); 92 mainWindow.setSkipTaskbar(true);
93 trayIcon.show();
88 } 94 }
89 }); 95 });
90 96
@@ -102,6 +108,10 @@ const createWindow = async () => {
102 if (app.wasMaximized) { 108 if (app.wasMaximized) {
103 mainWindow.maximize(); 109 mainWindow.maximize();
104 } 110 }
111
112 if (!settings.get('enableSystemTray')) {
113 trayIcon.hide();
114 }
105 }); 115 });
106 116
107 mainWindow.on('show', () => { 117 mainWindow.on('show', () => {
diff --git a/src/lib/Tray.js b/src/lib/Tray.js
new file mode 100644
index 000000000..631342b24
--- /dev/null
+++ b/src/lib/Tray.js
@@ -0,0 +1,64 @@
1import { app, Tray, Menu } from 'electron';
2import path from 'path';
3
4const FILE_EXTENSION = process.platform === 'win32' ? 'ico' : 'png';
5const INDICATOR_TRAY_PLAIN = 'tray';
6const INDICATOR_TRAY_UNREAD = 'tray-unread';
7
8function getAsset(type, asset) {
9 return path.join(
10 __dirname, '..', 'assets', 'images', type, process.platform, `${asset}.${FILE_EXTENSION}`,
11 );
12}
13
14export default class TrayIcon {
15 mainWindow = null;
16 trayIcon = null;
17
18 constructor(mainWindow) {
19 this.mainWindow = mainWindow;
20 }
21
22 show() {
23 this.trayIcon = new Tray(getAsset('tray', INDICATOR_TRAY_PLAIN));
24 const trayMenuTemplate = [
25 {
26 label: 'Show Franz',
27 click() {
28 this.mainWindow.show();
29 },
30 }, {
31 label: 'Quit Franz',
32 click() {
33 app.quit();
34 },
35 },
36 ];
37
38 const trayMenu = Menu.buildFromTemplate(trayMenuTemplate);
39 this.trayIcon.setContextMenu(trayMenu);
40
41 this.trayIcon.on('click', () => {
42 this.mainWindow.show();
43 });
44 }
45
46 hide() {
47 if (this.trayIcon) {
48 this.trayIcon.destroy();
49 this.trayIcon = null;
50 }
51 }
52
53 setIndicator(indicator) {
54 if (!this.trayIcon) return;
55
56 this.trayIcon.setImage(getAsset('tray', indicator !== 0 ? INDICATOR_TRAY_UNREAD : INDICATOR_TRAY_PLAIN));
57
58 if (process.platform === 'darwin') {
59 this.trayIcon.setPressedImage(
60 getAsset('tray', `${indicator !== 0 ? INDICATOR_TRAY_UNREAD : INDICATOR_TRAY_PLAIN}-active`),
61 );
62 }
63 }
64}
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js
index a5e0839f2..42ec25204 100644
--- a/src/stores/AppStore.js
+++ b/src/stores/AppStore.js
@@ -125,6 +125,18 @@ export default class AppStore extends Store {
125 this.actions.service.openDevToolsForActiveService(); 125 this.actions.service.openDevToolsForActiveService();
126 }); 126 });
127 127
128 // Set active the next service
129 key(
130 '⌘+pagedown, ctrl+pagedown, ⌘+shift+tab, ctrl+shift+tab', () => {
131 this.actions.service.setActiveNext();
132 });
133
134 // Set active the prev service
135 key(
136 '⌘+pageup, ctrl+pageup, ⌘+tab, ctrl+tab', () => {
137 this.actions.service.setActivePrev();
138 });
139
128 this.locale = this._getDefaultLocale(); 140 this.locale = this._getDefaultLocale();
129 141
130 this._healthCheck(); 142 this._healthCheck();
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index 77d2e7da4..19db05494 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -24,6 +24,8 @@ export default class ServicesStore extends Store {
24 24
25 // Register action handlers 25 // Register action handlers
26 this.actions.service.setActive.listen(this._setActive.bind(this)); 26 this.actions.service.setActive.listen(this._setActive.bind(this));
27 this.actions.service.setActiveNext.listen(this._setActiveNext.bind(this));
28 this.actions.service.setActivePrev.listen(this._setActivePrev.bind(this));
27 this.actions.service.showAddServiceInterface.listen(this._showAddServiceInterface.bind(this)); 29 this.actions.service.showAddServiceInterface.listen(this._showAddServiceInterface.bind(this));
28 this.actions.service.createService.listen(this._createService.bind(this)); 30 this.actions.service.createService.listen(this._createService.bind(this));
29 this.actions.service.createFromLegacyService.listen(this._createFromLegacyService.bind(this)); 31 this.actions.service.createFromLegacyService.listen(this._createFromLegacyService.bind(this));
@@ -206,6 +208,24 @@ export default class ServicesStore extends Store {
206 service.isActive = true; 208 service.isActive = true;
207 } 209 }
208 210
211 @action _setActiveNext() {
212 const nextIndex = this._wrapIndex(this.enabled.findIndex(service => service.isActive), 1, this.enabled.length);
213
214 this.all.forEach((s, index) => {
215 this.all[index].isActive = false;
216 });
217 this.enabled[nextIndex].isActive = true;
218 }
219
220 @action _setActivePrev() {
221 const prevIndex = this._wrapIndex(this.enabled.findIndex(service => service.isActive), -1, this.enabled.length);
222
223 this.all.forEach((s, index) => {
224 this.all[index].isActive = false;
225 });
226 this.enabled[prevIndex].isActive = true;
227 }
228
209 @action _setUnreadMessageCount({ serviceId, count }) { 229 @action _setUnreadMessageCount({ serviceId, count }) {
210 const service = this.one(serviceId); 230 const service = this.one(serviceId);
211 231
@@ -500,4 +520,8 @@ export default class ServicesStore extends Store {
500 _reorderAnalytics = debounce(() => { 520 _reorderAnalytics = debounce(() => {
501 gaEvent('Service', 'order'); 521 gaEvent('Service', 'order');
502 }, 5000); 522 }, 5000);
523
524 _wrapIndex(index, delta, size) {
525 return (((index + delta) % size) + size) % size;
526 }
503} 527}