diff options
Diffstat (limited to 'src/components/layout/Sidebar.jsx')
-rw-r--r-- | src/components/layout/Sidebar.jsx | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/src/components/layout/Sidebar.jsx b/src/components/layout/Sidebar.jsx new file mode 100644 index 000000000..6949544b4 --- /dev/null +++ b/src/components/layout/Sidebar.jsx | |||
@@ -0,0 +1,336 @@ | |||
1 | import { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import ReactTooltip from 'react-tooltip'; | ||
4 | import { defineMessages, injectIntl } from 'react-intl'; | ||
5 | import { inject, observer } from 'mobx-react'; | ||
6 | import { | ||
7 | mdiCheckAll, | ||
8 | mdiViewGrid, | ||
9 | mdiPlusBox, | ||
10 | mdiCog, | ||
11 | mdiBellOff, | ||
12 | mdiBell, | ||
13 | mdiLock, | ||
14 | mdiMenu, | ||
15 | mdiChevronDown, | ||
16 | mdiChevronRight, | ||
17 | mdiViewSplitVertical, | ||
18 | } from '@mdi/js'; | ||
19 | |||
20 | import Tabbar from '../services/tabs/Tabbar'; | ||
21 | import { | ||
22 | settingsShortcutKey, | ||
23 | lockFerdiumShortcutKey, | ||
24 | todosToggleShortcutKey, | ||
25 | workspaceToggleShortcutKey, | ||
26 | addNewServiceShortcutKey, | ||
27 | splitModeToggleShortcutKey, | ||
28 | muteFerdiumShortcutKey, | ||
29 | } from '../../environment'; | ||
30 | import { todosStore } from '../../features/todos'; | ||
31 | import { todoActions } from '../../features/todos/actions'; | ||
32 | import AppStore from '../../stores/AppStore'; | ||
33 | import SettingsStore from '../../stores/SettingsStore'; | ||
34 | import globalMessages from '../../i18n/globalMessages'; | ||
35 | import { Icon } from '../ui/icon'; | ||
36 | |||
37 | const messages = defineMessages({ | ||
38 | addNewService: { | ||
39 | id: 'sidebar.addNewService', | ||
40 | defaultMessage: 'Add new service', | ||
41 | }, | ||
42 | splitModeToggle: { | ||
43 | id: 'sidebar.splitModeToggle', | ||
44 | defaultMessage: 'Split Mode Toggle', | ||
45 | }, | ||
46 | mute: { | ||
47 | id: 'sidebar.muteApp', | ||
48 | defaultMessage: 'Disable notifications & audio', | ||
49 | }, | ||
50 | unmute: { | ||
51 | id: 'sidebar.unmuteApp', | ||
52 | defaultMessage: 'Enable notifications & audio', | ||
53 | }, | ||
54 | openWorkspaceDrawer: { | ||
55 | id: 'sidebar.openWorkspaceDrawer', | ||
56 | defaultMessage: 'Open workspace drawer', | ||
57 | }, | ||
58 | closeWorkspaceDrawer: { | ||
59 | id: 'sidebar.closeWorkspaceDrawer', | ||
60 | defaultMessage: 'Close workspace drawer', | ||
61 | }, | ||
62 | openTodosDrawer: { | ||
63 | id: 'sidebar.openTodosDrawer', | ||
64 | defaultMessage: 'Open Ferdium Todos', | ||
65 | }, | ||
66 | closeTodosDrawer: { | ||
67 | id: 'sidebar.closeTodosDrawer', | ||
68 | defaultMessage: 'Close Ferdium Todos', | ||
69 | }, | ||
70 | lockFerdium: { | ||
71 | id: 'sidebar.lockFerdium', | ||
72 | defaultMessage: 'Lock Ferdium', | ||
73 | }, | ||
74 | }); | ||
75 | |||
76 | class Sidebar extends Component { | ||
77 | static propTypes = { | ||
78 | openSettings: PropTypes.func.isRequired, | ||
79 | closeSettings: PropTypes.func.isRequired, | ||
80 | setActive: PropTypes.func.isRequired, | ||
81 | reorder: PropTypes.func.isRequired, | ||
82 | reload: PropTypes.func.isRequired, | ||
83 | toggleNotifications: PropTypes.func.isRequired, | ||
84 | toggleAudio: PropTypes.func.isRequired, | ||
85 | toggleDarkMode: PropTypes.func.isRequired, | ||
86 | showServicesUpdatedInfoBar: PropTypes.bool.isRequired, | ||
87 | showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired, | ||
88 | showServiceNameSetting: PropTypes.bool.isRequired, | ||
89 | showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired, | ||
90 | deleteService: PropTypes.func.isRequired, | ||
91 | updateService: PropTypes.func.isRequired, | ||
92 | hibernateService: PropTypes.func.isRequired, | ||
93 | wakeUpService: PropTypes.func.isRequired, | ||
94 | toggleMuteApp: PropTypes.func.isRequired, | ||
95 | isAppMuted: PropTypes.bool.isRequired, | ||
96 | toggleCollapseMenu: PropTypes.func.isRequired, | ||
97 | isMenuCollapsed: PropTypes.bool.isRequired, | ||
98 | isWorkspaceDrawerOpen: PropTypes.bool.isRequired, | ||
99 | toggleWorkspaceDrawer: PropTypes.func.isRequired, | ||
100 | isTodosServiceActive: PropTypes.bool.isRequired, | ||
101 | stores: PropTypes.shape({ | ||
102 | app: PropTypes.instanceOf(AppStore).isRequired, | ||
103 | settings: PropTypes.instanceOf(SettingsStore).isRequired, | ||
104 | }).isRequired, | ||
105 | actions: PropTypes.shape({ | ||
106 | settings: PropTypes.instanceOf(SettingsStore).isRequired, | ||
107 | }).isRequired, | ||
108 | }; | ||
109 | |||
110 | constructor(props) { | ||
111 | super(props); | ||
112 | |||
113 | this.state = { | ||
114 | tooltipEnabled: true, | ||
115 | }; | ||
116 | } | ||
117 | |||
118 | componentDidUpdate() { | ||
119 | ReactTooltip.rebuild(); | ||
120 | } | ||
121 | |||
122 | enableToolTip() { | ||
123 | this.setState({ tooltipEnabled: true }); | ||
124 | } | ||
125 | |||
126 | disableToolTip() { | ||
127 | this.setState({ tooltipEnabled: false }); | ||
128 | } | ||
129 | |||
130 | updateToolTip() { | ||
131 | this.disableToolTip(); | ||
132 | setTimeout(this.enableToolTip.bind(this)); | ||
133 | } | ||
134 | |||
135 | render() { | ||
136 | const { | ||
137 | openSettings, | ||
138 | toggleMuteApp, | ||
139 | toggleCollapseMenu, | ||
140 | isAppMuted, | ||
141 | isWorkspaceDrawerOpen, | ||
142 | toggleWorkspaceDrawer, | ||
143 | stores, | ||
144 | actions, | ||
145 | isTodosServiceActive, | ||
146 | } = this.props; | ||
147 | const { | ||
148 | hideCollapseButton, | ||
149 | hideRecipesButton, | ||
150 | hideWorkspacesButton, | ||
151 | hideNotificationsButton, | ||
152 | hideSettingsButton, | ||
153 | hideSplitModeButton, | ||
154 | useVerticalStyle, | ||
155 | splitMode, | ||
156 | } = stores.settings.app; | ||
157 | const { intl } = this.props; | ||
158 | const todosToggleMessage = todosStore.isTodosPanelVisible | ||
159 | ? messages.closeTodosDrawer | ||
160 | : messages.openTodosDrawer; | ||
161 | |||
162 | const workspaceToggleMessage = isWorkspaceDrawerOpen | ||
163 | ? messages.closeWorkspaceDrawer | ||
164 | : messages.openWorkspaceDrawer; | ||
165 | |||
166 | const numberActiveButtons = [ | ||
167 | !hideRecipesButton, | ||
168 | !hideWorkspacesButton, | ||
169 | !hideNotificationsButton, | ||
170 | !hideSettingsButton, | ||
171 | !hideSplitModeButton, | ||
172 | todosStore.isFeatureEnabledByUser, | ||
173 | ].filter(Boolean).length; | ||
174 | |||
175 | const { isMenuCollapsed } = stores.settings.all.app; | ||
176 | |||
177 | return ( | ||
178 | <div className="sidebar"> | ||
179 | <Tabbar | ||
180 | {...this.props} | ||
181 | enableToolTip={() => this.enableToolTip()} | ||
182 | disableToolTip={() => this.disableToolTip()} | ||
183 | useVerticalStyle={stores.settings.all.app.useVerticalStyle} | ||
184 | /> | ||
185 | <> | ||
186 | {numberActiveButtons <= 1 || hideCollapseButton ? null : ( | ||
187 | <button | ||
188 | type="button" | ||
189 | onClick={() => toggleCollapseMenu()} | ||
190 | className="sidebar__button sidebar__button--hamburger-menu" | ||
191 | > | ||
192 | {isMenuCollapsed && !useVerticalStyle ? ( | ||
193 | <Icon icon={mdiMenu} size={1.5} /> | ||
194 | ) : null} | ||
195 | |||
196 | {isMenuCollapsed && useVerticalStyle ? ( | ||
197 | <Icon icon={mdiChevronRight} size={1.5} /> | ||
198 | ) : null} | ||
199 | |||
200 | {!isMenuCollapsed ? ( | ||
201 | <Icon icon={mdiChevronDown} size={1.5} /> | ||
202 | ) : null} | ||
203 | </button> | ||
204 | )} | ||
205 | {!hideRecipesButton && !isMenuCollapsed ? ( | ||
206 | <button | ||
207 | type="button" | ||
208 | onClick={() => openSettings({ path: 'recipes' })} | ||
209 | className="sidebar__button sidebar__button--new-service" | ||
210 | data-tip={`${intl.formatMessage( | ||
211 | messages.addNewService, | ||
212 | )} (${addNewServiceShortcutKey(false)})`} | ||
213 | > | ||
214 | <Icon icon={mdiPlusBox} size={1.5} /> | ||
215 | </button> | ||
216 | ) : null} | ||
217 | {!hideSplitModeButton && !isMenuCollapsed ? ( | ||
218 | <button | ||
219 | type="button" | ||
220 | onClick={() => { | ||
221 | actions.settings.update({ | ||
222 | type: 'app', | ||
223 | data: { | ||
224 | splitMode: !splitMode, | ||
225 | }, | ||
226 | }); | ||
227 | }} | ||
228 | className="sidebar__button sidebar__button--split-mode-toggle" | ||
229 | data-tip={`${intl.formatMessage( | ||
230 | messages.splitModeToggle, | ||
231 | )} (${splitModeToggleShortcutKey(false)})`} | ||
232 | > | ||
233 | <Icon icon={mdiViewSplitVertical} size={1.5} /> | ||
234 | </button> | ||
235 | ) : null} | ||
236 | {!hideWorkspacesButton && !isMenuCollapsed ? ( | ||
237 | <button | ||
238 | type="button" | ||
239 | onClick={() => { | ||
240 | toggleWorkspaceDrawer(); | ||
241 | this.updateToolTip(); | ||
242 | }} | ||
243 | className={`sidebar__button sidebar__button--workspaces ${ | ||
244 | isWorkspaceDrawerOpen ? 'is-active' : '' | ||
245 | }`} | ||
246 | data-tip={`${intl.formatMessage( | ||
247 | workspaceToggleMessage, | ||
248 | )} (${workspaceToggleShortcutKey(false)})`} | ||
249 | > | ||
250 | <Icon icon={mdiViewGrid} size={1.5} /> | ||
251 | </button> | ||
252 | ) : null} | ||
253 | {!hideNotificationsButton && !isMenuCollapsed ? ( | ||
254 | <button | ||
255 | type="button" | ||
256 | onClick={() => { | ||
257 | toggleMuteApp(); | ||
258 | this.updateToolTip(); | ||
259 | }} | ||
260 | className={`sidebar__button sidebar__button--audio ${ | ||
261 | isAppMuted ? 'is-muted' : '' | ||
262 | }`} | ||
263 | data-tip={`${intl.formatMessage( | ||
264 | isAppMuted ? messages.unmute : messages.mute, | ||
265 | )} (${muteFerdiumShortcutKey(false)})`} | ||
266 | > | ||
267 | <Icon icon={isAppMuted ? mdiBellOff : mdiBell} size={1.5} /> | ||
268 | </button> | ||
269 | ) : null} | ||
270 | {todosStore.isFeatureEnabledByUser && !isMenuCollapsed ? ( | ||
271 | <button | ||
272 | type="button" | ||
273 | onClick={() => { | ||
274 | todoActions.toggleTodosPanel(); | ||
275 | this.updateToolTip(); | ||
276 | }} | ||
277 | disabled={isTodosServiceActive} | ||
278 | className={`sidebar__button sidebar__button--todos ${ | ||
279 | todosStore.isTodosPanelVisible ? 'is-active' : '' | ||
280 | }`} | ||
281 | data-tip={`${intl.formatMessage( | ||
282 | todosToggleMessage, | ||
283 | )} (${todosToggleShortcutKey(false)})`} | ||
284 | > | ||
285 | <Icon icon={mdiCheckAll} size={1.5} /> | ||
286 | </button> | ||
287 | ) : null} | ||
288 | {stores.settings.all.app.lockingFeatureEnabled ? ( | ||
289 | <button | ||
290 | type="button" | ||
291 | className="sidebar__button" | ||
292 | onClick={() => { | ||
293 | actions.settings.update({ | ||
294 | type: 'app', | ||
295 | data: { | ||
296 | locked: true, | ||
297 | }, | ||
298 | }); | ||
299 | }} | ||
300 | data-tip={`${intl.formatMessage( | ||
301 | messages.lockFerdium, | ||
302 | )} (${lockFerdiumShortcutKey(false)})`} | ||
303 | > | ||
304 | <Icon icon={mdiLock} size={1.5} /> | ||
305 | </button> | ||
306 | ) : null} | ||
307 | </> | ||
308 | {this.state.tooltipEnabled && ( | ||
309 | <ReactTooltip place="right" type="dark" effect="solid" /> | ||
310 | )} | ||
311 | {!hideSettingsButton && !isMenuCollapsed ? ( | ||
312 | <button | ||
313 | type="button" | ||
314 | onClick={() => openSettings({ path: 'app' })} | ||
315 | className="sidebar__button sidebar__button--settings" | ||
316 | data-tip={`${intl.formatMessage( | ||
317 | globalMessages.settings, | ||
318 | )} (${settingsShortcutKey(false)})`} | ||
319 | > | ||
320 | <Icon icon={mdiCog} size={1.5} /> | ||
321 | {this.props.stores.settings.app.automaticUpdates && | ||
322 | (this.props.stores.app.updateStatus === | ||
323 | this.props.stores.app.updateStatusTypes.AVAILABLE || | ||
324 | this.props.stores.app.updateStatus === | ||
325 | this.props.stores.app.updateStatusTypes.DOWNLOADED || | ||
326 | this.props.showServicesUpdatedInfoBar) && ( | ||
327 | <span className="update-available">•</span> | ||
328 | )} | ||
329 | </button> | ||
330 | ) : null} | ||
331 | </div> | ||
332 | ); | ||
333 | } | ||
334 | } | ||
335 | |||
336 | export default injectIntl(inject('stores', 'actions')(observer(Sidebar))); | ||