aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-03-19 19:38:56 +0100
committerLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-03-19 19:40:08 +0100
commite4f1862644d5921e2ee77078c10e16efa3e58c7b (patch)
treeff7f23eb83ee974a9f719ed6f58647ce7f0a1175 /src
parentfix conflicts with latest develop (diff)
downloadferdium-app-e4f1862644d5921e2ee77078c10e16efa3e58c7b.tar.gz
ferdium-app-e4f1862644d5921e2ee77078c10e16efa3e58c7b.tar.zst
ferdium-app-e4f1862644d5921e2ee77078c10e16efa3e58c7b.zip
add workspace drawer
Diffstat (limited to 'src')
-rw-r--r--src/components/layout/AppLayout.js24
-rw-r--r--src/components/layout/Sidebar.js42
-rw-r--r--src/components/services/content/ServiceView.js1
-rw-r--r--src/containers/layout/AppLayoutContainer.js14
-rw-r--r--src/features/workspaces/actions.js6
-rw-r--r--src/features/workspaces/components/WorkspaceDrawer.js94
-rw-r--r--src/features/workspaces/components/WorkspaceDrawerItem.js88
-rw-r--r--src/features/workspaces/state.js1
-rw-r--r--src/features/workspaces/store.js12
-rw-r--r--src/i18n/locales/defaultMessages.json57
-rw-r--r--src/i18n/locales/en-US.json8
-rw-r--r--src/i18n/messages/src/components/layout/AppLayout.json24
-rw-r--r--src/i18n/messages/src/components/layout/Sidebar.json26
-rw-r--r--src/i18n/messages/src/features/workspaces/components/WorkspaceDrawer.json28
-rw-r--r--src/lib/Menu.js4
-rw-r--r--src/styles/layout.scss12
16 files changed, 416 insertions, 25 deletions
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js
index 593149e72..e06192f87 100644
--- a/src/components/layout/AppLayout.js
+++ b/src/components/layout/AppLayout.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; 3import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 4import { defineMessages, intlShape } from 'react-intl';
5import { TitleBar } from 'electron-react-titlebar'; 5import { TitleBar } from 'electron-react-titlebar';
6import injectSheet from 'react-jss';
6 7
7import InfoBar from '../ui/InfoBar'; 8import InfoBar from '../ui/InfoBar';
8import { Component as DelayApp } from '../../features/delayApp'; 9import { Component as DelayApp } from '../../features/delayApp';
@@ -13,6 +14,7 @@ import ErrorBoundary from '../util/ErrorBoundary';
13// import globalMessages from '../../i18n/globalMessages'; 14// import globalMessages from '../../i18n/globalMessages';
14 15
15import { isWindows } from '../../environment'; 16import { isWindows } from '../../environment';
17import { workspacesState } from '../../features/workspaces/state';
16 18
17function createMarkup(HTMLString) { 19function createMarkup(HTMLString) {
18 return { __html: HTMLString }; 20 return { __html: HTMLString };
@@ -45,10 +47,23 @@ const messages = defineMessages({
45 }, 47 },
46}); 48});
47 49
48export default @observer class AppLayout extends Component { 50const styles = theme => ({
51 appContent: {
52 width: `calc(100% + ${theme.workspaceDrawerWidth})`,
53 transition: 'transform 0.5s ease',
54 transform() {
55 return workspacesState.isWorkspaceDrawerOpen ? 'translateX(0)' : 'translateX(-220px)';
56 },
57 },
58});
59
60@injectSheet(styles) @observer
61class AppLayout extends Component {
49 static propTypes = { 62 static propTypes = {
63 classes: PropTypes.object.isRequired,
50 isFullScreen: PropTypes.bool.isRequired, 64 isFullScreen: PropTypes.bool.isRequired,
51 sidebar: PropTypes.element.isRequired, 65 sidebar: PropTypes.element.isRequired,
66 workspacesDrawer: PropTypes.element.isRequired,
52 services: PropTypes.element.isRequired, 67 services: PropTypes.element.isRequired,
53 children: PropTypes.element, 68 children: PropTypes.element,
54 news: MobxPropTypes.arrayOrObservableArray.isRequired, 69 news: MobxPropTypes.arrayOrObservableArray.isRequired,
@@ -76,7 +91,9 @@ export default @observer class AppLayout extends Component {
76 91
77 render() { 92 render() {
78 const { 93 const {
94 classes,
79 isFullScreen, 95 isFullScreen,
96 workspacesDrawer,
80 sidebar, 97 sidebar,
81 services, 98 services,
82 children, 99 children,
@@ -102,7 +119,8 @@ export default @observer class AppLayout extends Component {
102 <div className={(darkMode ? 'theme__dark' : '')}> 119 <div className={(darkMode ? 'theme__dark' : '')}>
103 <div className="app"> 120 <div className="app">
104 {isWindows && !isFullScreen && <TitleBar menu={window.franz.menu.template} icon="assets/images/logo.svg" />} 121 {isWindows && !isFullScreen && <TitleBar menu={window.franz.menu.template} icon="assets/images/logo.svg" />}
105 <div className="app__content"> 122 <div className={`app__content ${classes.appContent}`}>
123 {workspacesDrawer}
106 {sidebar} 124 {sidebar}
107 <div className="app__service"> 125 <div className="app__service">
108 {news.length > 0 && news.map(item => ( 126 {news.length > 0 && news.map(item => (
@@ -176,3 +194,5 @@ export default @observer class AppLayout extends Component {
176 ); 194 );
177 } 195 }
178} 196}
197
198export default AppLayout;
diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js
index fcc5b0001..de379875e 100644
--- a/src/components/layout/Sidebar.js
+++ b/src/components/layout/Sidebar.js
@@ -24,6 +24,14 @@ const messages = defineMessages({
24 id: 'sidebar.unmuteApp', 24 id: 'sidebar.unmuteApp',
25 defaultMessage: '!!!Enable notifications & audio', 25 defaultMessage: '!!!Enable notifications & audio',
26 }, 26 },
27 openWorkspaceDrawer: {
28 id: 'sidebar.openWorkspaceDrawer',
29 defaultMessage: '!!!Open workspace drawer',
30 },
31 closeWorkspaceDrawer: {
32 id: 'sidebar.closeWorkspaceDrawer',
33 defaultMessage: '!!!Close workspace drawer',
34 },
27}); 35});
28 36
29export default @observer class Sidebar extends Component { 37export default @observer class Sidebar extends Component {
@@ -31,6 +39,8 @@ export default @observer class Sidebar extends Component {
31 openSettings: PropTypes.func.isRequired, 39 openSettings: PropTypes.func.isRequired,
32 toggleMuteApp: PropTypes.func.isRequired, 40 toggleMuteApp: PropTypes.func.isRequired,
33 isAppMuted: PropTypes.bool.isRequired, 41 isAppMuted: PropTypes.bool.isRequired,
42 isWorkspaceDrawerOpen: PropTypes.bool.isRequired,
43 toggleWorkspaceDrawer: PropTypes.func.isRequired,
34 }; 44 };
35 45
36 static contextTypes = { 46 static contextTypes = {
@@ -53,9 +63,23 @@ export default @observer class Sidebar extends Component {
53 this.setState({ tooltipEnabled: false }); 63 this.setState({ tooltipEnabled: false });
54 } 64 }
55 65
66 updateToolTip() {
67 this.disableToolTip();
68 setTimeout(this.enableToolTip.bind(this));
69 }
70
56 render() { 71 render() {
57 const { openSettings, toggleMuteApp, isAppMuted } = this.props; 72 const {
73 openSettings,
74 toggleMuteApp,
75 isAppMuted,
76 isWorkspaceDrawerOpen,
77 toggleWorkspaceDrawer,
78 } = this.props;
58 const { intl } = this.context; 79 const { intl } = this.context;
80 const workspaceToggleMessage = (
81 isWorkspaceDrawerOpen ? messages.closeWorkspaceDrawer : messages.openWorkspaceDrawer
82 );
59 83
60 return ( 84 return (
61 <div className="sidebar"> 85 <div className="sidebar">
@@ -66,7 +90,21 @@ export default @observer class Sidebar extends Component {
66 /> 90 />
67 <button 91 <button
68 type="button" 92 type="button"
69 onClick={toggleMuteApp} 93 onClick={() => {
94 toggleWorkspaceDrawer();
95 this.updateToolTip();
96 }}
97 className={`sidebar__button sidebar__button--workspaces ${isWorkspaceDrawerOpen ? 'is-active' : ''}`}
98 data-tip={`${intl.formatMessage(workspaceToggleMessage)} (${ctrlKey}+Shift+D)`}
99 >
100 <i className="mdi mdi-view-grid" />
101 </button>
102 <button
103 type="button"
104 onClick={() => {
105 toggleMuteApp();
106 this.updateToolTip();
107 }}
70 className={`sidebar__button sidebar__button--audio ${isAppMuted ? 'is-muted' : ''}`} 108 className={`sidebar__button sidebar__button--audio ${isAppMuted ? 'is-muted' : ''}`}
71 data-tip={`${intl.formatMessage(isAppMuted ? messages.unmute : messages.mute)} (${ctrlKey}+Shift+M)`} 109 data-tip={`${intl.formatMessage(isAppMuted ? messages.unmute : messages.mute)} (${ctrlKey}+Shift+M)`}
72 > 110 >
diff --git a/src/components/services/content/ServiceView.js b/src/components/services/content/ServiceView.js
index 5afc54f9d..ada920cb6 100644
--- a/src/components/services/content/ServiceView.js
+++ b/src/components/services/content/ServiceView.js
@@ -37,6 +37,7 @@ export default @observer class ServiceView extends Component {
37 37
38 componentDidMount() { 38 componentDidMount() {
39 this.autorunDisposer = autorun(() => { 39 this.autorunDisposer = autorun(() => {
40 if (!this.isMounted) return;
40 if (this.props.service.isActive) { 41 if (this.props.service.isActive) {
41 this.setState({ forceRepaint: true }); 42 this.setState({ forceRepaint: true });
42 setTimeout(() => { 43 setTimeout(() => {
diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js
index 5a05ce431..772458eab 100644
--- a/src/containers/layout/AppLayoutContainer.js
+++ b/src/containers/layout/AppLayoutContainer.js
@@ -20,6 +20,9 @@ import Services from '../../components/services/content/Services';
20import AppLoader from '../../components/ui/AppLoader'; 20import AppLoader from '../../components/ui/AppLoader';
21 21
22import { state as delayAppState } from '../../features/delayApp'; 22import { state as delayAppState } from '../../features/delayApp';
23import { workspacesState } from '../../features/workspaces/state';
24import { workspaceActions } from '../../features/workspaces/actions';
25import WorkspaceDrawer from '../../features/workspaces/components/WorkspaceDrawer';
23 26
24export default @inject('stores', 'actions') @observer class AppLayoutContainer extends Component { 27export default @inject('stores', 'actions') @observer class AppLayoutContainer extends Component {
25 static defaultProps = { 28 static defaultProps = {
@@ -82,6 +85,14 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e
82 ); 85 );
83 } 86 }
84 87
88 const workspacesDrawer = (
89 <WorkspaceDrawer
90 getServicesForWorkspace={workspace => (
91 workspace ? workspace.services.map(id => services.one(id).name) : services.all.map(s => s.name)
92 )}
93 />
94 );
95
85 const sidebar = ( 96 const sidebar = (
86 <Sidebar 97 <Sidebar
87 services={services.allDisplayed} 98 services={services.allDisplayed}
@@ -96,6 +107,8 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e
96 deleteService={deleteService} 107 deleteService={deleteService}
97 updateService={updateService} 108 updateService={updateService}
98 toggleMuteApp={toggleMuteApp} 109 toggleMuteApp={toggleMuteApp}
110 toggleWorkspaceDrawer={workspaceActions.toggleWorkspaceDrawer}
111 isWorkspaceDrawerOpen={workspacesState.isWorkspaceDrawerOpen}
99 showMessageBadgeWhenMutedSetting={settings.all.app.showMessageBadgeWhenMuted} 112 showMessageBadgeWhenMutedSetting={settings.all.app.showMessageBadgeWhenMuted}
100 showMessageBadgesEvenWhenMuted={ui.showMessageBadgesEvenWhenMuted} 113 showMessageBadgesEvenWhenMuted={ui.showMessageBadgesEvenWhenMuted}
101 /> 114 />
@@ -122,6 +135,7 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e
122 showServicesUpdatedInfoBar={ui.showServicesUpdatedInfoBar} 135 showServicesUpdatedInfoBar={ui.showServicesUpdatedInfoBar}
123 appUpdateIsDownloaded={app.updateStatus === app.updateStatusTypes.DOWNLOADED} 136 appUpdateIsDownloaded={app.updateStatus === app.updateStatusTypes.DOWNLOADED}
124 sidebar={sidebar} 137 sidebar={sidebar}
138 workspacesDrawer={workspacesDrawer}
125 services={servicesContainer} 139 services={servicesContainer}
126 news={news.latest} 140 news={news.latest}
127 removeNewsItem={hide} 141 removeNewsItem={hide}
diff --git a/src/features/workspaces/actions.js b/src/features/workspaces/actions.js
index 25246de09..a85f8f57f 100644
--- a/src/features/workspaces/actions.js
+++ b/src/features/workspaces/actions.js
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
2import Workspace from './models/Workspace'; 2import Workspace from './models/Workspace';
3import { createActionsFromDefinitions } from '../../actions/lib/actions'; 3import { createActionsFromDefinitions } from '../../actions/lib/actions';
4 4
5export default createActionsFromDefinitions({ 5export const workspaceActions = createActionsFromDefinitions({
6 edit: { 6 edit: {
7 workspace: PropTypes.instanceOf(Workspace).isRequired, 7 workspace: PropTypes.instanceOf(Workspace).isRequired,
8 }, 8 },
@@ -19,4 +19,8 @@ export default createActionsFromDefinitions({
19 workspace: PropTypes.instanceOf(Workspace).isRequired, 19 workspace: PropTypes.instanceOf(Workspace).isRequired,
20 }, 20 },
21 deactivate: {}, 21 deactivate: {},
22 toggleWorkspaceDrawer: {},
23 openWorkspaceSettings: {},
22}, PropTypes.checkPropTypes); 24}, PropTypes.checkPropTypes);
25
26export default workspaceActions;
diff --git a/src/features/workspaces/components/WorkspaceDrawer.js b/src/features/workspaces/components/WorkspaceDrawer.js
new file mode 100644
index 000000000..27bf08361
--- /dev/null
+++ b/src/features/workspaces/components/WorkspaceDrawer.js
@@ -0,0 +1,94 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react';
4import injectSheet from 'react-jss';
5import { defineMessages, intlShape } from 'react-intl';
6import { H1, Icon } from '@meetfranz/ui';
7import { workspacesState } from '../state';
8import WorkspaceDrawerItem from './WorkspaceDrawerItem';
9import { workspaceActions } from '../actions';
10
11const messages = defineMessages({
12 headline: {
13 id: 'workspaceDrawer.headline',
14 defaultMessage: '!!!Workspaces',
15 },
16 allServices: {
17 id: 'workspaceDrawer.allServices',
18 defaultMessage: '!!!All services',
19 },
20});
21
22const styles = theme => ({
23 drawer: {
24 backgroundColor: theme.workspaceDrawerBackground,
25 width: theme.workspaceDrawerWidth,
26 },
27 headline: {
28 fontSize: '24px',
29 marginTop: '38px',
30 marginBottom: '25px',
31 marginLeft: '10px',
32 },
33 addWorkspaceButton: {
34 float: 'right',
35 marginRight: '10px',
36 marginTop: '2px',
37 },
38});
39
40@injectSheet(styles) @observer
41class WorkspaceDrawer extends Component {
42 static propTypes = {
43 classes: PropTypes.object.isRequired,
44 getServicesForWorkspace: PropTypes.func.isRequired,
45 };
46
47 static contextTypes = {
48 intl: intlShape,
49 };
50
51 render() {
52 const {
53 classes,
54 getServicesForWorkspace,
55 } = this.props;
56 const { intl } = this.context;
57
58 return (
59 <div className={classes.drawer}>
60 <H1 className={classes.headline}>
61 {intl.formatMessage(messages.headline)}
62 <span
63 className={classes.addWorkspaceButton}
64 onClick={workspaceActions.openWorkspaceSettings}
65 >
66 <Icon
67 icon="mdiPlusBox"
68 size={1.5}
69 />
70 </span>
71 </H1>
72 <div className={classes.workspaces}>
73 <WorkspaceDrawerItem
74 name={intl.formatMessage(messages.allServices)}
75 onClick={() => workspaceActions.deactivate()}
76 services={getServicesForWorkspace(null)}
77 isActive={workspacesState.activeWorkspace == null}
78 />
79 {workspacesState.workspaces.map(workspace => (
80 <WorkspaceDrawerItem
81 key={workspace.id}
82 name={workspace.name}
83 isActive={workspacesState.activeWorkspace === workspace}
84 onClick={() => workspaceActions.activate({ workspace })}
85 services={getServicesForWorkspace(workspace)}
86 />
87 ))}
88 </div>
89 </div>
90 );
91 }
92}
93
94export default WorkspaceDrawer;
diff --git a/src/features/workspaces/components/WorkspaceDrawerItem.js b/src/features/workspaces/components/WorkspaceDrawerItem.js
new file mode 100644
index 000000000..00207d323
--- /dev/null
+++ b/src/features/workspaces/components/WorkspaceDrawerItem.js
@@ -0,0 +1,88 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react';
4import injectSheet from 'react-jss';
5import classnames from 'classnames';
6
7const styles = theme => ({
8 item: {
9 height: '67px',
10 padding: '15px 10px',
11 borderBottom: `1px solid ${theme.workspaceDrawerItemBorder}`,
12 '&:first-child': {
13 borderTop: `1px solid ${theme.workspaceDrawerItemBorder}`,
14 },
15 },
16 isActiveItem: {
17 backgroundColor: theme.workspaceDrawerItemActiveBackground,
18 },
19 name: {
20 marginTop: '4px',
21 color: theme.workspaceDrawerNameColor,
22 },
23 activeName: {
24 color: theme.workspaceDrawerNameActiveColor,
25 },
26 services: {
27 display: 'block',
28 fontSize: '11px',
29 marginTop: '5px',
30 color: theme.workspaceDrawerServicesColor,
31 whiteSpace: 'nowrap',
32 textOverflow: 'ellipsis',
33 overflow: 'hidden',
34 lineHeight: '15px',
35 },
36 activeServices: {
37 color: theme.workspaceDrawerServicesActiveColor,
38 },
39});
40
41@injectSheet(styles) @observer
42class WorkspaceDrawerItem extends Component {
43 static propTypes = {
44 classes: PropTypes.object.isRequired,
45 isActive: PropTypes.bool.isRequired,
46 name: PropTypes.string.isRequired,
47 onClick: PropTypes.func.isRequired,
48 services: PropTypes.arrayOf(PropTypes.string).isRequired,
49 };
50
51 render() {
52 const {
53 classes,
54 isActive,
55 name,
56 onClick,
57 services,
58 } = this.props;
59 return (
60 <div
61 className={classnames([
62 classes.item,
63 isActive ? classes.isActiveItem : null,
64 ])}
65 onClick={onClick}
66 >
67 <span
68 className={classnames([
69 classes.name,
70 isActive ? classes.activeName : null,
71 ])}
72 >
73 {name}
74 </span>
75 <span
76 className={classnames([
77 classes.services,
78 isActive ? classes.activeServices : null,
79 ])}
80 >
81 {services.join(', ')}
82 </span>
83 </div>
84 );
85 }
86}
87
88export default WorkspaceDrawerItem;
diff --git a/src/features/workspaces/state.js b/src/features/workspaces/state.js
index 963b96f81..68a7d8d91 100644
--- a/src/features/workspaces/state.js
+++ b/src/features/workspaces/state.js
@@ -4,6 +4,7 @@ const defaultState = {
4 activeWorkspace: null, 4 activeWorkspace: null,
5 isLoading: false, 5 isLoading: false,
6 isFeatureActive: false, 6 isFeatureActive: false,
7 isWorkspaceDrawerOpen: false,
7 workspaces: [], 8 workspaces: [],
8 workspaceBeingEdited: null, 9 workspaceBeingEdited: null,
9}; 10};
diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js
index a2997a0d2..1b57ba2da 100644
--- a/src/features/workspaces/store.js
+++ b/src/features/workspaces/store.js
@@ -3,7 +3,7 @@ import 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'; 6import { workspaceActions } from './actions';
7 7
8const debug = require('debug')('Franz:feature:workspaces'); 8const debug = require('debug')('Franz:feature:workspaces');
9 9
@@ -55,6 +55,8 @@ export default class WorkspacesStore extends Store {
55 workspaceActions.update.listen(this._update); 55 workspaceActions.update.listen(this._update);
56 workspaceActions.activate.listen(this._setActiveWorkspace); 56 workspaceActions.activate.listen(this._setActiveWorkspace);
57 workspaceActions.deactivate.listen(this._deactivateActiveWorkspace); 57 workspaceActions.deactivate.listen(this._deactivateActiveWorkspace);
58 workspaceActions.toggleWorkspaceDrawer.listen(this._toggleWorkspaceDrawer);
59 workspaceActions.openWorkspaceSettings.listen(this._openWorkspaceSettings);
58 } 60 }
59 61
60 _getWorkspaceById = id => this.state.workspaces.find(w => w.id === id); 62 _getWorkspaceById = id => this.state.workspaces.find(w => w.id === id);
@@ -111,4 +113,12 @@ export default class WorkspacesStore extends Store {
111 @action _deactivateActiveWorkspace = () => { 113 @action _deactivateActiveWorkspace = () => {
112 this.state.activeWorkspace = null; 114 this.state.activeWorkspace = null;
113 }; 115 };
116
117 @action _toggleWorkspaceDrawer = () => {
118 this.state.isWorkspaceDrawerOpen = !this.state.isWorkspaceDrawerOpen;
119 };
120
121 @action _openWorkspaceSettings = () => {
122 this.actions.ui.openSettings({ path: 'workspaces' });
123 };
114} 124}
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json
index c8bb03ded..4bf9a8c0a 100644
--- a/src/i18n/locales/defaultMessages.json
+++ b/src/i18n/locales/defaultMessages.json
@@ -755,6 +755,32 @@
755 "column": 10, 755 "column": 10,
756 "line": 23 756 "line": 23
757 } 757 }
758 },
759 {
760 "defaultMessage": "!!!Open workspace drawer",
761 "end": {
762 "column": 3,
763 "line": 30
764 },
765 "file": "src/components/layout/Sidebar.js",
766 "id": "sidebar.openWorkspaceDrawer",
767 "start": {
768 "column": 23,
769 "line": 27
770 }
771 },
772 {
773 "defaultMessage": "!!!Close workspace drawer",
774 "end": {
775 "column": 3,
776 "line": 34
777 },
778 "file": "src/components/layout/Sidebar.js",
779 "id": "sidebar.closeWorkspaceDrawer",
780 "start": {
781 "column": 24,
782 "line": 31
783 }
758 } 784 }
759 ], 785 ],
760 "path": "src/components/layout/Sidebar.json" 786 "path": "src/components/layout/Sidebar.json"
@@ -3279,6 +3305,37 @@
3279 { 3305 {
3280 "descriptors": [ 3306 "descriptors": [
3281 { 3307 {
3308 "defaultMessage": "!!!Workspaces",
3309 "end": {
3310 "column": 3,
3311 "line": 15
3312 },
3313 "file": "src/features/workspaces/components/WorkspaceDrawer.js",
3314 "id": "workspaceDrawer.headline",
3315 "start": {
3316 "column": 12,
3317 "line": 12
3318 }
3319 },
3320 {
3321 "defaultMessage": "!!!All services",
3322 "end": {
3323 "column": 3,
3324 "line": 19
3325 },
3326 "file": "src/features/workspaces/components/WorkspaceDrawer.js",
3327 "id": "workspaceDrawer.allServices",
3328 "start": {
3329 "column": 15,
3330 "line": 16
3331 }
3332 }
3333 ],
3334 "path": "src/features/workspaces/components/WorkspaceDrawer.json"
3335 },
3336 {
3337 "descriptors": [
3338 {
3282 "defaultMessage": "!!!Your workspaces", 3339 "defaultMessage": "!!!Your workspaces",
3283 "end": { 3340 "end": {
3284 "column": 3, 3341 "column": 3,
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 1041a8b5f..94c6fcf32 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -249,7 +249,9 @@
249 "settings.workspaces.headline": "Your workspaces", 249 "settings.workspaces.headline": "Your workspaces",
250 "settings.workspaces.noWorkspacesAdded": "You haven't added any workspaces yet.", 250 "settings.workspaces.noWorkspacesAdded": "You haven't added any workspaces yet.",
251 "sidebar.addNewService": "Add new service", 251 "sidebar.addNewService": "Add new service",
252 "sidebar.closeWorkspaceDrawer": "Close workspace drawer",
252 "sidebar.muteApp": "Disable notifications & audio", 253 "sidebar.muteApp": "Disable notifications & audio",
254 "sidebar.openWorkspaceDrawer": "Open workspace drawer",
253 "sidebar.settings": "Settings", 255 "sidebar.settings": "Settings",
254 "sidebar.unmuteApp": "Enable notifications & audio", 256 "sidebar.unmuteApp": "Enable notifications & audio",
255 "signup.company.label": "Company", 257 "signup.company.label": "Company",
@@ -294,5 +296,7 @@
294 "validation.required": "{field} is required", 296 "validation.required": "{field} is required",
295 "validation.url": "{field} is not a valid URL", 297 "validation.url": "{field} is not a valid URL",
296 "welcome.loginButton": "Login to your account", 298 "welcome.loginButton": "Login to your account",
297 "welcome.signupButton": "Create a free account" 299 "welcome.signupButton": "Create a free account",
298} \ No newline at end of file 300 "workspaceDrawer.allServices": "All services",
301 "workspaceDrawer.headline": "Workspaces"
302}
diff --git a/src/i18n/messages/src/components/layout/AppLayout.json b/src/i18n/messages/src/components/layout/AppLayout.json
index 07603d062..85d3e8696 100644
--- a/src/i18n/messages/src/components/layout/AppLayout.json
+++ b/src/i18n/messages/src/components/layout/AppLayout.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Your services have been updated.", 4 "defaultMessage": "!!!Your services have been updated.",
5 "file": "src/components/layout/AppLayout.js", 5 "file": "src/components/layout/AppLayout.js",
6 "start": { 6 "start": {
7 "line": 22, 7 "line": 24,
8 "column": 19 8 "column": 19
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 25, 11 "line": 27,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!A new update for Franz is available.", 17 "defaultMessage": "!!!A new update for Franz is available.",
18 "file": "src/components/layout/AppLayout.js", 18 "file": "src/components/layout/AppLayout.js",
19 "start": { 19 "start": {
20 "line": 26, 20 "line": 28,
21 "column": 19 21 "column": 19
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 29, 24 "line": 31,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Reload services", 30 "defaultMessage": "!!!Reload services",
31 "file": "src/components/layout/AppLayout.js", 31 "file": "src/components/layout/AppLayout.js",
32 "start": { 32 "start": {
33 "line": 30, 33 "line": 32,
34 "column": 24 34 "column": 24
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 33, 37 "line": 35,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,11 @@
43 "defaultMessage": "!!!Changelog", 43 "defaultMessage": "!!!Changelog",
44 "file": "src/components/layout/AppLayout.js", 44 "file": "src/components/layout/AppLayout.js",
45 "start": { 45 "start": {
46 "line": 34, 46 "line": 36,
47 "column": 13 47 "column": 13
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 37, 50 "line": 39,
51 "column": 3 51 "column": 3
52 } 52 }
53 }, 53 },
@@ -56,11 +56,11 @@
56 "defaultMessage": "!!!Restart & install update", 56 "defaultMessage": "!!!Restart & install update",
57 "file": "src/components/layout/AppLayout.js", 57 "file": "src/components/layout/AppLayout.js",
58 "start": { 58 "start": {
59 "line": 38, 59 "line": 40,
60 "column": 23 60 "column": 23
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 41, 63 "line": 43,
64 "column": 3 64 "column": 3
65 } 65 }
66 }, 66 },
@@ -69,11 +69,11 @@
69 "defaultMessage": "!!!Could not load services and user information", 69 "defaultMessage": "!!!Could not load services and user information",
70 "file": "src/components/layout/AppLayout.js", 70 "file": "src/components/layout/AppLayout.js",
71 "start": { 71 "start": {
72 "line": 42, 72 "line": 44,
73 "column": 26 73 "column": 26
74 }, 74 },
75 "end": { 75 "end": {
76 "line": 45, 76 "line": 47,
77 "column": 3 77 "column": 3
78 } 78 }
79 } 79 }
diff --git a/src/i18n/messages/src/components/layout/Sidebar.json b/src/i18n/messages/src/components/layout/Sidebar.json
index 7aa00a186..0254af8da 100644
--- a/src/i18n/messages/src/components/layout/Sidebar.json
+++ b/src/i18n/messages/src/components/layout/Sidebar.json
@@ -50,5 +50,31 @@
50 "line": 26, 50 "line": 26,
51 "column": 3 51 "column": 3
52 } 52 }
53 },
54 {
55 "id": "sidebar.openWorkspaceDrawer",
56 "defaultMessage": "!!!Open workspace drawer",
57 "file": "src/components/layout/Sidebar.js",
58 "start": {
59 "line": 27,
60 "column": 23
61 },
62 "end": {
63 "line": 30,
64 "column": 3
65 }
66 },
67 {
68 "id": "sidebar.closeWorkspaceDrawer",
69 "defaultMessage": "!!!Close workspace drawer",
70 "file": "src/components/layout/Sidebar.js",
71 "start": {
72 "line": 31,
73 "column": 24
74 },
75 "end": {
76 "line": 34,
77 "column": 3
78 }
53 } 79 }
54] \ No newline at end of file 80] \ No newline at end of file
diff --git a/src/i18n/messages/src/features/workspaces/components/WorkspaceDrawer.json b/src/i18n/messages/src/features/workspaces/components/WorkspaceDrawer.json
new file mode 100644
index 000000000..c875b82cb
--- /dev/null
+++ b/src/i18n/messages/src/features/workspaces/components/WorkspaceDrawer.json
@@ -0,0 +1,28 @@
1[
2 {
3 "id": "workspaceDrawer.headline",
4 "defaultMessage": "!!!Workspaces",
5 "file": "src/features/workspaces/components/WorkspaceDrawer.js",
6 "start": {
7 "line": 12,
8 "column": 12
9 },
10 "end": {
11 "line": 15,
12 "column": 3
13 }
14 },
15 {
16 "id": "workspaceDrawer.allServices",
17 "defaultMessage": "!!!All services",
18 "file": "src/features/workspaces/components/WorkspaceDrawer.js",
19 "start": {
20 "line": 16,
21 "column": 15
22 },
23 "end": {
24 "line": 19,
25 "column": 3
26 }
27 }
28] \ No newline at end of file
diff --git a/src/lib/Menu.js b/src/lib/Menu.js
index b21a62b4d..7b224802f 100644
--- a/src/lib/Menu.js
+++ b/src/lib/Menu.js
@@ -4,7 +4,7 @@ import { defineMessages } from 'react-intl';
4 4
5import { isMac, ctrlKey, cmdKey } from '../environment'; 5import { isMac, ctrlKey, cmdKey } from '../environment';
6import { workspacesState } from '../features/workspaces/state'; 6import { workspacesState } from '../features/workspaces/state';
7import workspaceActions from '../features/workspaces/actions'; 7import { workspaceActions } from '../features/workspaces/actions';
8 8
9const { app, Menu, dialog } = remote; 9const { app, Menu, dialog } = remote;
10 10
@@ -785,7 +785,7 @@ export default class FranzMenu {
785 label: intl.formatMessage(menuItems.addNewWorkspace), 785 label: intl.formatMessage(menuItems.addNewWorkspace),
786 accelerator: `${cmdKey}+Shift+N`, 786 accelerator: `${cmdKey}+Shift+N`,
787 click: () => { 787 click: () => {
788 this.actions.ui.openSettings({ path: 'workspaces' }); 788 workspaceActions.openWorkspaceSettings();
789 }, 789 },
790 enabled: this.stores.user.isLoggedIn, 790 enabled: this.stores.user.isLoggedIn,
791 }, { 791 }, {
diff --git a/src/styles/layout.scss b/src/styles/layout.scss
index 9a003a922..78e9e68f1 100644
--- a/src/styles/layout.scss
+++ b/src/styles/layout.scss
@@ -18,8 +18,14 @@ html { overflow: hidden; }
18 font-size: 22px; 18 font-size: 22px;
19 19
20 &:hover, 20 &:hover,
21 &:active { color: $dark-theme-gray-smoke; } 21 &:active {
22 &.is-muted { color: $theme-brand-primary; } 22 color: $dark-theme-gray-smoke;
23 }
24
25 &.is-muted,
26 &.is-active {
27 color: $theme-brand-primary;
28 }
23 } 29 }
24 } 30 }
25 31
@@ -84,7 +90,7 @@ html { overflow: hidden; }
84 90
85 &:hover, 91 &:hover,
86 &:active { color: lighten($theme-gray-light, 10%); } 92 &:active { color: lighten($theme-gray-light, 10%); }
87 &.is-muted { color: $theme-brand-primary; } 93 &.is-muted, &.is-active { color: $theme-brand-primary; }
88 &--new-service { padding-bottom: 6px; } 94 &--new-service { padding-bottom: 6px; }
89 } 95 }
90 96