aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-03-01 14:25:44 +0100
committerLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-03-01 14:25:44 +0100
commit739ef2e8a2dec94c3e10c3d26d797fe759fac7aa (patch)
tree4a2e066bf8627249f803eee16ba8e73268127fbd /src
parentfixes merge conflicts with latest develop (diff)
downloadferdium-app-739ef2e8a2dec94c3e10c3d26d797fe759fac7aa.tar.gz
ferdium-app-739ef2e8a2dec94c3e10c3d26d797fe759fac7aa.tar.zst
ferdium-app-739ef2e8a2dec94c3e10c3d26d797fe759fac7aa.zip
finish workspaces mvp
Diffstat (limited to 'src')
-rw-r--r--src/actions/index.js8
-rw-r--r--src/actions/lib/actions.js29
-rw-r--r--src/components/layout/Sidebar.js2
-rw-r--r--src/components/services/tabs/Tabbar.js4
-rw-r--r--src/features/workspaces/actions.js9
-rw-r--r--src/features/workspaces/containers/EditWorkspaceScreen.js12
-rw-r--r--src/features/workspaces/containers/WorkspacesScreen.js10
-rw-r--r--src/features/workspaces/index.js40
-rw-r--r--src/features/workspaces/state.js6
-rw-r--r--src/features/workspaces/store.js37
-rw-r--r--src/i18n/locales/de.json5
-rw-r--r--src/i18n/locales/en-US.json5
-rw-r--r--src/lib/Menu.js71
-rw-r--r--src/stores/ServicesStore.js12
14 files changed, 193 insertions, 57 deletions
diff --git a/src/actions/index.js b/src/actions/index.js
index 45e6da515..00f843cd6 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -11,7 +11,7 @@ import payment from './payment';
11import news from './news'; 11import news from './news';
12import settings from './settings'; 12import settings from './settings';
13import requests from './requests'; 13import requests from './requests';
14import workspace from '../features/workspaces/actions'; 14import workspaces from '../features/workspaces/actions';
15 15
16const actions = Object.assign({}, { 16const actions = Object.assign({}, {
17 service, 17 service,
@@ -24,7 +24,9 @@ const actions = Object.assign({}, {
24 news, 24 news,
25 settings, 25 settings,
26 requests, 26 requests,
27 workspace,
28}); 27});
29 28
30export default defineActions(actions, PropTypes.checkPropTypes); 29export default Object.assign(
30 defineActions(actions, PropTypes.checkPropTypes),
31 { workspaces },
32);
diff --git a/src/actions/lib/actions.js b/src/actions/lib/actions.js
index 499018d70..6571e9441 100644
--- a/src/actions/lib/actions.js
+++ b/src/actions/lib/actions.js
@@ -1,18 +1,23 @@
1export const createActionsFromDefinitions = (actionDefinitions, validate) => {
2 const actions = {};
3 Object.keys(actionDefinitions).forEach((actionName) => {
4 const action = (params) => {
5 const schema = actionDefinitions[actionName];
6 validate(schema, params, actionName);
7 action.notify(params);
8 };
9 actions[actionName] = action;
10 action.listeners = [];
11 action.listen = listener => action.listeners.push(listener);
12 action.notify = params => action.listeners.forEach(listener => listener(params));
13 });
14 return actions;
15};
16
1export default (definitions, validate) => { 17export default (definitions, validate) => {
2 const newActions = {}; 18 const newActions = {};
3 Object.keys(definitions).forEach((scopeName) => { 19 Object.keys(definitions).forEach((scopeName) => {
4 newActions[scopeName] = {}; 20 newActions[scopeName] = createActionsFromDefinitions(definitions[scopeName], validate);
5 Object.keys(definitions[scopeName]).forEach((actionName) => {
6 const action = (params) => {
7 const schema = definitions[scopeName][actionName];
8 validate(schema, params, actionName);
9 action.notify(params);
10 };
11 newActions[scopeName][actionName] = action;
12 action.listeners = [];
13 action.listen = listener => action.listeners.push(listener);
14 action.notify = params => action.listeners.forEach(listener => listener(params));
15 });
16 }); 21 });
17 return newActions; 22 return newActions;
18}; 23};
diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js
index 609a3b604..fcc5b0001 100644
--- a/src/components/layout/Sidebar.js
+++ b/src/components/layout/Sidebar.js
@@ -31,7 +31,7 @@ export default @observer class Sidebar extends Component {
31 openSettings: PropTypes.func.isRequired, 31 openSettings: PropTypes.func.isRequired,
32 toggleMuteApp: PropTypes.func.isRequired, 32 toggleMuteApp: PropTypes.func.isRequired,
33 isAppMuted: PropTypes.bool.isRequired, 33 isAppMuted: PropTypes.bool.isRequired,
34 } 34 };
35 35
36 static contextTypes = { 36 static contextTypes = {
37 intl: intlShape, 37 intl: intlShape,
diff --git a/src/components/services/tabs/Tabbar.js b/src/components/services/tabs/Tabbar.js
index dd5c2140f..5e8260ad0 100644
--- a/src/components/services/tabs/Tabbar.js
+++ b/src/components/services/tabs/Tabbar.js
@@ -19,7 +19,7 @@ export default @observer class TabBar extends Component {
19 updateService: PropTypes.func.isRequired, 19 updateService: PropTypes.func.isRequired,
20 showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired, 20 showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired,
21 showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired, 21 showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired,
22 } 22 };
23 23
24 onSortEnd = ({ oldIndex, newIndex }) => { 24 onSortEnd = ({ oldIndex, newIndex }) => {
25 const { 25 const {
@@ -45,7 +45,7 @@ export default @observer class TabBar extends Component {
45 redirect: false, 45 redirect: false,
46 }); 46 });
47 } 47 }
48 } 48 };
49 49
50 disableService({ serviceId }) { 50 disableService({ serviceId }) {
51 this.toggleService({ serviceId, isEnabled: false }); 51 this.toggleService({ serviceId, isEnabled: false });
diff --git a/src/features/workspaces/actions.js b/src/features/workspaces/actions.js
index 84de2b011..25246de09 100644
--- a/src/features/workspaces/actions.js
+++ b/src/features/workspaces/actions.js
@@ -1,7 +1,8 @@
1import PropTypes from 'prop-types'; 1import PropTypes from 'prop-types';
2import Workspace from './models/Workspace'; 2import Workspace from './models/Workspace';
3import { createActionsFromDefinitions } from '../../actions/lib/actions';
3 4
4export default { 5export default createActionsFromDefinitions({
5 edit: { 6 edit: {
6 workspace: PropTypes.instanceOf(Workspace).isRequired, 7 workspace: PropTypes.instanceOf(Workspace).isRequired,
7 }, 8 },
@@ -14,4 +15,8 @@ export default {
14 update: { 15 update: {
15 workspace: PropTypes.instanceOf(Workspace).isRequired, 16 workspace: PropTypes.instanceOf(Workspace).isRequired,
16 }, 17 },
17}; 18 activate: {
19 workspace: PropTypes.instanceOf(Workspace).isRequired,
20 },
21 deactivate: {},
22}, PropTypes.checkPropTypes);
diff --git a/src/features/workspaces/containers/EditWorkspaceScreen.js b/src/features/workspaces/containers/EditWorkspaceScreen.js
index 790b8a0fe..1b13bc2d4 100644
--- a/src/features/workspaces/containers/EditWorkspaceScreen.js
+++ b/src/features/workspaces/containers/EditWorkspaceScreen.js
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
4 4
5import ErrorBoundary from '../../../components/util/ErrorBoundary'; 5import ErrorBoundary from '../../../components/util/ErrorBoundary';
6import EditWorkspaceForm from '../components/EditWorkspaceForm'; 6import EditWorkspaceForm from '../components/EditWorkspaceForm';
7import { state } from '../state'; 7import { workspacesState } from '../state';
8import ServicesStore from '../../../stores/ServicesStore'; 8import ServicesStore from '../../../stores/ServicesStore';
9import Workspace from '../models/Workspace'; 9import Workspace from '../models/Workspace';
10 10
@@ -22,23 +22,23 @@ class EditWorkspaceScreen extends Component {
22 }; 22 };
23 23
24 onDelete = () => { 24 onDelete = () => {
25 const { workspaceBeingEdited } = state; 25 const { workspaceBeingEdited } = workspacesState;
26 const { actions } = this.props; 26 const { actions } = this.props;
27 if (!workspaceBeingEdited) return null; 27 if (!workspaceBeingEdited) return null;
28 actions.workspace.delete({ workspace: workspaceBeingEdited }); 28 actions.workspaces.delete({ workspace: workspaceBeingEdited });
29 }; 29 };
30 30
31 onSave = (values) => { 31 onSave = (values) => {
32 const { workspaceBeingEdited } = state; 32 const { workspaceBeingEdited } = workspacesState;
33 const { actions } = this.props; 33 const { actions } = this.props;
34 const workspace = new Workspace( 34 const workspace = new Workspace(
35 Object.assign({}, workspaceBeingEdited, values), 35 Object.assign({}, workspaceBeingEdited, values),
36 ); 36 );
37 actions.workspace.update({ workspace }); 37 actions.workspaces.update({ workspace });
38 }; 38 };
39 39
40 render() { 40 render() {
41 const { workspaceBeingEdited } = state; 41 const { workspaceBeingEdited } = workspacesState;
42 const { stores } = this.props; 42 const { stores } = this.props;
43 if (!workspaceBeingEdited) return null; 43 if (!workspaceBeingEdited) return null;
44 return ( 44 return (
diff --git a/src/features/workspaces/containers/WorkspacesScreen.js b/src/features/workspaces/containers/WorkspacesScreen.js
index b89cbcf67..94e714255 100644
--- a/src/features/workspaces/containers/WorkspacesScreen.js
+++ b/src/features/workspaces/containers/WorkspacesScreen.js
@@ -1,7 +1,7 @@
1import React, { Component } from 'react'; 1import React, { Component } from 'react';
2import { inject, observer } from 'mobx-react'; 2import { inject, observer } from 'mobx-react';
3import PropTypes from 'prop-types'; 3import PropTypes from 'prop-types';
4import { state } from '../state'; 4import { workspacesState } from '../state';
5import WorkspacesDashboard from '../components/WorkspacesDashboard'; 5import WorkspacesDashboard from '../components/WorkspacesDashboard';
6import ErrorBoundary from '../../../components/util/ErrorBoundary'; 6import ErrorBoundary from '../../../components/util/ErrorBoundary';
7 7
@@ -20,10 +20,10 @@ class WorkspacesScreen extends Component {
20 return ( 20 return (
21 <ErrorBoundary> 21 <ErrorBoundary>
22 <WorkspacesDashboard 22 <WorkspacesDashboard
23 workspaces={state.workspaces} 23 workspaces={workspacesState.workspaces}
24 isLoading={state.isLoading} 24 isLoading={workspacesState.isLoading}
25 onCreateWorkspaceSubmit={data => actions.workspace.create(data)} 25 onCreateWorkspaceSubmit={data => actions.workspaces.create(data)}
26 onWorkspaceClick={w => actions.workspace.edit({ workspace: w })} 26 onWorkspaceClick={w => actions.workspaces.edit({ workspace: w })}
27 /> 27 />
28 </ErrorBoundary> 28 </ErrorBoundary>
29 ); 29 );
diff --git a/src/features/workspaces/index.js b/src/features/workspaces/index.js
index 50ac3b414..8091f49fc 100644
--- a/src/features/workspaces/index.js
+++ b/src/features/workspaces/index.js
@@ -1,14 +1,28 @@
1import { reaction } from 'mobx'; 1import { reaction, runInAction } from 'mobx';
2import WorkspacesStore from './store'; 2import WorkspacesStore from './store';
3import api from './api'; 3import api from './api';
4import { state, resetState } from './state'; 4import { workspacesState, resetState } from './state';
5 5
6const debug = require('debug')('Franz:feature:workspaces'); 6const debug = require('debug')('Franz:feature:workspaces');
7 7
8let store = null; 8let store = null;
9 9
10export const filterServicesByActiveWorkspace = (services) => {
11 const { isFeatureActive, activeWorkspace } = workspacesState;
12 if (isFeatureActive && activeWorkspace) {
13 return services.filter(s => activeWorkspace.services.includes(s.id));
14 }
15 return services;
16};
17
18export const getActiveWorkspaceServices = (services) => {
19 return filterServicesByActiveWorkspace(services);
20};
21
10export default function initWorkspaces(stores, actions) { 22export default function initWorkspaces(stores, actions) {
11 const { features, user } = stores; 23 const { features, user } = stores;
24
25 // Toggle workspace feature
12 reaction( 26 reaction(
13 () => ( 27 () => (
14 features.features.isWorkspaceEnabled && ( 28 features.features.isWorkspaceEnabled && (
@@ -18,10 +32,12 @@ export default function initWorkspaces(stores, actions) {
18 (isEnabled) => { 32 (isEnabled) => {
19 if (isEnabled) { 33 if (isEnabled) {
20 debug('Initializing `workspaces` feature'); 34 debug('Initializing `workspaces` feature');
21 store = new WorkspacesStore(stores, api, actions, state); 35 store = new WorkspacesStore(stores, api, actions, workspacesState);
22 store.initialize(); 36 store.initialize();
37 runInAction(() => { workspacesState.isFeatureActive = true; });
23 } else if (store) { 38 } else if (store) {
24 debug('Disabling `workspaces` feature'); 39 debug('Disabling `workspaces` feature');
40 runInAction(() => { workspacesState.isFeatureActive = false; });
25 store.teardown(); 41 store.teardown();
26 store = null; 42 store = null;
27 resetState(); // Reset state to default 43 resetState(); // Reset state to default
@@ -31,4 +47,22 @@ export default function initWorkspaces(stores, actions) {
31 fireImmediately: true, 47 fireImmediately: true,
32 }, 48 },
33 ); 49 );
50
51 // Update active service on workspace switches
52 reaction(() => ({
53 isFeatureActive: workspacesState.isFeatureActive,
54 activeWorkspace: workspacesState.activeWorkspace,
55 }), ({ isFeatureActive, activeWorkspace }) => {
56 if (!isFeatureActive) return;
57 if (activeWorkspace) {
58 const services = stores.services.allDisplayed;
59 const activeService = services.find(s => s.isActive);
60 const workspaceServices = filterServicesByActiveWorkspace(services);
61 const isActiveServiceInWorkspace = workspaceServices.includes(activeService);
62 if (!isActiveServiceInWorkspace) {
63 console.log(workspaceServices[0].id);
64 actions.service.setActive({ serviceId: workspaceServices[0].id });
65 }
66 }
67 });
34} 68}
diff --git a/src/features/workspaces/state.js b/src/features/workspaces/state.js
index f938c1470..963b96f81 100644
--- a/src/features/workspaces/state.js
+++ b/src/features/workspaces/state.js
@@ -1,13 +1,15 @@
1import { observable } from 'mobx'; 1import { observable } from 'mobx';
2 2
3const defaultState = { 3const defaultState = {
4 activeWorkspace: null,
4 isLoading: false, 5 isLoading: false,
6 isFeatureActive: false,
5 workspaces: [], 7 workspaces: [],
6 workspaceBeingEdited: null, 8 workspaceBeingEdited: null,
7}; 9};
8 10
9export const state = observable(defaultState); 11export const workspacesState = observable(defaultState);
10 12
11export function resetState() { 13export function resetState() {
12 Object.assign(state, defaultState); 14 Object.assign(workspacesState, defaultState);
13} 15}
diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js
index 5cccb2ab7..a2997a0d2 100644
--- a/src/features/workspaces/store.js
+++ b/src/features/workspaces/store.js
@@ -1,8 +1,9 @@
1import { observable, reaction } from 'mobx'; 1import { observable, reaction, action } from 'mobx';
2import Store from '../../stores/lib/Store'; 2import Store from '../../stores/lib/Store';
3import CachedRequest from '../../stores/lib/CachedRequest'; 3import CachedRequest from '../../stores/lib/CachedRequest';
4import Workspace from './models/Workspace'; 4import Workspace from './models/Workspace';
5import { matchRoute } from '../../helpers/routing-helpers'; 5import { matchRoute } from '../../helpers/routing-helpers';
6import workspaceActions from './actions';
6 7
7const debug = require('debug')('Franz:feature:workspaces'); 8const debug = require('debug')('Franz:feature:workspaces');
8 9
@@ -48,28 +49,30 @@ export default class WorkspacesStore extends Store {
48 }, 49 },
49 ); 50 );
50 51
51 this.actions.workspace.edit.listen(this._edit); 52 workspaceActions.edit.listen(this._edit);
52 this.actions.workspace.create.listen(this._create); 53 workspaceActions.create.listen(this._create);
53 this.actions.workspace.delete.listen(this._delete); 54 workspaceActions.delete.listen(this._delete);
54 this.actions.workspace.update.listen(this._update); 55 workspaceActions.update.listen(this._update);
56 workspaceActions.activate.listen(this._setActiveWorkspace);
57 workspaceActions.deactivate.listen(this._deactivateActiveWorkspace);
55 } 58 }
56 59
57 _setWorkspaces = (workspaces) => { 60 _getWorkspaceById = id => this.state.workspaces.find(w => w.id === id);
61
62 @action _setWorkspaces = (workspaces) => {
58 debug('setting user workspaces', workspaces.slice()); 63 debug('setting user workspaces', workspaces.slice());
59 this.state.workspaces = workspaces.map(data => new Workspace(data)); 64 this.state.workspaces = workspaces.map(data => new Workspace(data));
60 }; 65 };
61 66
62 _setIsLoading = (isLoading) => { 67 @action _setIsLoading = (isLoading) => {
63 this.state.isLoading = isLoading; 68 this.state.isLoading = isLoading;
64 }; 69 };
65 70
66 _getWorkspaceById = id => this.state.workspaces.find(w => w.id === id); 71 @action _edit = ({ workspace }) => {
67
68 _edit = ({ workspace }) => {
69 this.stores.router.push(`/settings/workspaces/edit/${workspace.id}`); 72 this.stores.router.push(`/settings/workspaces/edit/${workspace.id}`);
70 }; 73 };
71 74
72 _create = async ({ name }) => { 75 @action _create = async ({ name }) => {
73 try { 76 try {
74 const result = await this.api.createWorkspace(name); 77 const result = await this.api.createWorkspace(name);
75 const workspace = new Workspace(result); 78 const workspace = new Workspace(result);
@@ -80,7 +83,7 @@ export default class WorkspacesStore extends Store {
80 } 83 }
81 }; 84 };
82 85
83 _delete = async ({ workspace }) => { 86 @action _delete = async ({ workspace }) => {
84 try { 87 try {
85 await this.api.deleteWorkspace(workspace); 88 await this.api.deleteWorkspace(workspace);
86 this.state.workspaces.remove(workspace); 89 this.state.workspaces.remove(workspace);
@@ -90,7 +93,7 @@ export default class WorkspacesStore extends Store {
90 } 93 }
91 }; 94 };
92 95
93 _update = async ({ workspace }) => { 96 @action _update = async ({ workspace }) => {
94 try { 97 try {
95 await this.api.updateWorkspace(workspace); 98 await this.api.updateWorkspace(workspace);
96 const localWorkspace = this.state.workspaces.find(ws => ws.id === workspace.id); 99 const localWorkspace = this.state.workspaces.find(ws => ws.id === workspace.id);
@@ -100,4 +103,12 @@ export default class WorkspacesStore extends Store {
100 throw error; 103 throw error;
101 } 104 }
102 }; 105 };
106
107 @action _setActiveWorkspace = ({ workspace }) => {
108 this.state.activeWorkspace = workspace;
109 };
110
111 @action _deactivateActiveWorkspace = () => {
112 this.state.activeWorkspace = null;
113 };
103} 114}
diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json
index 4906070a3..0c1fb8aa6 100644
--- a/src/i18n/locales/de.json
+++ b/src/i18n/locales/de.json
@@ -74,6 +74,9 @@
74 "menu.window" : "Fenster", 74 "menu.window" : "Fenster",
75 "menu.window.close" : "Schließen", 75 "menu.window.close" : "Schließen",
76 "menu.window.minimize" : "Minimieren", 76 "menu.window.minimize" : "Minimieren",
77 "menu.workspaces": "Workspaces",
78 "menu.workspaces.defaultWorkspace": "All services",
79 "menu.workspaces.addNewWorkspace": "Add New Workspace",
77 "password.email.label" : "E-Mail Adresse", 80 "password.email.label" : "E-Mail Adresse",
78 "password.headline" : "Passwort zurücksetzen", 81 "password.headline" : "Passwort zurücksetzen",
79 "password.link.login" : "An Deinem Konto anmelden", 82 "password.link.login" : "An Deinem Konto anmelden",
@@ -224,7 +227,7 @@
224 "settings.workspace.form.name": "Name", 227 "settings.workspace.form.name": "Name",
225 "settings.workspace.form.buttonDelete": "Workspace löschen", 228 "settings.workspace.form.buttonDelete": "Workspace löschen",
226 "settings.workspace.form.buttonSave": "Workspace speichern", 229 "settings.workspace.form.buttonSave": "Workspace speichern",
227 "settings.workspace.form.servicesInWorkspaceHeadline": "Services in diesem Workspace", 230 "settings.workspace.form.servicesInWorkspaceHeadline": "Services in diesem Workspace",
228 "settings.user.form.accountType.company" : "Firma", 231 "settings.user.form.accountType.company" : "Firma",
229 "settings.user.form.accountType.individual" : "Einzelperson", 232 "settings.user.form.accountType.individual" : "Einzelperson",
230 "settings.user.form.accountType.label" : "Konto-Typ", 233 "settings.user.form.accountType.label" : "Konto-Typ",
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 988ac46f2..2a51662a2 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -276,7 +276,10 @@
276 "menu.app.hideOthers": "Hide Others", 276 "menu.app.hideOthers": "Hide Others",
277 "menu.app.unhide": "Unhide", 277 "menu.app.unhide": "Unhide",
278 "menu.app.quit": "Quit", 278 "menu.app.quit": "Quit",
279 "menu.services.addNewService": "Add New Service...", 279 "menu.services.addNewService": "Add New Service",
280 "menu.workspaces": "Workspaces",
281 "menu.workspaces.defaultWorkspace": "All services",
282 "menu.workspaces.addNewWorkspace": "Add New Workspace",
280 "validation.required": "{field} is required", 283 "validation.required": "{field} is required",
281 "validation.email": "{field} is not valid", 284 "validation.email": "{field} is not valid",
282 "validation.url": "{field} is not a valid URL", 285 "validation.url": "{field} is not a valid URL",
diff --git a/src/lib/Menu.js b/src/lib/Menu.js
index c378619ad..1560dd285 100644
--- a/src/lib/Menu.js
+++ b/src/lib/Menu.js
@@ -3,6 +3,8 @@ import { observable, autorun, computed } from 'mobx';
3import { defineMessages } from 'react-intl'; 3import { defineMessages } from 'react-intl';
4 4
5import { isMac, ctrlKey, cmdKey } from '../environment'; 5import { isMac, ctrlKey, cmdKey } from '../environment';
6import { workspacesState } from '../features/workspaces/state';
7import workspaceActions from '../features/workspaces/actions';
6 8
7const { app, Menu, dialog } = remote; 9const { app, Menu, dialog } = remote;
8 10
@@ -179,6 +181,18 @@ const menuItems = defineMessages({
179 id: 'menu.services.addNewService', 181 id: 'menu.services.addNewService',
180 defaultMessage: '!!!Add New Service...', 182 defaultMessage: '!!!Add New Service...',
181 }, 183 },
184 workspaces: {
185 id: 'menu.workspaces',
186 defaultMessage: '!!!Workspaces',
187 },
188 defaultWorkspace: {
189 id: 'menu.workspaces.defaultWorkspace',
190 defaultMessage: '!!!Default',
191 },
192 addNewWorkspace: {
193 id: 'menu.workspaces.addNewWorkspace',
194 defaultMessage: '!!!Add New Workspace...',
195 },
182}); 196});
183 197
184function getActiveWebview() { 198function getActiveWebview() {
@@ -266,6 +280,10 @@ const _templateFactory = intl => [
266 submenu: [], 280 submenu: [],
267 }, 281 },
268 { 282 {
283 label: intl.formatMessage(menuItems.workspaces),
284 submenu: [],
285 },
286 {
269 label: intl.formatMessage(menuItems.window), 287 label: intl.formatMessage(menuItems.window),
270 role: 'window', 288 role: 'window',
271 submenu: [ 289 submenu: [
@@ -499,7 +517,9 @@ export default class FranzMenu {
499 } 517 }
500 518
501 _build() { 519 _build() {
502 const serviceTpl = Object.assign([], this.serviceTpl); // need to clone object so we don't modify computed (cached) object 520 // need to clone object so we don't modify computed (cached) object
521 const serviceTpl = Object.assign([], this.serviceTpl);
522 const workspacesMenu = Object.assign([], this.workspacesMenu);
503 523
504 if (window.franz === undefined) { 524 if (window.franz === undefined) {
505 return; 525 return;
@@ -632,7 +652,7 @@ export default class FranzMenu {
632 }, 652 },
633 ); 653 );
634 654
635 tpl[4].submenu.unshift(about, { 655 tpl[5].submenu.unshift(about, {
636 type: 'separator', 656 type: 'separator',
637 }); 657 });
638 } else { 658 } else {
@@ -678,6 +698,8 @@ export default class FranzMenu {
678 tpl[3].submenu = serviceTpl; 698 tpl[3].submenu = serviceTpl;
679 } 699 }
680 700
701 tpl[4].submenu = workspacesMenu;
702
681 this.currentTemplate = tpl; 703 this.currentTemplate = tpl;
682 const menu = Menu.buildFromTemplate(tpl); 704 const menu = Menu.buildFromTemplate(tpl);
683 Menu.setApplicationMenu(menu); 705 Menu.setApplicationMenu(menu);
@@ -701,6 +723,51 @@ export default class FranzMenu {
701 return []; 723 return [];
702 } 724 }
703 725
726 @computed get workspacesMenu() {
727 const { workspaces, activeWorkspace } = workspacesState;
728 const { intl } = window.franz;
729 const menu = [];
730
731 // Add new workspace item:
732 menu.push({
733 label: intl.formatMessage(menuItems.addNewWorkspace),
734 accelerator: `${cmdKey}+Shift+N`,
735 click: () => {
736 this.actions.ui.openSettings({ path: 'workspaces' });
737 },
738 enabled: this.stores.user.isLoggedIn,
739 }, {
740 type: 'separator',
741 });
742
743 // Default workspace
744 menu.push({
745 label: intl.formatMessage(menuItems.defaultWorkspace),
746 accelerator: `${cmdKey}+Alt+1`,
747 type: 'radio',
748 checked: !activeWorkspace,
749 click: () => {
750 workspaceActions.deactivate();
751 },
752 });
753
754 // Workspace items
755 if (this.stores.user.isLoggedIn) {
756 workspaces.forEach((workspace, i) => menu.push({
757 label: workspace.name,
758 accelerator: i < 9 ? `${cmdKey}+Alt+${i + 2}` : null,
759 type: 'radio',
760 checked: activeWorkspace ? workspace.id === activeWorkspace.id : false,
761 click: () => {
762 workspaceActions.activate({ workspace });
763 },
764 }));
765 }
766
767 console.log(menu);
768 return menu;
769 }
770
704 _getServiceName(service) { 771 _getServiceName(service) {
705 if (service.name) { 772 if (service.name) {
706 return service.name; 773 return service.name;
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index c63bef196..a86db8103 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -2,7 +2,7 @@ import {
2 action, 2 action,
3 reaction, 3 reaction,
4 computed, 4 computed,
5 observable, 5 observable, runInAction,
6} from 'mobx'; 6} from 'mobx';
7import { debounce, remove } from 'lodash'; 7import { debounce, remove } from 'lodash';
8import ms from 'ms'; 8import ms from 'ms';
@@ -12,6 +12,8 @@ import Request from './lib/Request';
12import CachedRequest from './lib/CachedRequest'; 12import CachedRequest from './lib/CachedRequest';
13import { matchRoute } from '../helpers/routing-helpers'; 13import { matchRoute } from '../helpers/routing-helpers';
14import { gaEvent } from '../lib/analytics'; 14import { gaEvent } from '../lib/analytics';
15import { workspacesState } from '../features/workspaces/state';
16import { filterServicesByActiveWorkspace, getActiveWorkspaceServices } from '../features/workspaces';
15 17
16const debug = require('debug')('Franz:ServiceStore'); 18const debug = require('debug')('Franz:ServiceStore');
17 19
@@ -98,7 +100,6 @@ export default class ServicesStore extends Store {
98 return observable(services.slice().slice().sort((a, b) => a.order - b.order)); 100 return observable(services.slice().slice().sort((a, b) => a.order - b.order));
99 } 101 }
100 } 102 }
101
102 return []; 103 return [];
103 } 104 }
104 105
@@ -107,13 +108,16 @@ export default class ServicesStore extends Store {
107 } 108 }
108 109
109 @computed get allDisplayed() { 110 @computed get allDisplayed() {
110 return this.stores.settings.all.app.showDisabledServices ? this.all : this.enabled; 111 const services = this.stores.settings.all.app.showDisabledServices ? this.all : this.enabled;
112 return filterServicesByActiveWorkspace(services);
111 } 113 }
112 114
113 // This is just used to avoid unnecessary rerendering of resource-heavy webviews 115 // This is just used to avoid unnecessary rerendering of resource-heavy webviews
114 @computed get allDisplayedUnordered() { 116 @computed get allDisplayedUnordered() {
117 const { showDisabledServices } = this.stores.settings.all.app;
115 const services = this.allServicesRequest.execute().result || []; 118 const services = this.allServicesRequest.execute().result || [];
116 return this.stores.settings.all.app.showDisabledServices ? services : services.filter(service => service.isEnabled); 119 const filteredServices = showDisabledServices ? services : services.filter(service => service.isEnabled);
120 return getActiveWorkspaceServices(filteredServices);
117 } 121 }
118 122
119 @computed get filtered() { 123 @computed get filtered() {