diff options
Diffstat (limited to 'src/components/services')
-rw-r--r-- | src/components/services/content/ConnectionLostBanner.js | 36 | ||||
-rw-r--r-- | src/components/services/tabs/TabItem.js | 193 |
2 files changed, 129 insertions, 100 deletions
diff --git a/src/components/services/content/ConnectionLostBanner.js b/src/components/services/content/ConnectionLostBanner.js index 0b9d3122c..343ddf8b4 100644 --- a/src/components/services/content/ConnectionLostBanner.js +++ b/src/components/services/content/ConnectionLostBanner.js | |||
@@ -5,9 +5,7 @@ import injectSheet from 'react-jss'; | |||
5 | import { Icon } from '@meetfranz/ui'; | 5 | import { Icon } from '@meetfranz/ui'; |
6 | import { intlShape, defineMessages } from 'react-intl'; | 6 | import { intlShape, defineMessages } from 'react-intl'; |
7 | 7 | ||
8 | import { | 8 | import { mdiAlert } from '@mdi/js'; |
9 | mdiAlert, | ||
10 | } from '@mdi/js'; | ||
11 | import { LIVE_API_FERDI_WEBSITE } from '../../../config'; | 9 | import { LIVE_API_FERDI_WEBSITE } from '../../../config'; |
12 | // import { Button } from '@meetfranz/forms'; | 10 | // import { Button } from '@meetfranz/forms'; |
13 | 11 | ||
@@ -26,7 +24,13 @@ const messages = defineMessages({ | |||
26 | }, | 24 | }, |
27 | }); | 25 | }); |
28 | 26 | ||
29 | const styles = (theme) => ({ | 27 | let buttonTransition = 'none'; |
28 | |||
29 | if (window.matchMedia('(prefers-reduced-motion: no-preference)')) { | ||
30 | buttonTransition = 'opacity 0.25s'; | ||
31 | } | ||
32 | |||
33 | const styles = theme => ({ | ||
30 | root: { | 34 | root: { |
31 | background: theme.colorBackground, | 35 | background: theme.colorBackground, |
32 | borderRadius: theme.borderRadius, | 36 | borderRadius: theme.borderRadius, |
@@ -47,7 +51,7 @@ const styles = (theme) => ({ | |||
47 | opacity: 0.7, | 51 | opacity: 0.7, |
48 | }, | 52 | }, |
49 | button: { | 53 | button: { |
50 | transition: 'opacity 0.25s', | 54 | transition: buttonTransition, |
51 | color: theme.colorText, | 55 | color: theme.colorText, |
52 | border: [1, 'solid', theme.colorText], | 56 | border: [1, 'solid', theme.colorText], |
53 | borderRadius: theme.borderRadiusSmall, | 57 | borderRadius: theme.borderRadiusSmall, |
@@ -65,13 +69,14 @@ const styles = (theme) => ({ | |||
65 | }, | 69 | }, |
66 | }); | 70 | }); |
67 | 71 | ||
68 | @injectSheet(styles) @observer | 72 | @injectSheet(styles) |
73 | @observer | ||
69 | class ConnectionLostBanner extends Component { | 74 | class ConnectionLostBanner extends Component { |
70 | static propTypes = { | 75 | static propTypes = { |
71 | classes: PropTypes.object.isRequired, | 76 | classes: PropTypes.object.isRequired, |
72 | name: PropTypes.string.isRequired, | 77 | name: PropTypes.string.isRequired, |
73 | reload: PropTypes.func.isRequired, | 78 | reload: PropTypes.func.isRequired, |
74 | } | 79 | }; |
75 | 80 | ||
76 | static contextTypes = { | 81 | static contextTypes = { |
77 | intl: intlShape, | 82 | intl: intlShape, |
@@ -80,20 +85,13 @@ class ConnectionLostBanner extends Component { | |||
80 | inputRef = React.createRef(); | 85 | inputRef = React.createRef(); |
81 | 86 | ||
82 | render() { | 87 | render() { |
83 | const { | 88 | const { classes, name, reload } = this.props; |
84 | classes, | ||
85 | name, | ||
86 | reload, | ||
87 | } = this.props; | ||
88 | 89 | ||
89 | const { intl } = this.context; | 90 | const { intl } = this.context; |
90 | 91 | ||
91 | return ( | 92 | return ( |
92 | <div className={classes.root}> | 93 | <div className={classes.root}> |
93 | <Icon | 94 | <Icon icon={mdiAlert} className={classes.icon} /> |
94 | icon={mdiAlert} | ||
95 | className={classes.icon} | ||
96 | /> | ||
97 | <p> | 95 | <p> |
98 | {intl.formatMessage(messages.text, { name })} | 96 | {intl.formatMessage(messages.text, { name })} |
99 | <br /> | 97 | <br /> |
@@ -104,11 +102,7 @@ class ConnectionLostBanner extends Component { | |||
104 | {intl.formatMessage(messages.moreInformation)} | 102 | {intl.formatMessage(messages.moreInformation)} |
105 | </a> | 103 | </a> |
106 | </p> | 104 | </p> |
107 | <button | 105 | <button type="button" className={classes.button} onClick={reload}> |
108 | type="button" | ||
109 | className={classes.button} | ||
110 | onClick={reload} | ||
111 | > | ||
112 | {intl.formatMessage(messages.cta)} | 106 | {intl.formatMessage(messages.cta)} |
113 | </button> | 107 | </button> |
114 | </div> | 108 | </div> |
diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js index ccf3333f8..023e152c7 100644 --- a/src/components/services/tabs/TabItem.js +++ b/src/components/services/tabs/TabItem.js | |||
@@ -1,6 +1,4 @@ | |||
1 | import { | 1 | import { Menu, dialog, app, getCurrentWindow } from '@electron/remote'; |
2 | Menu, dialog, app, getCurrentWindow, | ||
3 | } from '@electron/remote'; | ||
4 | import React, { Component } from 'react'; | 2 | import React, { Component } from 'react'; |
5 | import { defineMessages, intlShape } from 'react-intl'; | 3 | import { defineMessages, intlShape } from 'react-intl'; |
6 | import PropTypes from 'prop-types'; | 4 | import PropTypes from 'prop-types'; |
@@ -14,7 +12,9 @@ import { observable, autorun } from 'mobx'; | |||
14 | import ServiceModel from '../../../models/Service'; | 12 | import ServiceModel from '../../../models/Service'; |
15 | import { ctrlKey, cmdKey } from '../../../environment'; | 13 | import { ctrlKey, cmdKey } from '../../../environment'; |
16 | 14 | ||
17 | const IS_SERVICE_DEBUGGING_ENABLED = (localStorage.getItem('debug') || '').includes('Ferdi:Service'); | 15 | const IS_SERVICE_DEBUGGING_ENABLED = ( |
16 | localStorage.getItem('debug') || '' | ||
17 | ).includes('Ferdi:Service'); | ||
18 | 18 | ||
19 | const messages = defineMessages({ | 19 | const messages = defineMessages({ |
20 | reload: { | 20 | reload: { |
@@ -63,10 +63,21 @@ const messages = defineMessages({ | |||
63 | }, | 63 | }, |
64 | confirmDeleteService: { | 64 | confirmDeleteService: { |
65 | id: 'tabs.item.confirmDeleteService', | 65 | id: 'tabs.item.confirmDeleteService', |
66 | defaultMessage: '!!!Do you really want to delete the {serviceName} service?', | 66 | defaultMessage: |
67 | '!!!Do you really want to delete the {serviceName} service?', | ||
67 | }, | 68 | }, |
68 | }); | 69 | }); |
69 | 70 | ||
71 | let pollIndicatorTransition = 'none'; | ||
72 | let polledTransition = 'none'; | ||
73 | let pollAnsweredTransition = 'none'; | ||
74 | |||
75 | if (window.matchMedia('(prefers-reduced-motion: no-preference)')) { | ||
76 | pollIndicatorTransition = 'background 0.5s'; | ||
77 | polledTransition = 'background 0.1s'; | ||
78 | pollAnsweredTransition = 'background 0.1s'; | ||
79 | } | ||
80 | |||
70 | const styles = { | 81 | const styles = { |
71 | pollIndicator: { | 82 | pollIndicator: { |
72 | position: 'absolute', | 83 | position: 'absolute', |
@@ -75,7 +86,7 @@ const styles = { | |||
75 | height: 10, | 86 | height: 10, |
76 | borderRadius: 5, | 87 | borderRadius: 5, |
77 | background: 'gray', | 88 | background: 'gray', |
78 | transition: 'background 0.5s', | 89 | transition: pollIndicatorTransition, |
79 | }, | 90 | }, |
80 | pollIndicatorPoll: { | 91 | pollIndicatorPoll: { |
81 | left: 2, | 92 | left: 2, |
@@ -85,18 +96,20 @@ const styles = { | |||
85 | }, | 96 | }, |
86 | polled: { | 97 | polled: { |
87 | background: 'yellow !important', | 98 | background: 'yellow !important', |
88 | transition: 'background 0.1s', | 99 | transition: polledTransition, |
89 | }, | 100 | }, |
90 | pollAnswered: { | 101 | pollAnswered: { |
91 | background: 'green !important', | 102 | background: 'green !important', |
92 | transition: 'background 0.1s', | 103 | transition: pollAnsweredTransition, |
93 | }, | 104 | }, |
94 | stale: { | 105 | stale: { |
95 | background: 'red !important', | 106 | background: 'red !important', |
96 | }, | 107 | }, |
97 | }; | 108 | }; |
98 | 109 | ||
99 | @injectSheet(styles) @observer class TabItem extends Component { | 110 | @injectSheet(styles) |
111 | @observer | ||
112 | class TabItem extends Component { | ||
100 | static propTypes = { | 113 | static propTypes = { |
101 | classes: PropTypes.object.isRequired, | 114 | classes: PropTypes.object.isRequired, |
102 | service: PropTypes.instanceOf(ServiceModel).isRequired, | 115 | service: PropTypes.instanceOf(ServiceModel).isRequired, |
@@ -131,13 +144,17 @@ const styles = { | |||
131 | if (Date.now() - service.lastPoll < ms('0.2s')) { | 144 | if (Date.now() - service.lastPoll < ms('0.2s')) { |
132 | this.isPolled = true; | 145 | this.isPolled = true; |
133 | 146 | ||
134 | setTimeout(() => { this.isPolled = false; }, ms('1s')); | 147 | setTimeout(() => { |
148 | this.isPolled = false; | ||
149 | }, ms('1s')); | ||
135 | } | 150 | } |
136 | 151 | ||
137 | if (Date.now() - service.lastPollAnswer < ms('0.2s')) { | 152 | if (Date.now() - service.lastPollAnswer < ms('0.2s')) { |
138 | this.isPollAnswered = true; | 153 | this.isPollAnswered = true; |
139 | 154 | ||
140 | setTimeout(() => { this.isPollAnswered = false; }, ms('1s')); | 155 | setTimeout(() => { |
156 | this.isPollAnswered = false; | ||
157 | }, ms('1s')); | ||
141 | } | 158 | } |
142 | }); | 159 | }); |
143 | } | 160 | } |
@@ -163,62 +180,85 @@ const styles = { | |||
163 | } = this.props; | 180 | } = this.props; |
164 | const { intl } = this.context; | 181 | const { intl } = this.context; |
165 | 182 | ||
166 | const menuTemplate = [{ | 183 | const menuTemplate = [ |
167 | label: service.name || service.recipe.name, | 184 | { |
168 | enabled: false, | 185 | label: service.name || service.recipe.name, |
169 | }, { | 186 | enabled: false, |
170 | type: 'separator', | 187 | }, |
171 | }, { | 188 | { |
172 | label: intl.formatMessage(messages.reload), | 189 | type: 'separator', |
173 | click: reload, | 190 | }, |
174 | accelerator: `${cmdKey}+R`, | 191 | { |
175 | }, { | 192 | label: intl.formatMessage(messages.reload), |
176 | label: intl.formatMessage(messages.edit), | 193 | click: reload, |
177 | click: () => openSettings({ | 194 | accelerator: `${cmdKey}+R`, |
178 | path: `services/edit/${service.id}`, | 195 | }, |
179 | }), | 196 | { |
180 | }, { | 197 | label: intl.formatMessage(messages.edit), |
181 | type: 'separator', | 198 | click: () => |
182 | }, { | 199 | openSettings({ |
183 | label: service.isNotificationEnabled | 200 | path: `services/edit/${service.id}`, |
184 | ? intl.formatMessage(messages.disableNotifications) | 201 | }), |
185 | : intl.formatMessage(messages.enableNotifications), | 202 | }, |
186 | click: () => toggleNotifications(), | 203 | { |
187 | }, { | 204 | type: 'separator', |
188 | label: service.isMuted | ||
189 | ? intl.formatMessage(messages.enableAudio) | ||
190 | : intl.formatMessage(messages.disableAudio), | ||
191 | click: () => toggleAudio(), | ||
192 | }, { | ||
193 | label: intl.formatMessage(service.isEnabled ? messages.disableService : messages.enableService), | ||
194 | click: () => (service.isEnabled ? disableService() : enableService()), | ||
195 | }, { | ||
196 | label: intl.formatMessage(service.isHibernating ? messages.wakeUpService : messages.hibernateService), | ||
197 | click: () => (service.isHibernating ? wakeUpService() : hibernateService()), | ||
198 | enabled: service.canHibernate, | ||
199 | }, { | ||
200 | type: 'separator', | ||
201 | }, { | ||
202 | label: intl.formatMessage(messages.deleteService), | ||
203 | click: () => { | ||
204 | const selection = dialog.showMessageBoxSync(app.mainWindow, { | ||
205 | type: 'question', | ||
206 | message: intl.formatMessage(messages.deleteService), | ||
207 | detail: intl.formatMessage(messages.confirmDeleteService, { serviceName: service.name || service.recipe.name }), | ||
208 | buttons: [ | ||
209 | 'Yes', | ||
210 | 'No', | ||
211 | ], | ||
212 | }); | ||
213 | if (selection === 0) { | ||
214 | deleteService(); | ||
215 | } | ||
216 | }, | 205 | }, |
217 | }]; | 206 | { |
207 | label: service.isNotificationEnabled | ||
208 | ? intl.formatMessage(messages.disableNotifications) | ||
209 | : intl.formatMessage(messages.enableNotifications), | ||
210 | click: () => toggleNotifications(), | ||
211 | }, | ||
212 | { | ||
213 | label: service.isMuted | ||
214 | ? intl.formatMessage(messages.enableAudio) | ||
215 | : intl.formatMessage(messages.disableAudio), | ||
216 | click: () => toggleAudio(), | ||
217 | }, | ||
218 | { | ||
219 | label: intl.formatMessage( | ||
220 | service.isEnabled ? messages.disableService : messages.enableService, | ||
221 | ), | ||
222 | click: () => (service.isEnabled ? disableService() : enableService()), | ||
223 | }, | ||
224 | { | ||
225 | label: intl.formatMessage( | ||
226 | service.isHibernating | ||
227 | ? messages.wakeUpService | ||
228 | : messages.hibernateService, | ||
229 | ), | ||
230 | click: () => | ||
231 | (service.isHibernating ? wakeUpService() : hibernateService()), | ||
232 | enabled: service.canHibernate, | ||
233 | }, | ||
234 | { | ||
235 | type: 'separator', | ||
236 | }, | ||
237 | { | ||
238 | label: intl.formatMessage(messages.deleteService), | ||
239 | click: () => { | ||
240 | const selection = dialog.showMessageBoxSync(app.mainWindow, { | ||
241 | type: 'question', | ||
242 | message: intl.formatMessage(messages.deleteService), | ||
243 | detail: intl.formatMessage(messages.confirmDeleteService, { | ||
244 | serviceName: service.name || service.recipe.name, | ||
245 | }), | ||
246 | buttons: ['Yes', 'No'], | ||
247 | }); | ||
248 | if (selection === 0) { | ||
249 | deleteService(); | ||
250 | } | ||
251 | }, | ||
252 | }, | ||
253 | ]; | ||
218 | const menu = Menu.buildFromTemplate(menuTemplate); | 254 | const menu = Menu.buildFromTemplate(menuTemplate); |
219 | 255 | ||
220 | let notificationBadge = null; | 256 | let notificationBadge = null; |
221 | if ((showMessageBadgeWhenMutedSetting || service.isNotificationEnabled) && showMessageBadgesEvenWhenMuted && service.isBadgeEnabled) { | 257 | if ( |
258 | (showMessageBadgeWhenMutedSetting || service.isNotificationEnabled) && | ||
259 | showMessageBadgesEvenWhenMuted && | ||
260 | service.isBadgeEnabled | ||
261 | ) { | ||
222 | notificationBadge = ( | 262 | notificationBadge = ( |
223 | <span> | 263 | <span> |
224 | {service.unreadDirectMessageCount > 0 && ( | 264 | {service.unreadDirectMessageCount > 0 && ( |
@@ -226,17 +266,13 @@ const styles = { | |||
226 | {service.unreadDirectMessageCount} | 266 | {service.unreadDirectMessageCount} |
227 | </span> | 267 | </span> |
228 | )} | 268 | )} |
229 | {service.unreadIndirectMessageCount > 0 | 269 | {service.unreadIndirectMessageCount > 0 && |
230 | && service.unreadDirectMessageCount === 0 | 270 | service.unreadDirectMessageCount === 0 && |
231 | && service.isIndirectMessageBadgeEnabled && ( | 271 | service.isIndirectMessageBadgeEnabled && ( |
232 | <span className="tab-item__message-count is-indirect"> | 272 | <span className="tab-item__message-count is-indirect">•</span> |
233 | • | ||
234 | </span> | ||
235 | )} | 273 | )} |
236 | {service.isHibernating && ( | 274 | {service.isHibernating && ( |
237 | <span className="tab-item__message-count hibernating"> | 275 | <span className="tab-item__message-count hibernating">•</span> |
238 | • | ||
239 | </span> | ||
240 | )} | 276 | )} |
241 | </span> | 277 | </span> |
242 | ); | 278 | ); |
@@ -245,7 +281,8 @@ const styles = { | |||
245 | return ( | 281 | return ( |
246 | <li | 282 | <li |
247 | className={classnames({ | 283 | className={classnames({ |
248 | [classes.stale]: IS_SERVICE_DEBUGGING_ENABLED && service.lostRecipeConnection, | 284 | [classes.stale]: |
285 | IS_SERVICE_DEBUGGING_ENABLED && service.lostRecipeConnection, | ||
249 | 'tab-item': true, | 286 | 'tab-item': true, |
250 | 'is-active': service.isActive, | 287 | 'is-active': service.isActive, |
251 | 'has-custom-icon': service.hasCustomIcon, | 288 | 'has-custom-icon': service.hasCustomIcon, |
@@ -253,13 +290,11 @@ const styles = { | |||
253 | })} | 290 | })} |
254 | onClick={clickHandler} | 291 | onClick={clickHandler} |
255 | onContextMenu={() => menu.popup(getCurrentWindow())} | 292 | onContextMenu={() => menu.popup(getCurrentWindow())} |
256 | data-tip={`${service.name} ${shortcutIndex <= 9 ? `(${ctrlKey}+${shortcutIndex})` : ''}`} | 293 | data-tip={`${service.name} ${ |
294 | shortcutIndex <= 9 ? `(${ctrlKey}+${shortcutIndex})` : '' | ||
295 | }`} | ||
257 | > | 296 | > |
258 | <img | 297 | <img src={service.icon} className="tab-item__icon" alt="" /> |
259 | src={service.icon} | ||
260 | className="tab-item__icon" | ||
261 | alt="" | ||
262 | /> | ||
263 | {notificationBadge} | 298 | {notificationBadge} |
264 | {IS_SERVICE_DEBUGGING_ENABLED && ( | 299 | {IS_SERVICE_DEBUGGING_ENABLED && ( |
265 | <> | 300 | <> |