diff options
-rw-r--r-- | packages/theme/src/themes/dark/index.ts | 9 | ||||
-rw-r--r-- | packages/theme/src/themes/default/index.ts | 10 | ||||
-rw-r--r-- | src/components/layout/AppLayout.js | 24 | ||||
-rw-r--r-- | src/components/layout/Sidebar.js | 42 | ||||
-rw-r--r-- | src/components/services/content/ServiceView.js | 1 | ||||
-rw-r--r-- | src/containers/layout/AppLayoutContainer.js | 14 | ||||
-rw-r--r-- | src/features/workspaces/actions.js | 6 | ||||
-rw-r--r-- | src/features/workspaces/components/WorkspaceDrawer.js | 94 | ||||
-rw-r--r-- | src/features/workspaces/components/WorkspaceDrawerItem.js | 88 | ||||
-rw-r--r-- | src/features/workspaces/state.js | 1 | ||||
-rw-r--r-- | src/features/workspaces/store.js | 12 | ||||
-rw-r--r-- | src/i18n/locales/defaultMessages.json | 57 | ||||
-rw-r--r-- | src/i18n/locales/en-US.json | 8 | ||||
-rw-r--r-- | src/i18n/messages/src/components/layout/AppLayout.json | 24 | ||||
-rw-r--r-- | src/i18n/messages/src/components/layout/Sidebar.json | 26 | ||||
-rw-r--r-- | src/i18n/messages/src/features/workspaces/components/WorkspaceDrawer.json | 28 | ||||
-rw-r--r-- | src/lib/Menu.js | 4 | ||||
-rw-r--r-- | src/styles/layout.scss | 12 |
18 files changed, 435 insertions, 25 deletions
diff --git a/packages/theme/src/themes/dark/index.ts b/packages/theme/src/themes/dark/index.ts index 3a56719b2..eaa552961 100644 --- a/packages/theme/src/themes/dark/index.ts +++ b/packages/theme/src/themes/dark/index.ts | |||
@@ -63,3 +63,12 @@ export const selectSearchColor = inputBackground; | |||
63 | 63 | ||
64 | // Modal | 64 | // Modal |
65 | export const colorModalOverlayBackground = color(legacyStyles.darkThemeBlack).alpha(0.8).rgb().string(); | 65 | export const colorModalOverlayBackground = color(legacyStyles.darkThemeBlack).alpha(0.8).rgb().string(); |
66 | |||
67 | // Workspace Drawer | ||
68 | export const workspaceDrawerBackground = color(colorBackground).lighten(0.3).hex(); | ||
69 | export const workspaceDrawerItemBorder = color(workspaceDrawerBackground).lighten(0.2).hex(); | ||
70 | export const workspaceDrawerItemActiveBackground = defaultStyles.brandPrimary; | ||
71 | export const workspaceDrawerNameColor = colorText; | ||
72 | export const workspaceDrawerNameActiveColor = 'white'; | ||
73 | export const workspaceDrawerServicesColor = color(colorText).darken(0.5).hex(); | ||
74 | export const workspaceDrawerServicesActiveColor = color(defaultStyles.brandPrimary).lighten(0.5).hex(); | ||
diff --git a/packages/theme/src/themes/default/index.ts b/packages/theme/src/themes/default/index.ts index 8a71e61cf..fc03b67de 100644 --- a/packages/theme/src/themes/default/index.ts +++ b/packages/theme/src/themes/default/index.ts | |||
@@ -140,3 +140,13 @@ export const badgeBorderRadius = 50; | |||
140 | 140 | ||
141 | // Modal | 141 | // Modal |
142 | export const colorModalOverlayBackground = color('#000').alpha(0.5).rgb().string(); | 142 | export const colorModalOverlayBackground = color('#000').alpha(0.5).rgb().string(); |
143 | |||
144 | // Workspace Drawer | ||
145 | export const workspaceDrawerWidth = '220px'; | ||
146 | export const workspaceDrawerBackground = color(colorBackground).lighten(0.1).hex(); | ||
147 | export const workspaceDrawerItemActiveBackground = legacyStyles.themeGrayLightest; | ||
148 | export const workspaceDrawerItemBorder = color(workspaceDrawerBackground).darken(0.05).hex(); | ||
149 | export const workspaceDrawerNameColor = colorText; | ||
150 | export const workspaceDrawerNameActiveColor = colorText; | ||
151 | export const workspaceDrawerServicesColor = color(colorText).lighten(1.5).hex(); | ||
152 | export const workspaceDrawerServicesActiveColor = workspaceDrawerServicesColor; | ||
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'; | |||
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; |
4 | import { defineMessages, intlShape } from 'react-intl'; | 4 | import { defineMessages, intlShape } from 'react-intl'; |
5 | import { TitleBar } from 'electron-react-titlebar'; | 5 | import { TitleBar } from 'electron-react-titlebar'; |
6 | import injectSheet from 'react-jss'; | ||
6 | 7 | ||
7 | import InfoBar from '../ui/InfoBar'; | 8 | import InfoBar from '../ui/InfoBar'; |
8 | import { Component as DelayApp } from '../../features/delayApp'; | 9 | import { 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 | ||
15 | import { isWindows } from '../../environment'; | 16 | import { isWindows } from '../../environment'; |
17 | import { workspacesState } from '../../features/workspaces/state'; | ||
16 | 18 | ||
17 | function createMarkup(HTMLString) { | 19 | function createMarkup(HTMLString) { |
18 | return { __html: HTMLString }; | 20 | return { __html: HTMLString }; |
@@ -45,10 +47,23 @@ const messages = defineMessages({ | |||
45 | }, | 47 | }, |
46 | }); | 48 | }); |
47 | 49 | ||
48 | export default @observer class AppLayout extends Component { | 50 | const 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 | ||
61 | class 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 | |||
198 | export 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 | ||
29 | export default @observer class Sidebar extends Component { | 37 | export 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'; | |||
20 | import AppLoader from '../../components/ui/AppLoader'; | 20 | import AppLoader from '../../components/ui/AppLoader'; |
21 | 21 | ||
22 | import { state as delayAppState } from '../../features/delayApp'; | 22 | import { state as delayAppState } from '../../features/delayApp'; |
23 | import { workspacesState } from '../../features/workspaces/state'; | ||
24 | import { workspaceActions } from '../../features/workspaces/actions'; | ||
25 | import WorkspaceDrawer from '../../features/workspaces/components/WorkspaceDrawer'; | ||
23 | 26 | ||
24 | export default @inject('stores', 'actions') @observer class AppLayoutContainer extends Component { | 27 | export 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'; | |||
2 | import Workspace from './models/Workspace'; | 2 | import Workspace from './models/Workspace'; |
3 | import { createActionsFromDefinitions } from '../../actions/lib/actions'; | 3 | import { createActionsFromDefinitions } from '../../actions/lib/actions'; |
4 | 4 | ||
5 | export default createActionsFromDefinitions({ | 5 | export 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 | |||
26 | export 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 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import injectSheet from 'react-jss'; | ||
5 | import { defineMessages, intlShape } from 'react-intl'; | ||
6 | import { H1, Icon } from '@meetfranz/ui'; | ||
7 | import { workspacesState } from '../state'; | ||
8 | import WorkspaceDrawerItem from './WorkspaceDrawerItem'; | ||
9 | import { workspaceActions } from '../actions'; | ||
10 | |||
11 | const 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 | |||
22 | const 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 | ||
41 | class 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 | |||
94 | export 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 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import injectSheet from 'react-jss'; | ||
5 | import classnames from 'classnames'; | ||
6 | |||
7 | const 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 | ||
42 | class 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 | |||
88 | export 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'; | |||
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 | import { workspaceActions } from './actions'; |
7 | 7 | ||
8 | const debug = require('debug')('Franz:feature:workspaces'); | 8 | const 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 | ||
5 | import { isMac, ctrlKey, cmdKey } from '../environment'; | 5 | import { isMac, ctrlKey, cmdKey } from '../environment'; |
6 | import { workspacesState } from '../features/workspaces/state'; | 6 | import { workspacesState } from '../features/workspaces/state'; |
7 | import workspaceActions from '../features/workspaces/actions'; | 7 | import { workspaceActions } from '../features/workspaces/actions'; |
8 | 8 | ||
9 | const { app, Menu, dialog } = remote; | 9 | const { 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 | ||