aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/services/tabs/TabItem.js
diff options
context:
space:
mode:
authorLibravatar Balaji Vijayakumar <kuttibalaji.v6@gmail.com>2022-10-26 09:47:11 +0530
committerLibravatar Vijay Aravamudhan <vraravam@users.noreply.github.com>2022-10-26 10:15:16 +0530
commit354ffea92be3a1030c730b6bb8ece7f680e01b12 (patch)
tree6425e972752c9b250e861b16da1a3120bc6db13c /src/components/services/tabs/TabItem.js
parent6.2.1-nightly.29 [skip ci] (diff)
downloadferdium-app-354ffea92be3a1030c730b6bb8ece7f680e01b12.tar.gz
ferdium-app-354ffea92be3a1030c730b6bb8ece7f680e01b12.tar.zst
ferdium-app-354ffea92be3a1030c730b6bb8ece7f680e01b12.zip
refactor: convert TabBar to typescript
Diffstat (limited to 'src/components/services/tabs/TabItem.js')
-rw-r--r--src/components/services/tabs/TabItem.js419
1 files changed, 0 insertions, 419 deletions
diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js
deleted file mode 100644
index 0aab82a6f..000000000
--- a/src/components/services/tabs/TabItem.js
+++ /dev/null
@@ -1,419 +0,0 @@
1import { Menu, dialog, app } from '@electron/remote';
2import { Component } from 'react';
3import { defineMessages, injectIntl } from 'react-intl';
4import PropTypes from 'prop-types';
5import { inject, observer } from 'mobx-react';
6import classnames from 'classnames';
7import { SortableElement } from 'react-sortable-hoc';
8import injectSheet from 'react-jss';
9import ms from 'ms';
10
11import { observable, autorun, reaction, makeObservable } from 'mobx';
12import { mdiExclamation, mdiVolumeSource } from '@mdi/js';
13import ServiceModel from '../../../models/Service';
14import { cmdOrCtrlShortcutKey, shiftKey, altKey } from '../../../environment';
15import globalMessages from '../../../i18n/globalMessages';
16import SettingsStore from '../../../stores/SettingsStore';
17import Icon from '../../ui/icon';
18
19const IS_SERVICE_DEBUGGING_ENABLED = (
20 localStorage.getItem('debug') || ''
21).includes('Ferdium:Service');
22
23const messages = defineMessages({
24 reload: {
25 id: 'tabs.item.reload',
26 defaultMessage: 'Reload',
27 },
28 disableNotifications: {
29 id: 'tabs.item.disableNotifications',
30 defaultMessage: 'Disable notifications',
31 },
32 enableNotifications: {
33 id: 'tabs.item.enableNotification',
34 defaultMessage: 'Enable notifications',
35 },
36 disableAudio: {
37 id: 'tabs.item.disableAudio',
38 defaultMessage: 'Disable audio',
39 },
40 enableAudio: {
41 id: 'tabs.item.enableAudio',
42 defaultMessage: 'Enable audio',
43 },
44 enableDarkMode: {
45 id: 'tabs.item.enableDarkMode',
46 defaultMessage: 'Enable Dark mode',
47 },
48 disableDarkMode: {
49 id: 'tabs.item.disableDarkMode',
50 defaultMessage: 'Disable Dark mode',
51 },
52 disableService: {
53 id: 'tabs.item.disableService',
54 defaultMessage: 'Disable service',
55 },
56 enableService: {
57 id: 'tabs.item.enableService',
58 defaultMessage: 'Enable service',
59 },
60 hibernateService: {
61 id: 'tabs.item.hibernateService',
62 defaultMessage: 'Hibernate service',
63 },
64 wakeUpService: {
65 id: 'tabs.item.wakeUpService',
66 defaultMessage: 'Wake up service',
67 },
68 deleteService: {
69 id: 'tabs.item.deleteService',
70 defaultMessage: 'Delete service',
71 },
72 confirmDeleteService: {
73 id: 'tabs.item.confirmDeleteService',
74 defaultMessage: 'Do you really want to delete the {serviceName} service?',
75 },
76});
77
78let pollIndicatorTransition = 'none';
79let polledTransition = 'none';
80let pollAnsweredTransition = 'none';
81
82if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) {
83 pollIndicatorTransition = 'background 0.5s';
84 polledTransition = 'background 0.1s';
85 pollAnsweredTransition = 'background 0.1s';
86}
87
88const styles = {
89 pollIndicator: {
90 position: 'absolute',
91 bottom: 2,
92 width: 10,
93 height: 10,
94 borderRadius: 5,
95 background: 'gray',
96 transition: pollIndicatorTransition,
97 },
98 pollIndicatorPoll: {
99 left: 2,
100 },
101 pollIndicatorAnswer: {
102 left: 14,
103 },
104 polled: {
105 background: 'yellow !important',
106 transition: polledTransition,
107 },
108 pollAnswered: {
109 background: 'green !important',
110 transition: pollAnsweredTransition,
111 },
112 stale: {
113 background: 'red !important',
114 },
115};
116
117class TabItem extends Component {
118 static propTypes = {
119 classes: PropTypes.object.isRequired,
120 service: PropTypes.instanceOf(ServiceModel).isRequired,
121 clickHandler: PropTypes.func.isRequired,
122 shortcutIndex: PropTypes.number.isRequired,
123 reload: PropTypes.func.isRequired,
124 toggleNotifications: PropTypes.func.isRequired,
125 toggleAudio: PropTypes.func.isRequired,
126 toggleDarkMode: PropTypes.func.isRequired,
127 openSettings: PropTypes.func.isRequired,
128 deleteService: PropTypes.func.isRequired,
129 disableService: PropTypes.func.isRequired,
130 enableService: PropTypes.func.isRequired,
131 hibernateService: PropTypes.func.isRequired,
132 wakeUpService: PropTypes.func.isRequired,
133 showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired,
134 showServiceNameSetting: PropTypes.bool.isRequired,
135 showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired,
136 stores: PropTypes.shape({
137 settings: PropTypes.instanceOf(SettingsStore).isRequired,
138 }).isRequired,
139 };
140
141 @observable isPolled = false;
142
143 @observable isPollAnswered = false;
144
145 constructor(props) {
146 super(props);
147
148 makeObservable(this);
149
150 this.state = {
151 showShortcutIndex: false,
152 };
153
154 reaction(
155 () => this.props.stores.settings.app.enableLongPressServiceHint,
156 () => {
157 this.checkForLongPress(
158 this.props.stores.settings.app.enableLongPressServiceHint,
159 );
160 },
161 );
162 }
163
164 handleShortcutIndex = (event, showShortcutIndex = true) => {
165 if (event.key === 'Shift') {
166 this.setState({ showShortcutIndex });
167 }
168 };
169
170 checkForLongPress = enableLongPressServiceHint => {
171 if (enableLongPressServiceHint) {
172 document.addEventListener('keydown', e => {
173 this.handleShortcutIndex(e);
174 });
175
176 document.addEventListener('keyup', e => {
177 this.handleShortcutIndex(e, false);
178 });
179 }
180 };
181
182 componentDidMount() {
183 const { service, stores } = this.props;
184
185 if (IS_SERVICE_DEBUGGING_ENABLED) {
186 autorun(() => {
187 if (Date.now() - service.lastPoll < ms('0.2s')) {
188 this.isPolled = true;
189
190 setTimeout(() => {
191 this.isPolled = false;
192 }, ms('1s'));
193 }
194
195 if (Date.now() - service.lastPollAnswer < ms('0.2s')) {
196 this.isPollAnswered = true;
197
198 setTimeout(() => {
199 this.isPollAnswered = false;
200 }, ms('1s'));
201 }
202 });
203 }
204
205 this.checkForLongPress(stores.settings.app.enableLongPressServiceHint);
206 }
207
208 render() {
209 const {
210 classes,
211 service,
212 clickHandler,
213 shortcutIndex,
214 reload,
215 toggleNotifications,
216 toggleAudio,
217 toggleDarkMode,
218 deleteService,
219 disableService,
220 enableService,
221 hibernateService,
222 wakeUpService,
223 openSettings,
224 showMessageBadgeWhenMutedSetting,
225 showServiceNameSetting,
226 showMessageBadgesEvenWhenMuted,
227 } = this.props;
228 const { intl } = this.props;
229
230 const menuTemplate = [
231 {
232 label: service.name || service.recipe.name,
233 enabled: false,
234 },
235 {
236 type: 'separator',
237 },
238 {
239 label: intl.formatMessage(messages.reload),
240 click: reload,
241 accelerator: `${cmdOrCtrlShortcutKey()}+R`,
242 enabled: service.isEnabled,
243 },
244 {
245 label: intl.formatMessage(globalMessages.edit),
246 click: () =>
247 openSettings({
248 path: `services/edit/${service.id}`,
249 }),
250 },
251 {
252 type: 'separator',
253 },
254 {
255 label: service.isNotificationEnabled
256 ? intl.formatMessage(messages.disableNotifications)
257 : intl.formatMessage(messages.enableNotifications),
258 click: () => toggleNotifications(),
259 accelerator: `${cmdOrCtrlShortcutKey()}+${altKey()}+N`,
260 enabled: service.isEnabled,
261 },
262 {
263 label: service.isMuted
264 ? intl.formatMessage(messages.enableAudio)
265 : intl.formatMessage(messages.disableAudio),
266 click: () => toggleAudio(),
267 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+A`,
268 enabled: service.isEnabled,
269 },
270 {
271 label: service.isDarkModeEnabled
272 ? intl.formatMessage(messages.disableDarkMode)
273 : intl.formatMessage(messages.enableDarkMode),
274 click: () => toggleDarkMode(),
275 accelerator: `${shiftKey()}+${altKey()}+D`,
276 enabled: service.isEnabled,
277 },
278 {
279 label: intl.formatMessage(
280 service.isEnabled ? messages.disableService : messages.enableService,
281 ),
282 click: () => (service.isEnabled ? disableService() : enableService()),
283 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+S`,
284 },
285 {
286 label: intl.formatMessage(
287 service.isHibernating
288 ? messages.wakeUpService
289 : messages.hibernateService,
290 ),
291 // eslint-disable-next-line no-confusing-arrow
292 click: () =>
293 service.isHibernating ? wakeUpService() : hibernateService(),
294 enabled: service.isEnabled && service.canHibernate,
295 },
296 {
297 type: 'separator',
298 },
299 {
300 label: intl.formatMessage(messages.deleteService),
301 click: () => {
302 const selection = dialog.showMessageBoxSync(app.mainWindow, {
303 type: 'question',
304 message: intl.formatMessage(messages.deleteService),
305 detail: intl.formatMessage(messages.confirmDeleteService, {
306 serviceName: service.name || service.recipe.name,
307 }),
308 buttons: [
309 intl.formatMessage(globalMessages.yes),
310 intl.formatMessage(globalMessages.no),
311 ],
312 });
313 if (selection === 0) {
314 deleteService();
315 }
316 },
317 },
318 ];
319 const menu = Menu.buildFromTemplate(menuTemplate);
320
321 let notificationBadge = null;
322 if (
323 (showMessageBadgeWhenMutedSetting || service.isNotificationEnabled) &&
324 showMessageBadgesEvenWhenMuted &&
325 service.isBadgeEnabled
326 ) {
327 notificationBadge = (
328 <>
329 {service.unreadDirectMessageCount > 0 && (
330 <span className="tab-item__message-count">
331 {service.unreadDirectMessageCount}
332 </span>
333 )}
334 {service.unreadIndirectMessageCount > 0 &&
335 service.unreadDirectMessageCount === 0 &&
336 service.isIndirectMessageBadgeEnabled && (
337 <span className="tab-item__message-count is-indirect">•</span>
338 )}
339 {service.isHibernating && (
340 <span className="tab-item__message-count hibernating">•</span>
341 )}
342 </>
343 );
344 }
345
346 let errorBadge = null;
347 if (service.isError) {
348 errorBadge = (
349 <Icon icon={mdiExclamation} className="tab-item__error-icon" />
350 );
351 }
352
353 const showMediaBadge =
354 service.isMediaBadgeEnabled &&
355 service.isMediaPlaying &&
356 service.isEnabled;
357 const mediaBadge = (
358 <Icon icon={mdiVolumeSource} className="tab-item__icon" />
359 );
360
361 return (
362 <li
363 className={classnames({
364 [classes.stale]:
365 IS_SERVICE_DEBUGGING_ENABLED && service.lostRecipeConnection,
366 'tab-item': true,
367 'is-active': service.isActive,
368 'has-custom-icon': service.hasCustomIcon,
369 'is-disabled': !service.isEnabled,
370 'is-label-enabled': showServiceNameSetting,
371 })}
372 onClick={clickHandler}
373 onContextMenu={() => menu.popup()}
374 data-tip={`${service.name} ${
375 shortcutIndex <= 9
376 ? `(${cmdOrCtrlShortcutKey(false)}+${shortcutIndex})`
377 : ''
378 }`}
379 >
380 <img src={service.icon} className="tab-item__icon" alt="" />
381 {showServiceNameSetting && (
382 <span className="tab-item__label">{service.name}</span>
383 )}
384 {notificationBadge}
385 {errorBadge}
386 {showMediaBadge && mediaBadge}
387 {IS_SERVICE_DEBUGGING_ENABLED && (
388 <>
389 <div
390 className={classnames({
391 [classes.pollIndicator]: true,
392 [classes.pollIndicatorPoll]: true,
393 [classes.polled]: this.isPolled,
394 })}
395 />
396 <div
397 className={classnames({
398 [classes.pollIndicator]: true,
399 [classes.pollIndicatorAnswer]: true,
400 [classes.pollAnswered]: this.isPollAnswered,
401 })}
402 />
403 </>
404 )}
405 {shortcutIndex <= 9 && this.state.showShortcutIndex && (
406 <span className="tab-item__shortcut-index">{shortcutIndex}</span>
407 )}
408 </li>
409 );
410 }
411}
412
413export default injectIntl(
414 SortableElement(
415 injectSheet(styles, { injectTheme: true })(
416 inject('stores')(observer(TabItem)),
417 ),
418 ),
419);