aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/layout
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/layout')
-rw-r--r--src/components/layout/AppLayout.js94
-rw-r--r--src/components/layout/Sidebar.js35
2 files changed, 73 insertions, 56 deletions
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js
index a60270a6f..d5e1deb39 100644
--- a/src/components/layout/AppLayout.js
+++ b/src/components/layout/AppLayout.js
@@ -19,10 +19,7 @@ import { isWindows } from '../../environment';
19import WorkspaceSwitchingIndicator from '../../features/workspaces/components/WorkspaceSwitchingIndicator'; 19import WorkspaceSwitchingIndicator from '../../features/workspaces/components/WorkspaceSwitchingIndicator';
20import { workspaceStore } from '../../features/workspaces'; 20import { workspaceStore } from '../../features/workspaces';
21import AppUpdateInfoBar from '../AppUpdateInfoBar'; 21import AppUpdateInfoBar from '../AppUpdateInfoBar';
22import TrialActivationInfoBar from '../TrialActivationInfoBar';
23import Todos from '../../features/todos/containers/TodosScreen'; 22import Todos from '../../features/todos/containers/TodosScreen';
24import PlanSelection from '../../features/planSelection/containers/PlanSelectionScreen';
25import TrialStatusBar from '../../features/trialStatusBar/containers/TrialStatusBarScreen';
26 23
27function createMarkup(HTMLString) { 24function createMarkup(HTMLString) {
28 return { __html: HTMLString }; 25 return { __html: HTMLString };
@@ -43,22 +40,32 @@ const messages = defineMessages({
43 }, 40 },
44 authRequestFailed: { 41 authRequestFailed: {
45 id: 'infobar.authRequestFailed', 42 id: 'infobar.authRequestFailed',
46 defaultMessage: '!!!There were errors while trying to perform an authenticated request. Please try logging out and back in if this error persists.', 43 defaultMessage:
44 '!!!There were errors while trying to perform an authenticated request. Please try logging out and back in if this error persists.',
47 }, 45 },
48}); 46});
49 47
48let transition = 'none';
49
50if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) {
51 transition = 'transform 0.5s ease';
52}
53
50const styles = theme => ({ 54const styles = theme => ({
51 appContent: { 55 appContent: {
52 // width: `calc(100% + ${theme.workspaces.drawer.width}px)`, 56 // width: `calc(100% + ${theme.workspaces.drawer.width}px)`,
53 width: '100%', 57 width: '100%',
54 transition: 'transform 0.5s ease', 58 transition,
55 transform() { 59 transform() {
56 return workspaceStore.isWorkspaceDrawerOpen ? 'translateX(0)' : `translateX(-${theme.workspaces.drawer.width}px)`; 60 return workspaceStore.isWorkspaceDrawerOpen
61 ? 'translateX(0)'
62 : `translateX(-${theme.workspaces.drawer.width}px)`;
57 }, 63 },
58 }, 64 },
59}); 65});
60 66
61@injectSheet(styles) @observer 67@injectSheet(styles)
68@observer
62class AppLayout extends Component { 69class AppLayout extends Component {
63 static propTypes = { 70 static propTypes = {
64 classes: PropTypes.object.isRequired, 71 classes: PropTypes.object.isRequired,
@@ -79,12 +86,11 @@ class AppLayout extends Component {
79 areRequiredRequestsSuccessful: PropTypes.bool.isRequired, 86 areRequiredRequestsSuccessful: PropTypes.bool.isRequired,
80 retryRequiredRequests: PropTypes.func.isRequired, 87 retryRequiredRequests: PropTypes.func.isRequired,
81 areRequiredRequestsLoading: PropTypes.bool.isRequired, 88 areRequiredRequestsLoading: PropTypes.bool.isRequired,
82 hasActivatedTrial: PropTypes.bool.isRequired,
83 }; 89 };
84 90
85 state = { 91 state = {
86 shouldShowAppUpdateInfoBar: true, 92 shouldShowAppUpdateInfoBar: true,
87 } 93 };
88 94
89 static defaultProps = { 95 static defaultProps = {
90 children: [], 96 children: [],
@@ -115,7 +121,6 @@ class AppLayout extends Component {
115 areRequiredRequestsSuccessful, 121 areRequiredRequestsSuccessful,
116 retryRequiredRequests, 122 retryRequiredRequests,
117 areRequiredRequestsLoading, 123 areRequiredRequestsLoading,
118 hasActivatedTrial,
119 } = this.props; 124 } = this.props;
120 125
121 const { intl } = this.context; 126 const { intl } = this.context;
@@ -123,45 +128,48 @@ class AppLayout extends Component {
123 return ( 128 return (
124 <ErrorBoundary> 129 <ErrorBoundary>
125 <div className="app"> 130 <div className="app">
126 {isWindows && !isFullScreen && <TitleBar menu={window.ferdi.menu.template} icon="assets/images/logo.svg" />} 131 {isWindows && !isFullScreen && (
132 <TitleBar
133 menu={window.ferdi.menu.template}
134 icon="assets/images/logo.svg"
135 />
136 )}
127 <div className={`app__content ${classes.appContent}`}> 137 <div className={`app__content ${classes.appContent}`}>
128 {workspacesDrawer} 138 {workspacesDrawer}
129 {sidebar} 139 {sidebar}
130 <div className="app__service"> 140 <div className="app__service">
131 <WorkspaceSwitchingIndicator /> 141 <WorkspaceSwitchingIndicator />
132 {news.length > 0 && news.map(item => ( 142 {news.length > 0 &&
143 news.map(item => (
144 <InfoBar
145 key={item.id}
146 position="top"
147 type={item.type}
148 sticky={item.sticky}
149 onHide={() => removeNewsItem({ newsId: item.id })}
150 >
151 <span
152 dangerouslySetInnerHTML={createMarkup(item.message)}
153 onClick={event => {
154 const { target } = event;
155 if (target && target.hasAttribute('data-is-news-cta')) {
156 removeNewsItem({ newsId: item.id });
157 }
158 }}
159 />
160 </InfoBar>
161 ))}
162 {!areRequiredRequestsSuccessful && showRequiredRequestsError && (
133 <InfoBar 163 <InfoBar
134 key={item.id} 164 type="danger"
135 position="top" 165 ctaLabel="Try again"
136 type={item.type} 166 ctaLoading={areRequiredRequestsLoading}
137 sticky={item.sticky} 167 sticky
138 onHide={() => removeNewsItem({ newsId: item.id })} 168 onClick={retryRequiredRequests}
139 > 169 >
140 <span 170 <span className="mdi mdi-flash" />
141 dangerouslySetInnerHTML={createMarkup(item.message)} 171 {intl.formatMessage(messages.requiredRequestsFailed)}
142 onClick={(event) => {
143 const { target } = event;
144 if (target && target.hasAttribute('data-is-news-cta')) {
145 removeNewsItem({ newsId: item.id });
146 }
147 }}
148 />
149 </InfoBar> 172 </InfoBar>
150 ))}
151 {hasActivatedTrial && (
152 <TrialActivationInfoBar />
153 )}
154 {!areRequiredRequestsSuccessful && showRequiredRequestsError && (
155 <InfoBar
156 type="danger"
157 ctaLabel="Try again"
158 ctaLoading={areRequiredRequestsLoading}
159 sticky
160 onClick={retryRequiredRequests}
161 >
162 <span className="mdi mdi-flash" />
163 {intl.formatMessage(messages.requiredRequestsFailed)}
164 </InfoBar>
165 )} 173 )}
166 {authRequestFailed && ( 174 {authRequestFailed && (
167 <InfoBar 175 <InfoBar
@@ -186,7 +194,7 @@ class AppLayout extends Component {
186 {intl.formatMessage(messages.servicesUpdated)} 194 {intl.formatMessage(messages.servicesUpdated)}
187 </InfoBar> 195 </InfoBar>
188 )} 196 )}
189 { appUpdateIsDownloaded && this.state.shouldShowAppUpdateInfoBar && ( 197 {appUpdateIsDownloaded && this.state.shouldShowAppUpdateInfoBar && (
190 <AppUpdateInfoBar 198 <AppUpdateInfoBar
191 nextAppReleaseVersion={nextAppReleaseVersion} 199 nextAppReleaseVersion={nextAppReleaseVersion}
192 onInstallUpdate={installAppUpdate} 200 onInstallUpdate={installAppUpdate}
@@ -202,11 +210,9 @@ class AppLayout extends Component {
202 <PublishDebugInfo /> 210 <PublishDebugInfo />
203 {services} 211 {services}
204 {children} 212 {children}
205 <TrialStatusBar />
206 </div> 213 </div>
207 <Todos /> 214 <Todos />
208 </div> 215 </div>
209 <PlanSelection />
210 </div> 216 </div>
211 </ErrorBoundary> 217 </ErrorBoundary>
212 ); 218 );
diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js
index 802538eba..daa5642c3 100644
--- a/src/components/layout/Sidebar.js
+++ b/src/components/layout/Sidebar.js
@@ -6,16 +6,13 @@ import { inject, observer } from 'mobx-react';
6import { Link } from 'react-router'; 6import { Link } from 'react-router';
7 7
8import Tabbar from '../services/tabs/Tabbar'; 8import Tabbar from '../services/tabs/Tabbar';
9import { ctrlKey, isMac } from '../../environment'; 9import { settingsShortcutKey, lockFerdiShortcutKey, todosToggleShortcutKey, workspaceToggleShortcutKey, addNewServiceShortcutKey, muteFerdiShortcutKey } from '../../environment';
10import { workspaceStore } from '../../features/workspaces'; 10import { workspaceStore } from '../../features/workspaces';
11import { todosStore } from '../../features/todos'; 11import { todosStore } from '../../features/todos';
12import { todoActions } from '../../features/todos/actions'; 12import { todoActions } from '../../features/todos/actions';
13import AppStore from '../../stores/AppStore'; 13import AppStore from '../../stores/AppStore';
14import SettingsStore from '../../stores/SettingsStore'; 14import SettingsStore from '../../stores/SettingsStore';
15 15
16// Platform specific shortcut keys
17const settingsShortcutKey = isMac ? ',' : 'P';
18
19const messages = defineMessages({ 16const messages = defineMessages({
20 settings: { 17 settings: {
21 id: 'sidebar.settings', 18 id: 'sidebar.settings',
@@ -58,6 +55,19 @@ const messages = defineMessages({
58export default @inject('stores', 'actions') @observer class Sidebar extends Component { 55export default @inject('stores', 'actions') @observer class Sidebar extends Component {
59 static propTypes = { 56 static propTypes = {
60 openSettings: PropTypes.func.isRequired, 57 openSettings: PropTypes.func.isRequired,
58 closeSettings: PropTypes.func.isRequired,
59 setActive: PropTypes.func.isRequired,
60 reorder: PropTypes.func.isRequired,
61 reload: PropTypes.func.isRequired,
62 toggleNotifications: PropTypes.func.isRequired,
63 toggleAudio: PropTypes.func.isRequired,
64 toggleDarkMode: PropTypes.func.isRequired,
65 showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired,
66 showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired,
67 deleteService: PropTypes.func.isRequired,
68 updateService: PropTypes.func.isRequired,
69 hibernateService: PropTypes.func.isRequired,
70 wakeUpService: PropTypes.func.isRequired,
61 toggleMuteApp: PropTypes.func.isRequired, 71 toggleMuteApp: PropTypes.func.isRequired,
62 isAppMuted: PropTypes.bool.isRequired, 72 isAppMuted: PropTypes.bool.isRequired,
63 isWorkspaceDrawerOpen: PropTypes.bool.isRequired, 73 isWorkspaceDrawerOpen: PropTypes.bool.isRequired,
@@ -139,7 +149,7 @@ export default @inject('stores', 'actions') @observer class Sidebar extends Comp
139 }, 149 },
140 }); 150 });
141 }} 151 }}
142 data-tip={`${intl.formatMessage(messages.lockFerdi)} (${ctrlKey}+Shift+L)`} 152 data-tip={`${intl.formatMessage(messages.lockFerdi)} (${lockFerdiShortcutKey(false)})`}
143 > 153 >
144 <i className="mdi mdi-lock" /> 154 <i className="mdi mdi-lock" />
145 </button> 155 </button>
@@ -152,8 +162,8 @@ export default @inject('stores', 'actions') @observer class Sidebar extends Comp
152 this.updateToolTip(); 162 this.updateToolTip();
153 }} 163 }}
154 disabled={isTodosServiceActive} 164 disabled={isTodosServiceActive}
155 className={`sidebar__button sidebar__button--todos ${todosStore.isTodosPanelVisible ? 'is-active' : ''}`} 165 className={`sidebar__button sidebar__button--todos ${todosStore.isTodosPanelVisible ? 'is-active' : ''}`}
156 data-tip={`${intl.formatMessage(todosToggleMessage)} (${ctrlKey}+T)`} 166 data-tip={`${intl.formatMessage(todosToggleMessage)} (${todosToggleShortcutKey(false)})`}
157 > 167 >
158 <i className="mdi mdi-check-all" /> 168 <i className="mdi mdi-check-all" />
159 </button> 169 </button>
@@ -166,7 +176,7 @@ export default @inject('stores', 'actions') @observer class Sidebar extends Comp
166 this.updateToolTip(); 176 this.updateToolTip();
167 }} 177 }}
168 className={`sidebar__button sidebar__button--workspaces ${isWorkspaceDrawerOpen ? 'is-active' : ''}`} 178 className={`sidebar__button sidebar__button--workspaces ${isWorkspaceDrawerOpen ? 'is-active' : ''}`}
169 data-tip={`${intl.formatMessage(workspaceToggleMessage)} (${ctrlKey}+D)`} 179 data-tip={`${intl.formatMessage(workspaceToggleMessage)} (${workspaceToggleShortcutKey(false)})`}
170 > 180 >
171 <i className="mdi mdi-view-grid" /> 181 <i className="mdi mdi-view-grid" />
172 </button> 182 </button>
@@ -178,7 +188,7 @@ export default @inject('stores', 'actions') @observer class Sidebar extends Comp
178 this.updateToolTip(); 188 this.updateToolTip();
179 }} 189 }}
180 className={`sidebar__button sidebar__button--audio ${isAppMuted ? 'is-muted' : ''}`} 190 className={`sidebar__button sidebar__button--audio ${isAppMuted ? 'is-muted' : ''}`}
181 data-tip={`${intl.formatMessage(isAppMuted ? messages.unmute : messages.mute)} (${ctrlKey}+Shift+M)`} 191 data-tip={`${intl.formatMessage(isAppMuted ? messages.unmute : messages.mute)} (${muteFerdiShortcutKey(false)})`}
182 > 192 >
183 <i className={`mdi mdi-bell${isAppMuted ? '-off' : ''}`} /> 193 <i className={`mdi mdi-bell${isAppMuted ? '-off' : ''}`} />
184 </button> 194 </button>
@@ -186,7 +196,7 @@ export default @inject('stores', 'actions') @observer class Sidebar extends Comp
186 type="button" 196 type="button"
187 onClick={() => openSettings({ path: 'recipes' })} 197 onClick={() => openSettings({ path: 'recipes' })}
188 className="sidebar__button sidebar__button--new-service" 198 className="sidebar__button sidebar__button--new-service"
189 data-tip={`${intl.formatMessage(messages.addNewService)} (${ctrlKey}+N)`} 199 data-tip={`${intl.formatMessage(messages.addNewService)} (${addNewServiceShortcutKey(false)})`}
190 > 200 >
191 <i className="mdi mdi-plus-box" /> 201 <i className="mdi mdi-plus-box" />
192 </button> 202 </button>
@@ -204,9 +214,10 @@ export default @inject('stores', 'actions') @observer class Sidebar extends Comp
204 type="button" 214 type="button"
205 onClick={() => openSettings({ path: 'app' })} 215 onClick={() => openSettings({ path: 'app' })}
206 className="sidebar__button sidebar__button--settings" 216 className="sidebar__button sidebar__button--settings"
207 data-tip={`${intl.formatMessage(messages.settings)} (${ctrlKey}+${settingsShortcutKey})`} 217 data-tip={`${intl.formatMessage(messages.settings)} (${settingsShortcutKey(false)})`}
208 > 218 >
209 <i className="mdi mdi-settings" /> 219 {/* TODO: Because of https://github.com/Templarian/MaterialDesign-Webfont/issues/81 bug in @mdi/font in 5.9.55, added `mdi-memory` as a fallback */}
220 <i className="mdi mdi-settings mdi-memory" />
210 { (this.props.stores.app.updateStatus === this.props.stores.app.updateStatusTypes.AVAILABLE 221 { (this.props.stores.app.updateStatus === this.props.stores.app.updateStatusTypes.AVAILABLE
211 || this.props.stores.app.updateStatus === this.props.stores.app.updateStatusTypes.DOWNLOADED) && ( 222 || this.props.stores.app.updateStatus === this.props.stores.app.updateStatusTypes.DOWNLOADED) && (
212 <span className="update-available"> 223 <span className="update-available">