diff options
author | 2019-03-01 14:25:44 +0100 | |
---|---|---|
committer | 2019-03-01 14:25:44 +0100 | |
commit | 739ef2e8a2dec94c3e10c3d26d797fe759fac7aa (patch) | |
tree | 4a2e066bf8627249f803eee16ba8e73268127fbd /src | |
parent | fixes merge conflicts with latest develop (diff) | |
download | ferdium-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.js | 8 | ||||
-rw-r--r-- | src/actions/lib/actions.js | 29 | ||||
-rw-r--r-- | src/components/layout/Sidebar.js | 2 | ||||
-rw-r--r-- | src/components/services/tabs/Tabbar.js | 4 | ||||
-rw-r--r-- | src/features/workspaces/actions.js | 9 | ||||
-rw-r--r-- | src/features/workspaces/containers/EditWorkspaceScreen.js | 12 | ||||
-rw-r--r-- | src/features/workspaces/containers/WorkspacesScreen.js | 10 | ||||
-rw-r--r-- | src/features/workspaces/index.js | 40 | ||||
-rw-r--r-- | src/features/workspaces/state.js | 6 | ||||
-rw-r--r-- | src/features/workspaces/store.js | 37 | ||||
-rw-r--r-- | src/i18n/locales/de.json | 5 | ||||
-rw-r--r-- | src/i18n/locales/en-US.json | 5 | ||||
-rw-r--r-- | src/lib/Menu.js | 71 | ||||
-rw-r--r-- | src/stores/ServicesStore.js | 12 |
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'; | |||
11 | import news from './news'; | 11 | import news from './news'; |
12 | import settings from './settings'; | 12 | import settings from './settings'; |
13 | import requests from './requests'; | 13 | import requests from './requests'; |
14 | import workspace from '../features/workspaces/actions'; | 14 | import workspaces from '../features/workspaces/actions'; |
15 | 15 | ||
16 | const actions = Object.assign({}, { | 16 | const 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 | ||
30 | export default defineActions(actions, PropTypes.checkPropTypes); | 29 | export 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 @@ | |||
1 | export 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 | |||
1 | export default (definitions, validate) => { | 17 | export 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 @@ | |||
1 | import PropTypes from 'prop-types'; | 1 | import PropTypes from 'prop-types'; |
2 | import Workspace from './models/Workspace'; | 2 | import Workspace from './models/Workspace'; |
3 | import { createActionsFromDefinitions } from '../../actions/lib/actions'; | ||
3 | 4 | ||
4 | export default { | 5 | export 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 | ||
5 | import ErrorBoundary from '../../../components/util/ErrorBoundary'; | 5 | import ErrorBoundary from '../../../components/util/ErrorBoundary'; |
6 | import EditWorkspaceForm from '../components/EditWorkspaceForm'; | 6 | import EditWorkspaceForm from '../components/EditWorkspaceForm'; |
7 | import { state } from '../state'; | 7 | import { workspacesState } from '../state'; |
8 | import ServicesStore from '../../../stores/ServicesStore'; | 8 | import ServicesStore from '../../../stores/ServicesStore'; |
9 | import Workspace from '../models/Workspace'; | 9 | import 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 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import React, { Component } from 'react'; |
2 | import { inject, observer } from 'mobx-react'; | 2 | import { inject, observer } from 'mobx-react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | import { state } from '../state'; | 4 | import { workspacesState } from '../state'; |
5 | import WorkspacesDashboard from '../components/WorkspacesDashboard'; | 5 | import WorkspacesDashboard from '../components/WorkspacesDashboard'; |
6 | import ErrorBoundary from '../../../components/util/ErrorBoundary'; | 6 | import 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 @@ | |||
1 | import { reaction } from 'mobx'; | 1 | import { reaction, runInAction } from 'mobx'; |
2 | import WorkspacesStore from './store'; | 2 | import WorkspacesStore from './store'; |
3 | import api from './api'; | 3 | import api from './api'; |
4 | import { state, resetState } from './state'; | 4 | import { workspacesState, resetState } from './state'; |
5 | 5 | ||
6 | const debug = require('debug')('Franz:feature:workspaces'); | 6 | const debug = require('debug')('Franz:feature:workspaces'); |
7 | 7 | ||
8 | let store = null; | 8 | let store = null; |
9 | 9 | ||
10 | export 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 | |||
18 | export const getActiveWorkspaceServices = (services) => { | ||
19 | return filterServicesByActiveWorkspace(services); | ||
20 | }; | ||
21 | |||
10 | export default function initWorkspaces(stores, actions) { | 22 | export 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 @@ | |||
1 | import { observable } from 'mobx'; | 1 | import { observable } from 'mobx'; |
2 | 2 | ||
3 | const defaultState = { | 3 | const 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 | ||
9 | export const state = observable(defaultState); | 11 | export const workspacesState = observable(defaultState); |
10 | 12 | ||
11 | export function resetState() { | 13 | export 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 @@ | |||
1 | import { observable, reaction } from 'mobx'; | 1 | import { observable, reaction, action } from 'mobx'; |
2 | import Store from '../../stores/lib/Store'; | 2 | import Store from '../../stores/lib/Store'; |
3 | import CachedRequest from '../../stores/lib/CachedRequest'; | 3 | import CachedRequest from '../../stores/lib/CachedRequest'; |
4 | import Workspace from './models/Workspace'; | 4 | import Workspace from './models/Workspace'; |
5 | import { matchRoute } from '../../helpers/routing-helpers'; | 5 | import { matchRoute } from '../../helpers/routing-helpers'; |
6 | import workspaceActions from './actions'; | ||
6 | 7 | ||
7 | const debug = require('debug')('Franz:feature:workspaces'); | 8 | const 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'; | |||
3 | import { defineMessages } from 'react-intl'; | 3 | import { defineMessages } from 'react-intl'; |
4 | 4 | ||
5 | import { isMac, ctrlKey, cmdKey } from '../environment'; | 5 | import { isMac, ctrlKey, cmdKey } from '../environment'; |
6 | import { workspacesState } from '../features/workspaces/state'; | ||
7 | import workspaceActions from '../features/workspaces/actions'; | ||
6 | 8 | ||
7 | const { app, Menu, dialog } = remote; | 9 | const { 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 | ||
184 | function getActiveWebview() { | 198 | function 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'; |
7 | import { debounce, remove } from 'lodash'; | 7 | import { debounce, remove } from 'lodash'; |
8 | import ms from 'ms'; | 8 | import ms from 'ms'; |
@@ -12,6 +12,8 @@ import Request from './lib/Request'; | |||
12 | import CachedRequest from './lib/CachedRequest'; | 12 | import CachedRequest from './lib/CachedRequest'; |
13 | import { matchRoute } from '../helpers/routing-helpers'; | 13 | import { matchRoute } from '../helpers/routing-helpers'; |
14 | import { gaEvent } from '../lib/analytics'; | 14 | import { gaEvent } from '../lib/analytics'; |
15 | import { workspacesState } from '../features/workspaces/state'; | ||
16 | import { filterServicesByActiveWorkspace, getActiveWorkspaceServices } from '../features/workspaces'; | ||
15 | 17 | ||
16 | const debug = require('debug')('Franz:ServiceStore'); | 18 | const 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() { |