aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/services/tabs/TabItem.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/services/tabs/TabItem.js')
-rw-r--r--src/components/services/tabs/TabItem.js221
1 files changed, 144 insertions, 77 deletions
diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js
index 5c3149a11..6a6d2c8c5 100644
--- a/src/components/services/tabs/TabItem.js
+++ b/src/components/services/tabs/TabItem.js
@@ -1,6 +1,4 @@
1import { 1import { Menu, dialog, app, getCurrentWindow } from '@electron/remote';
2 Menu, dialog, app, getCurrentWindow,
3} from '@electron/remote';
4import React, { Component } from 'react'; 2import React, { Component } from 'react';
5import { defineMessages, intlShape } from 'react-intl'; 3import { defineMessages, intlShape } from 'react-intl';
6import PropTypes from 'prop-types'; 4import PropTypes from 'prop-types';
@@ -12,9 +10,11 @@ import ms from 'ms';
12 10
13import { observable, autorun } from 'mobx'; 11import { observable, autorun } from 'mobx';
14import ServiceModel from '../../../models/Service'; 12import ServiceModel from '../../../models/Service';
15import { ctrlKey, cmdKey } from '../../../environment'; 13import { shortcutKey } from '../../../environment';
16 14
17const IS_SERVICE_DEBUGGING_ENABLED = (localStorage.getItem('debug') || '').includes('Ferdi:Service'); 15const IS_SERVICE_DEBUGGING_ENABLED = (
16 localStorage.getItem('debug') || ''
17).includes('Ferdi:Service');
18 18
19const messages = defineMessages({ 19const messages = defineMessages({
20 reload: { 20 reload: {
@@ -41,6 +41,14 @@ const messages = defineMessages({
41 id: 'tabs.item.enableAudio', 41 id: 'tabs.item.enableAudio',
42 defaultMessage: '!!!Enable audio', 42 defaultMessage: '!!!Enable audio',
43 }, 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 },
44 disableService: { 52 disableService: {
45 id: 'tabs.item.disableService', 53 id: 'tabs.item.disableService',
46 defaultMessage: '!!!Disable Service', 54 defaultMessage: '!!!Disable Service',
@@ -49,16 +57,35 @@ const messages = defineMessages({
49 id: 'tabs.item.enableService', 57 id: 'tabs.item.enableService',
50 defaultMessage: '!!!Enable Service', 58 defaultMessage: '!!!Enable Service',
51 }, 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 },
52 deleteService: { 68 deleteService: {
53 id: 'tabs.item.deleteService', 69 id: 'tabs.item.deleteService',
54 defaultMessage: '!!!Delete Service', 70 defaultMessage: '!!!Delete Service',
55 }, 71 },
56 confirmDeleteService: { 72 confirmDeleteService: {
57 id: 'tabs.item.confirmDeleteService', 73 id: 'tabs.item.confirmDeleteService',
58 defaultMessage: '!!!Do you really want to delete the {serviceName} service?', 74 defaultMessage:
75 '!!!Do you really want to delete the {serviceName} service?',
59 }, 76 },
60}); 77});
61 78
79let pollIndicatorTransition = 'none';
80let polledTransition = 'none';
81let pollAnsweredTransition = 'none';
82
83if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) {
84 pollIndicatorTransition = 'background 0.5s';
85 polledTransition = 'background 0.1s';
86 pollAnsweredTransition = 'background 0.1s';
87}
88
62const styles = { 89const styles = {
63 pollIndicator: { 90 pollIndicator: {
64 position: 'absolute', 91 position: 'absolute',
@@ -67,7 +94,7 @@ const styles = {
67 height: 10, 94 height: 10,
68 borderRadius: 5, 95 borderRadius: 5,
69 background: 'gray', 96 background: 'gray',
70 transition: 'background 0.5s', 97 transition: pollIndicatorTransition,
71 }, 98 },
72 pollIndicatorPoll: { 99 pollIndicatorPoll: {
73 left: 2, 100 left: 2,
@@ -77,18 +104,20 @@ const styles = {
77 }, 104 },
78 polled: { 105 polled: {
79 background: 'yellow !important', 106 background: 'yellow !important',
80 transition: 'background 0.1s', 107 transition: polledTransition,
81 }, 108 },
82 pollAnswered: { 109 pollAnswered: {
83 background: 'green !important', 110 background: 'green !important',
84 transition: 'background 0.1s', 111 transition: pollAnsweredTransition,
85 }, 112 },
86 stale: { 113 stale: {
87 background: 'red !important', 114 background: 'red !important',
88 }, 115 },
89}; 116};
90 117
91@injectSheet(styles) @observer class TabItem extends Component { 118@injectSheet(styles)
119@observer
120class TabItem extends Component {
92 static propTypes = { 121 static propTypes = {
93 classes: PropTypes.object.isRequired, 122 classes: PropTypes.object.isRequired,
94 service: PropTypes.instanceOf(ServiceModel).isRequired, 123 service: PropTypes.instanceOf(ServiceModel).isRequired,
@@ -97,10 +126,13 @@ const styles = {
97 reload: PropTypes.func.isRequired, 126 reload: PropTypes.func.isRequired,
98 toggleNotifications: PropTypes.func.isRequired, 127 toggleNotifications: PropTypes.func.isRequired,
99 toggleAudio: PropTypes.func.isRequired, 128 toggleAudio: PropTypes.func.isRequired,
129 toggleDarkMode: PropTypes.func.isRequired,
100 openSettings: PropTypes.func.isRequired, 130 openSettings: PropTypes.func.isRequired,
101 deleteService: PropTypes.func.isRequired, 131 deleteService: PropTypes.func.isRequired,
102 disableService: PropTypes.func.isRequired, 132 disableService: PropTypes.func.isRequired,
103 enableService: PropTypes.func.isRequired, 133 enableService: PropTypes.func.isRequired,
134 hibernateService: PropTypes.func.isRequired,
135 wakeUpService: PropTypes.func.isRequired,
104 showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired, 136 showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired,
105 showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired, 137 showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired,
106 }; 138 };
@@ -121,13 +153,17 @@ const styles = {
121 if (Date.now() - service.lastPoll < ms('0.2s')) { 153 if (Date.now() - service.lastPoll < ms('0.2s')) {
122 this.isPolled = true; 154 this.isPolled = true;
123 155
124 setTimeout(() => { this.isPolled = false; }, ms('1s')); 156 setTimeout(() => {
157 this.isPolled = false;
158 }, ms('1s'));
125 } 159 }
126 160
127 if (Date.now() - service.lastPollAnswer < ms('0.2s')) { 161 if (Date.now() - service.lastPollAnswer < ms('0.2s')) {
128 this.isPollAnswered = true; 162 this.isPollAnswered = true;
129 163
130 setTimeout(() => { this.isPollAnswered = false; }, ms('1s')); 164 setTimeout(() => {
165 this.isPollAnswered = false;
166 }, ms('1s'));
131 } 167 }
132 }); 168 });
133 } 169 }
@@ -142,67 +178,103 @@ const styles = {
142 reload, 178 reload,
143 toggleNotifications, 179 toggleNotifications,
144 toggleAudio, 180 toggleAudio,
181 toggleDarkMode,
145 deleteService, 182 deleteService,
146 disableService, 183 disableService,
147 enableService, 184 enableService,
185 hibernateService,
186 wakeUpService,
148 openSettings, 187 openSettings,
149 showMessageBadgeWhenMutedSetting, 188 showMessageBadgeWhenMutedSetting,
150 showMessageBadgesEvenWhenMuted, 189 showMessageBadgesEvenWhenMuted,
151 } = this.props; 190 } = this.props;
152 const { intl } = this.context; 191 const { intl } = this.context;
153 192
154 const menuTemplate = [{ 193 const menuTemplate = [
155 label: service.name || service.recipe.name, 194 {
156 enabled: false, 195 label: service.name || service.recipe.name,
157 }, { 196 enabled: false,
158 type: 'separator', 197 },
159 }, { 198 {
160 label: intl.formatMessage(messages.reload), 199 type: 'separator',
161 click: reload, 200 },
162 accelerator: `${cmdKey}+R`, 201 {
163 }, { 202 label: intl.formatMessage(messages.reload),
164 label: intl.formatMessage(messages.edit), 203 click: reload,
165 click: () => openSettings({ 204 accelerator: `${shortcutKey()}+R`,
166 path: `services/edit/${service.id}`, 205 },
167 }), 206 {
168 }, { 207 label: intl.formatMessage(messages.edit),
169 type: 'separator', 208 click: () =>
170 }, { 209 openSettings({
171 label: service.isNotificationEnabled 210 path: `services/edit/${service.id}`,
172 ? intl.formatMessage(messages.disableNotifications) 211 }),
173 : intl.formatMessage(messages.enableNotifications),
174 click: () => toggleNotifications(),
175 }, {
176 label: service.isMuted
177 ? intl.formatMessage(messages.enableAudio)
178 : intl.formatMessage(messages.disableAudio),
179 click: () => toggleAudio(),
180 }, {
181 label: intl.formatMessage(service.isEnabled ? messages.disableService : messages.enableService),
182 click: () => (service.isEnabled ? disableService() : enableService()),
183 }, {
184 type: 'separator',
185 }, {
186 label: intl.formatMessage(messages.deleteService),
187 click: () => {
188 const selection = dialog.showMessageBoxSync(app.mainWindow, {
189 type: 'question',
190 message: intl.formatMessage(messages.deleteService),
191 detail: intl.formatMessage(messages.confirmDeleteService, { serviceName: service.name || service.recipe.name }),
192 buttons: [
193 'Yes',
194 'No',
195 ],
196 });
197 if (selection === 0) {
198 deleteService();
199 }
200 }, 212 },
201 }]; 213 {
214 type: 'separator',
215 },
216 {
217 label: service.isNotificationEnabled
218 ? intl.formatMessage(messages.disableNotifications)
219 : intl.formatMessage(messages.enableNotifications),
220 click: () => toggleNotifications(),
221 },
222 {
223 label: service.isMuted
224 ? intl.formatMessage(messages.enableAudio)
225 : intl.formatMessage(messages.disableAudio),
226 click: () => toggleAudio(),
227 },
228 {
229 label: service.isDarkModeEnabled
230 ? intl.formatMessage(messages.enableDarkMode)
231 : intl.formatMessage(messages.disableDarkMode),
232 click: () => toggleDarkMode(),
233 },
234 {
235 label: intl.formatMessage(
236 service.isEnabled ? messages.disableService : messages.enableService,
237 ),
238 click: () => (service.isEnabled ? disableService() : enableService()),
239 },
240 {
241 label: intl.formatMessage(
242 service.isHibernating
243 ? messages.wakeUpService
244 : messages.hibernateService,
245 ),
246 click: () =>
247 (service.isHibernating ? wakeUpService() : hibernateService()),
248 enabled: service.canHibernate,
249 },
250 {
251 type: 'separator',
252 },
253 {
254 label: intl.formatMessage(messages.deleteService),
255 click: () => {
256 const selection = dialog.showMessageBoxSync(app.mainWindow, {
257 type: 'question',
258 message: intl.formatMessage(messages.deleteService),
259 detail: intl.formatMessage(messages.confirmDeleteService, {
260 serviceName: service.name || service.recipe.name,
261 }),
262 buttons: ['Yes', 'No'],
263 });
264 if (selection === 0) {
265 deleteService();
266 }
267 },
268 },
269 ];
202 const menu = Menu.buildFromTemplate(menuTemplate); 270 const menu = Menu.buildFromTemplate(menuTemplate);
203 271
204 let notificationBadge = null; 272 let notificationBadge = null;
205 if ((showMessageBadgeWhenMutedSetting || service.isNotificationEnabled) && showMessageBadgesEvenWhenMuted && service.isBadgeEnabled) { 273 if (
274 (showMessageBadgeWhenMutedSetting || service.isNotificationEnabled) &&
275 showMessageBadgesEvenWhenMuted &&
276 service.isBadgeEnabled
277 ) {
206 notificationBadge = ( 278 notificationBadge = (
207 <span> 279 <span>
208 {service.unreadDirectMessageCount > 0 && ( 280 {service.unreadDirectMessageCount > 0 && (
@@ -210,17 +282,13 @@ const styles = {
210 {service.unreadDirectMessageCount} 282 {service.unreadDirectMessageCount}
211 </span> 283 </span>
212 )} 284 )}
213 {service.unreadIndirectMessageCount > 0 285 {service.unreadIndirectMessageCount > 0 &&
214 && service.unreadDirectMessageCount === 0 286 service.unreadDirectMessageCount === 0 &&
215 && service.isIndirectMessageBadgeEnabled && ( 287 service.isIndirectMessageBadgeEnabled && (
216 <span className="tab-item__message-count is-indirect"> 288 <span className="tab-item__message-count is-indirect">•</span>
217
218 </span>
219 )} 289 )}
220 {service.isHibernating && !service.isHibernationEnabled && ( 290 {service.isHibernating && (
221 <span className="tab-item__message-count hibernating"> 291 <span className="tab-item__message-count hibernating">•</span>
222
223 </span>
224 )} 292 )}
225 </span> 293 </span>
226 ); 294 );
@@ -229,7 +297,8 @@ const styles = {
229 return ( 297 return (
230 <li 298 <li
231 className={classnames({ 299 className={classnames({
232 [classes.stale]: IS_SERVICE_DEBUGGING_ENABLED && service.lostRecipeConnection, 300 [classes.stale]:
301 IS_SERVICE_DEBUGGING_ENABLED && service.lostRecipeConnection,
233 'tab-item': true, 302 'tab-item': true,
234 'is-active': service.isActive, 303 'is-active': service.isActive,
235 'has-custom-icon': service.hasCustomIcon, 304 'has-custom-icon': service.hasCustomIcon,
@@ -237,13 +306,11 @@ const styles = {
237 })} 306 })}
238 onClick={clickHandler} 307 onClick={clickHandler}
239 onContextMenu={() => menu.popup(getCurrentWindow())} 308 onContextMenu={() => menu.popup(getCurrentWindow())}
240 data-tip={`${service.name} ${shortcutIndex <= 9 ? `(${ctrlKey}+${shortcutIndex})` : ''}`} 309 data-tip={`${service.name} ${
310 shortcutIndex <= 9 ? `(${shortcutKey(false)}+${shortcutIndex})` : ''
311 }`}
241 > 312 >
242 <img 313 <img src={service.icon} className="tab-item__icon" alt="" />
243 src={service.icon}
244 className="tab-item__icon"
245 alt=""
246 />
247 {notificationBadge} 314 {notificationBadge}
248 {IS_SERVICE_DEBUGGING_ENABLED && ( 315 {IS_SERVICE_DEBUGGING_ENABLED && (
249 <> 316 <>