diff options
Diffstat (limited to 'src/components/layout')
-rw-r--r-- | src/components/layout/AppLayout.js | 26 | ||||
-rw-r--r-- | src/components/layout/Sidebar.js | 151 |
2 files changed, 121 insertions, 56 deletions
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js index 9b110262a..80e6daf19 100644 --- a/src/components/layout/AppLayout.js +++ b/src/components/layout/AppLayout.js | |||
@@ -6,9 +6,9 @@ import { TitleBar } from 'electron-react-titlebar'; | |||
6 | import injectSheet from 'react-jss'; | 6 | import injectSheet from 'react-jss'; |
7 | 7 | ||
8 | import InfoBar from '../ui/InfoBar'; | 8 | import InfoBar from '../ui/InfoBar'; |
9 | import { Component as DelayApp } from '../../features/delayApp'; | ||
10 | import { Component as BasicAuth } from '../../features/basicAuth'; | 9 | import { Component as BasicAuth } from '../../features/basicAuth'; |
11 | import { Component as ShareFranz } from '../../features/shareFranz'; | 10 | import { Component as ShareFranz } from '../../features/shareFranz'; |
11 | import { Component as QuickSwitch } from '../../features/quickSwitch'; | ||
12 | import ErrorBoundary from '../util/ErrorBoundary'; | 12 | import ErrorBoundary from '../util/ErrorBoundary'; |
13 | 13 | ||
14 | // import globalMessages from '../../i18n/globalMessages'; | 14 | // import globalMessages from '../../i18n/globalMessages'; |
@@ -39,6 +39,10 @@ const messages = defineMessages({ | |||
39 | id: 'infobar.requiredRequestsFailed', | 39 | id: 'infobar.requiredRequestsFailed', |
40 | defaultMessage: '!!!Could not load services and user information', | 40 | defaultMessage: '!!!Could not load services and user information', |
41 | }, | 41 | }, |
42 | authRequestFailed: { | ||
43 | id: 'infobar.authRequestFailed', | ||
44 | defaultMessage: '!!!There were errors while trying to perform an authenticated request. Please try logging out and back in if this error persists.', | ||
45 | }, | ||
42 | }); | 46 | }); |
43 | 47 | ||
44 | const styles = theme => ({ | 48 | const styles = theme => ({ |
@@ -65,6 +69,7 @@ class AppLayout extends Component { | |||
65 | showServicesUpdatedInfoBar: PropTypes.bool.isRequired, | 69 | showServicesUpdatedInfoBar: PropTypes.bool.isRequired, |
66 | appUpdateIsDownloaded: PropTypes.bool.isRequired, | 70 | appUpdateIsDownloaded: PropTypes.bool.isRequired, |
67 | nextAppReleaseVersion: PropTypes.string, | 71 | nextAppReleaseVersion: PropTypes.string, |
72 | authRequestFailed: PropTypes.bool.isRequired, | ||
68 | removeNewsItem: PropTypes.func.isRequired, | 73 | removeNewsItem: PropTypes.func.isRequired, |
69 | reloadServicesAfterUpdate: PropTypes.func.isRequired, | 74 | reloadServicesAfterUpdate: PropTypes.func.isRequired, |
70 | installAppUpdate: PropTypes.func.isRequired, | 75 | installAppUpdate: PropTypes.func.isRequired, |
@@ -72,7 +77,6 @@ class AppLayout extends Component { | |||
72 | areRequiredRequestsSuccessful: PropTypes.bool.isRequired, | 77 | areRequiredRequestsSuccessful: PropTypes.bool.isRequired, |
73 | retryRequiredRequests: PropTypes.func.isRequired, | 78 | retryRequiredRequests: PropTypes.func.isRequired, |
74 | areRequiredRequestsLoading: PropTypes.bool.isRequired, | 79 | areRequiredRequestsLoading: PropTypes.bool.isRequired, |
75 | isDelayAppScreenVisible: PropTypes.bool.isRequired, | ||
76 | hasActivatedTrial: PropTypes.bool.isRequired, | 80 | hasActivatedTrial: PropTypes.bool.isRequired, |
77 | }; | 81 | }; |
78 | 82 | ||
@@ -97,6 +101,7 @@ class AppLayout extends Component { | |||
97 | showServicesUpdatedInfoBar, | 101 | showServicesUpdatedInfoBar, |
98 | appUpdateIsDownloaded, | 102 | appUpdateIsDownloaded, |
99 | nextAppReleaseVersion, | 103 | nextAppReleaseVersion, |
104 | authRequestFailed, | ||
100 | removeNewsItem, | 105 | removeNewsItem, |
101 | reloadServicesAfterUpdate, | 106 | reloadServicesAfterUpdate, |
102 | installAppUpdate, | 107 | installAppUpdate, |
@@ -104,7 +109,6 @@ class AppLayout extends Component { | |||
104 | areRequiredRequestsSuccessful, | 109 | areRequiredRequestsSuccessful, |
105 | retryRequiredRequests, | 110 | retryRequiredRequests, |
106 | areRequiredRequestsLoading, | 111 | areRequiredRequestsLoading, |
107 | isDelayAppScreenVisible, | ||
108 | hasActivatedTrial, | 112 | hasActivatedTrial, |
109 | } = this.props; | 113 | } = this.props; |
110 | 114 | ||
@@ -113,7 +117,7 @@ class AppLayout extends Component { | |||
113 | return ( | 117 | return ( |
114 | <ErrorBoundary> | 118 | <ErrorBoundary> |
115 | <div className="app"> | 119 | <div className="app"> |
116 | {isWindows && !isFullScreen && <TitleBar menu={window.franz.menu.template} icon="assets/images/logo.svg" />} | 120 | {isWindows && !isFullScreen && <TitleBar menu={window.ferdi.menu.template} icon="assets/images/logo.svg" />} |
117 | <div className={`app__content ${classes.appContent}`}> | 121 | <div className={`app__content ${classes.appContent}`}> |
118 | {workspacesDrawer} | 122 | {workspacesDrawer} |
119 | {sidebar} | 123 | {sidebar} |
@@ -153,6 +157,18 @@ class AppLayout extends Component { | |||
153 | {intl.formatMessage(messages.requiredRequestsFailed)} | 157 | {intl.formatMessage(messages.requiredRequestsFailed)} |
154 | </InfoBar> | 158 | </InfoBar> |
155 | )} | 159 | )} |
160 | {authRequestFailed && ( | ||
161 | <InfoBar | ||
162 | type="danger" | ||
163 | ctaLabel="Try again" | ||
164 | ctaLoading={areRequiredRequestsLoading} | ||
165 | sticky | ||
166 | onClick={retryRequiredRequests} | ||
167 | > | ||
168 | <span className="mdi mdi-flash" /> | ||
169 | {intl.formatMessage(messages.authRequestFailed)} | ||
170 | </InfoBar> | ||
171 | )} | ||
156 | {showServicesUpdatedInfoBar && ( | 172 | {showServicesUpdatedInfoBar && ( |
157 | <InfoBar | 173 | <InfoBar |
158 | type="primary" | 174 | type="primary" |
@@ -170,9 +186,9 @@ class AppLayout extends Component { | |||
170 | onInstallUpdate={installAppUpdate} | 186 | onInstallUpdate={installAppUpdate} |
171 | /> | 187 | /> |
172 | )} | 188 | )} |
173 | {isDelayAppScreenVisible && (<DelayApp />)} | ||
174 | <BasicAuth /> | 189 | <BasicAuth /> |
175 | <ShareFranz /> | 190 | <ShareFranz /> |
191 | <QuickSwitch /> | ||
176 | {services} | 192 | {services} |
177 | {children} | 193 | {children} |
178 | <TrialStatusBar /> | 194 | <TrialStatusBar /> |
diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js index 918298011..48a83c5a1 100644 --- a/src/components/layout/Sidebar.js +++ b/src/components/layout/Sidebar.js | |||
@@ -2,13 +2,13 @@ import React, { Component } from 'react'; | |||
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import ReactTooltip from 'react-tooltip'; | 3 | import ReactTooltip from 'react-tooltip'; |
4 | import { defineMessages, intlShape } from 'react-intl'; | 4 | import { defineMessages, intlShape } from 'react-intl'; |
5 | import { observer } from 'mobx-react'; | 5 | import { inject, observer } from 'mobx-react'; |
6 | import { Link } from 'react-router'; | ||
6 | 7 | ||
7 | import Tabbar from '../services/tabs/Tabbar'; | 8 | import Tabbar from '../services/tabs/Tabbar'; |
8 | import { ctrlKey } from '../../environment'; | 9 | import { ctrlKey } from '../../environment'; |
9 | import { GA_CATEGORY_WORKSPACES, workspaceStore } from '../../features/workspaces'; | 10 | import { workspaceStore } from '../../features/workspaces'; |
10 | import { gaEvent } from '../../lib/analytics'; | 11 | import { todosStore } from '../../features/todos'; |
11 | import { todosStore, GA_CATEGORY_TODOS } from '../../features/todos'; | ||
12 | import { todoActions } from '../../features/todos/actions'; | 12 | import { todoActions } from '../../features/todos/actions'; |
13 | 13 | ||
14 | const messages = defineMessages({ | 14 | const messages = defineMessages({ |
@@ -44,9 +44,13 @@ const messages = defineMessages({ | |||
44 | id: 'sidebar.closeTodosDrawer', | 44 | id: 'sidebar.closeTodosDrawer', |
45 | defaultMessage: '!!!Close Franz Todos', | 45 | defaultMessage: '!!!Close Franz Todos', |
46 | }, | 46 | }, |
47 | lockFerdi: { | ||
48 | id: 'sidebar.lockFerdi', | ||
49 | defaultMessage: '!!!Lock Ferdi', | ||
50 | }, | ||
47 | }); | 51 | }); |
48 | 52 | ||
49 | export default @observer class Sidebar extends Component { | 53 | export default @inject('stores', 'actions') @observer class Sidebar extends Component { |
50 | static propTypes = { | 54 | static propTypes = { |
51 | openSettings: PropTypes.func.isRequired, | 55 | openSettings: PropTypes.func.isRequired, |
52 | toggleMuteApp: PropTypes.func.isRequired, | 56 | toggleMuteApp: PropTypes.func.isRequired, |
@@ -87,6 +91,8 @@ export default @observer class Sidebar extends Component { | |||
87 | isAppMuted, | 91 | isAppMuted, |
88 | isWorkspaceDrawerOpen, | 92 | isWorkspaceDrawerOpen, |
89 | toggleWorkspaceDrawer, | 93 | toggleWorkspaceDrawer, |
94 | stores, | ||
95 | actions, | ||
90 | } = this.props; | 96 | } = this.props; |
91 | const { intl } = this.context; | 97 | const { intl } = this.context; |
92 | const todosToggleMessage = ( | 98 | const todosToggleMessage = ( |
@@ -96,6 +102,7 @@ export default @observer class Sidebar extends Component { | |||
96 | const workspaceToggleMessage = ( | 102 | const workspaceToggleMessage = ( |
97 | isWorkspaceDrawerOpen ? messages.closeWorkspaceDrawer : messages.openWorkspaceDrawer | 103 | isWorkspaceDrawerOpen ? messages.closeWorkspaceDrawer : messages.openWorkspaceDrawer |
98 | ); | 104 | ); |
105 | const isLoggedIn = Boolean(localStorage.getItem('authToken')); | ||
99 | 106 | ||
100 | return ( | 107 | return ( |
101 | <div className="sidebar"> | 108 | <div className="sidebar"> |
@@ -104,53 +111,89 @@ export default @observer class Sidebar extends Component { | |||
104 | enableToolTip={() => this.enableToolTip()} | 111 | enableToolTip={() => this.enableToolTip()} |
105 | disableToolTip={() => this.disableToolTip()} | 112 | disableToolTip={() => this.disableToolTip()} |
106 | /> | 113 | /> |
107 | {todosStore.isFeatureEnabled && todosStore.isFeatureEnabledByUser ? ( | 114 | { isLoggedIn ? ( |
108 | <button | 115 | <> |
109 | type="button" | 116 | { stores.settings.all.app.lockingFeatureEnabled ? ( |
110 | onClick={() => { | 117 | <button |
111 | todoActions.toggleTodosPanel(); | 118 | type="button" |
112 | this.updateToolTip(); | 119 | className="sidebar__button" |
113 | gaEvent(GA_CATEGORY_TODOS, 'toggleDrawer', 'sidebar'); | 120 | onClick={() => { |
114 | }} | 121 | // Disable lock first - otherwise the application might not update correctly |
115 | className={`sidebar__button sidebar__button--todos ${todosStore.isTodosPanelVisible ? 'is-active' : ''}`} | 122 | actions.settings.update({ |
116 | data-tip={`${intl.formatMessage(todosToggleMessage)} (${ctrlKey}+T)`} | 123 | type: 'app', |
117 | > | 124 | data: { |
118 | <i className="mdi mdi-check-all" /> | 125 | locked: false, |
119 | </button> | 126 | }, |
120 | ) : null} | 127 | }); |
121 | {workspaceStore.isFeatureEnabled ? ( | 128 | setTimeout(() => { |
122 | <button | 129 | actions.settings.update({ |
123 | type="button" | 130 | type: 'app', |
124 | onClick={() => { | 131 | data: { |
125 | toggleWorkspaceDrawer(); | 132 | locked: true, |
126 | this.updateToolTip(); | 133 | }, |
127 | gaEvent(GA_CATEGORY_WORKSPACES, 'toggleDrawer', 'sidebar'); | 134 | }); |
128 | }} | 135 | }, 0); |
129 | className={`sidebar__button sidebar__button--workspaces ${isWorkspaceDrawerOpen ? 'is-active' : ''}`} | 136 | }} |
130 | data-tip={`${intl.formatMessage(workspaceToggleMessage)} (${ctrlKey}+D)`} | 137 | data-tip={`${intl.formatMessage(messages.lockFerdi)} (${ctrlKey}+Shift+L)`} |
138 | > | ||
139 | <i className="mdi mdi-lock" /> | ||
140 | </button> | ||
141 | ) : null} | ||
142 | {todosStore.isFeatureEnabled && todosStore.isFeatureEnabledByUser ? ( | ||
143 | <button | ||
144 | type="button" | ||
145 | onClick={() => { | ||
146 | todoActions.toggleTodosPanel(); | ||
147 | this.updateToolTip(); | ||
148 | }} | ||
149 | className={`sidebar__button sidebar__button--todos ${todosStore.isTodosPanelVisible ? 'is-active' : ''}`} | ||
150 | data-tip={`${intl.formatMessage(todosToggleMessage)} (${ctrlKey}+T)`} | ||
151 | > | ||
152 | <i className="mdi mdi-check-all" /> | ||
153 | </button> | ||
154 | ) : null} | ||
155 | {workspaceStore.isFeatureEnabled ? ( | ||
156 | <button | ||
157 | type="button" | ||
158 | onClick={() => { | ||
159 | toggleWorkspaceDrawer(); | ||
160 | this.updateToolTip(); | ||
161 | }} | ||
162 | className={`sidebar__button sidebar__button--workspaces ${isWorkspaceDrawerOpen ? 'is-active' : ''}`} | ||
163 | data-tip={`${intl.formatMessage(workspaceToggleMessage)} (${ctrlKey}+D)`} | ||
164 | > | ||
165 | <i className="mdi mdi-view-grid" /> | ||
166 | </button> | ||
167 | ) : null} | ||
168 | <button | ||
169 | type="button" | ||
170 | onClick={() => { | ||
171 | toggleMuteApp(); | ||
172 | this.updateToolTip(); | ||
173 | }} | ||
174 | className={`sidebar__button sidebar__button--audio ${isAppMuted ? 'is-muted' : ''}`} | ||
175 | data-tip={`${intl.formatMessage(isAppMuted ? messages.unmute : messages.mute)} (${ctrlKey}+Shift+M)`} | ||
176 | > | ||
177 | <i className={`mdi mdi-bell${isAppMuted ? '-off' : ''}`} /> | ||
178 | </button> | ||
179 | <button | ||
180 | type="button" | ||
181 | onClick={() => openSettings({ path: 'recipes' })} | ||
182 | className="sidebar__button sidebar__button--new-service" | ||
183 | data-tip={`${intl.formatMessage(messages.addNewService)} (${ctrlKey}+N)`} | ||
184 | > | ||
185 | <i className="mdi mdi-plus-box" /> | ||
186 | </button> | ||
187 | </> | ||
188 | ) : ( | ||
189 | <Link | ||
190 | to="/auth/welcome" | ||
191 | className="sidebar__button sidebar__button--new-service" | ||
192 | data-tip="Login" | ||
131 | > | 193 | > |
132 | <i className="mdi mdi-view-grid" /> | 194 | <i className="mdi mdi-login-variant" /> |
133 | </button> | 195 | </Link> |
134 | ) : null} | 196 | )} |
135 | <button | ||
136 | type="button" | ||
137 | onClick={() => { | ||
138 | toggleMuteApp(); | ||
139 | this.updateToolTip(); | ||
140 | }} | ||
141 | className={`sidebar__button sidebar__button--audio ${isAppMuted ? 'is-muted' : ''}`} | ||
142 | data-tip={`${intl.formatMessage(isAppMuted ? messages.unmute : messages.mute)} (${ctrlKey}+Shift+M)`} | ||
143 | > | ||
144 | <i className={`mdi mdi-bell${isAppMuted ? '-off' : ''}`} /> | ||
145 | </button> | ||
146 | <button | ||
147 | type="button" | ||
148 | onClick={() => openSettings({ path: 'recipes' })} | ||
149 | className="sidebar__button sidebar__button--new-service" | ||
150 | data-tip={`${intl.formatMessage(messages.addNewService)} (${ctrlKey}+N)`} | ||
151 | > | ||
152 | <i className="mdi mdi-plus-box" /> | ||
153 | </button> | ||
154 | <button | 197 | <button |
155 | type="button" | 198 | type="button" |
156 | onClick={() => openSettings({ path: 'app' })} | 199 | onClick={() => openSettings({ path: 'app' })} |
@@ -158,6 +201,12 @@ export default @observer class Sidebar extends Component { | |||
158 | data-tip={`${intl.formatMessage(messages.settings)} (${ctrlKey}+,)`} | 201 | data-tip={`${intl.formatMessage(messages.settings)} (${ctrlKey}+,)`} |
159 | > | 202 | > |
160 | <i className="mdi mdi-settings" /> | 203 | <i className="mdi mdi-settings" /> |
204 | { (this.props.stores.app.updateStatus === this.props.stores.app.updateStatusTypes.AVAILABLE | ||
205 | || this.props.stores.app.updateStatus === this.props.stores.app.updateStatusTypes.DOWNLOADED) && ( | ||
206 | <span className="update-available"> | ||
207 | • | ||
208 | </span> | ||
209 | ) } | ||
161 | </button> | 210 | </button> |
162 | {this.state.tooltipEnabled && ( | 211 | {this.state.tooltipEnabled && ( |
163 | <ReactTooltip place="right" type="dark" effect="solid" /> | 212 | <ReactTooltip place="right" type="dark" effect="solid" /> |