summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--appveyor.yml7
-rw-r--r--package.json1
-rw-r--r--src/actions/app.js4
-rw-r--r--src/actions/service.js3
-rw-r--r--src/components/layout/Sidebar.js52
-rw-r--r--src/components/services/content/ServiceWebview.js44
-rw-r--r--src/components/services/content/Services.js3
-rw-r--r--src/components/services/tabs/TabBarSortableList.js22
-rw-r--r--src/components/services/tabs/TabItem.js15
-rw-r--r--src/components/services/tabs/Tabbar.js3
-rw-r--r--src/components/settings/services/EditServiceForm.js12
-rw-r--r--src/components/settings/services/ServiceItem.js22
-rw-r--r--src/config.js1
-rw-r--r--src/containers/layout/AppLayoutContainer.js16
-rw-r--r--src/containers/settings/EditServiceScreen.js15
-rw-r--r--src/i18n/locales/en-US.json19
-rw-r--r--src/models/Service.js3
-rw-r--r--src/stores/AppStore.js27
-rw-r--r--src/stores/ServicesStore.js19
-rw-r--r--src/styles/layout.scss33
-rw-r--r--src/styles/settings.scss2
-rw-r--r--src/styles/tabs.scss8
-rw-r--r--yarn.lock35
23 files changed, 259 insertions, 107 deletions
diff --git a/appveyor.yml b/appveyor.yml
index c00198312..4b2796f4b 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -9,13 +9,14 @@ environment:
9version: 5.0.0.{build} 9version: 5.0.0.{build}
10 10
11install: 11install:
12 - ps: Install-Product node 7 12 - ps: Install-Product node 8
13 - yarn cache clean
13 - yarn add global gulp-cli@1.2.2 14 - yarn add global gulp-cli@1.2.2
14 - yarn add global gulpjs/gulp#4.0 15 - yarn add global gulpjs/gulp#4.0
15 - yarn install 16 - yarn install
16 17
17cache: 18# cache:
18 - "%LOCALAPPDATA%\\Yarn" 19# - "%LOCALAPPDATA%\\Yarn"
19 20
20before_build: 21before_build:
21 - yarn lint 22 - yarn lint
diff --git a/package.json b/package.json
index 9b56cdf27..f9f6ca91c 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
26 "author": "Stefan Malzner <stefan@adlk.io>", 26 "author": "Stefan Malzner <stefan@adlk.io>",
27 "license": "Apache-2.0", 27 "license": "Apache-2.0",
28 "dependencies": { 28 "dependencies": {
29 "@meetfranz/electron-notification-state": "^1.0.0",
29 "@paulcbetts/system-idle-time": "^1.0.4", 30 "@paulcbetts/system-idle-time": "^1.0.4",
30 "address-rfc2822": "^2.0.1", 31 "address-rfc2822": "^2.0.1",
31 "auto-launch": "https://github.com/meetfranz/node-auto-launch.git", 32 "auto-launch": "https://github.com/meetfranz/node-auto-launch.git",
diff --git a/src/actions/app.js b/src/actions/app.js
index 5db4b739e..25ff9344d 100644
--- a/src/actions/app.js
+++ b/src/actions/app.js
@@ -20,4 +20,8 @@ export default {
20 resetUpdateStatus: {}, 20 resetUpdateStatus: {},
21 installUpdate: {}, 21 installUpdate: {},
22 healthCheck: {}, 22 healthCheck: {},
23 muteApp: {
24 isMuted: PropTypes.bool.isRequired,
25 },
26 toggleMuteApp: {},
23}; 27};
diff --git a/src/actions/service.js b/src/actions/service.js
index ea6ea5acc..1b918251b 100644
--- a/src/actions/service.js
+++ b/src/actions/service.js
@@ -71,6 +71,9 @@ export default {
71 toggleNotifications: { 71 toggleNotifications: {
72 serviceId: PropTypes.string.isRequired, 72 serviceId: PropTypes.string.isRequired,
73 }, 73 },
74 toggleAudio: {
75 serviceId: PropTypes.string.isRequired,
76 },
74 openDevTools: { 77 openDevTools: {
75 serviceId: PropTypes.string.isRequired, 78 serviceId: PropTypes.string.isRequired,
76 }, 79 },
diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js
index 6a5c0f365..ea34e8702 100644
--- a/src/components/layout/Sidebar.js
+++ b/src/components/layout/Sidebar.js
@@ -11,16 +11,25 @@ const messages = defineMessages({
11 id: 'sidebar.settings', 11 id: 'sidebar.settings',
12 defaultMessage: '!!!Settings', 12 defaultMessage: '!!!Settings',
13 }, 13 },
14 addNewService: {
15 id: 'sidebar.addNewService',
16 defaultMessage: '!!!Add new service',
17 },
18 mute: {
19 id: 'sidebar.mute',
20 defaultMessage: '!!!Disable audio',
21 },
22 unmute: {
23 id: 'sidebar.unmute',
24 defaultMessage: '!!!Enable audio',
25 },
14}); 26});
15 27
16export default class Sidebar extends Component { 28export default class Sidebar extends Component {
17 static propTypes = { 29 static propTypes = {
18 openSettings: PropTypes.func.isRequired, 30 openSettings: PropTypes.func.isRequired,
19 isPremiumUser: PropTypes.bool, 31 toggleMuteApp: PropTypes.func.isRequired,
20 } 32 isAppMuted: PropTypes.bool.isRequired,
21
22 static defaultProps = {
23 isPremiumUser: false,
24 } 33 }
25 34
26 static contextTypes = { 35 static contextTypes = {
@@ -40,8 +49,9 @@ export default class Sidebar extends Component {
40 } 49 }
41 50
42 render() { 51 render() {
43 const { openSettings, isPremiumUser } = this.props; 52 const { openSettings, toggleMuteApp, isAppMuted } = this.props;
44 const { intl } = this.context; 53 const { intl } = this.context;
54
45 return ( 55 return (
46 <div className="sidebar"> 56 <div className="sidebar">
47 <Tabbar 57 <Tabbar
@@ -50,21 +60,25 @@ export default class Sidebar extends Component {
50 disableToolTip={() => this.disableToolTip()} 60 disableToolTip={() => this.disableToolTip()}
51 /> 61 />
52 <button 62 <button
53 onClick={openSettings} 63 onClick={toggleMuteApp}
54 className="sidebar__settings-button" 64 className={`sidebar__button sidebar__button--audio ${isAppMuted ? 'is-muted' : ''}`}
65 data-tip={`${intl.formatMessage(isAppMuted ? messages.unmute : messages.mute)} (${ctrlKey}+Shift+M)`}
66 >
67 <i className={`mdi mdi-bell${isAppMuted ? '-off' : ''}`} />
68 </button>
69 <button
70 onClick={() => openSettings({ path: 'recipes' })}
71 className="sidebar__button sidebar__button--new-service"
72 data-tip={`${intl.formatMessage(messages.addNewService)} (${ctrlKey}+N)`}
73 >
74 <i className="mdi mdi-plus-box" />
75 </button>
76 <button
77 onClick={() => openSettings({ path: 'app' })}
78 className="sidebar__button sidebar__button--settings"
55 data-tip={`${intl.formatMessage(messages.settings)} (${ctrlKey}+,)`} 79 data-tip={`${intl.formatMessage(messages.settings)} (${ctrlKey}+,)`}
56 > 80 >
57 {isPremiumUser && ( 81 <i className="mdi mdi-settings" />
58 <span className="emoji">
59 <img src="./assets/images/emoji/star.png" alt="" />
60 </span>
61 )}
62 <img
63 src="./assets/images/logo.svg"
64 className="sidebar__logo"
65 alt=""
66 />
67 {intl.formatMessage(messages.settings)}
68 </button> 82 </button>
69 {this.state.tooltipEnabled && ( 83 {this.state.tooltipEnabled && (
70 <ReactTooltip place="right" type="dark" effect="solid" /> 84 <ReactTooltip place="right" type="dark" effect="solid" />
diff --git a/src/components/services/content/ServiceWebview.js b/src/components/services/content/ServiceWebview.js
index a71017a6e..abbf21dee 100644
--- a/src/components/services/content/ServiceWebview.js
+++ b/src/components/services/content/ServiceWebview.js
@@ -16,6 +16,7 @@ export default class ServiceWebview extends Component {
16 service: PropTypes.instanceOf(ServiceModel).isRequired, 16 service: PropTypes.instanceOf(ServiceModel).isRequired,
17 setWebviewReference: PropTypes.func.isRequired, 17 setWebviewReference: PropTypes.func.isRequired,
18 reload: PropTypes.func.isRequired, 18 reload: PropTypes.func.isRequired,
19 isAppMuted: PropTypes.bool.isRequired,
19 enable: PropTypes.func.isRequired, 20 enable: PropTypes.func.isRequired,
20 }; 21 };
21 22
@@ -58,6 +59,7 @@ export default class ServiceWebview extends Component {
58 service, 59 service,
59 setWebviewReference, 60 setWebviewReference,
60 reload, 61 reload,
62 isAppMuted,
61 enable, 63 enable,
62 } = this.props; 64 } = this.props;
63 65
@@ -90,31 +92,23 @@ export default class ServiceWebview extends Component {
90 enable={enable} 92 enable={enable}
91 /> 93 />
92 )} 94 )}
93 {service.isEnabled && ( 95 <Webview
94 <div className="services__webview-wrapper"> 96 ref={(element) => { this.webview = element; }}
95 <Webview 97 autosize
96 ref={(element) => { this.webview = element; }} 98 src={service.url}
97 99 preload="./webview/plugin.js"
98 autosize 100 partition={`persist:service-${service.id}`}
99 src={service.url} 101 onDidAttach={() => setWebviewReference({
100 preload="./webview/plugin.js" 102 serviceId: service.id,
101 partition={`persist:service-${service.id}`} 103 webview: this.webview.view,
102 104 })}
103 onDidAttach={() => setWebviewReference({ 105 onUpdateTargetUrl={this.updateTargetUrl}
104 serviceId: service.id, 106 useragent={service.userAgent}
105 webview: this.webview.view, 107 muted={isAppMuted || service.isMuted}
106 })} 108 disablewebsecurity
107 109 allowpopups
108 onUpdateTargetUrl={this.updateTargetUrl} 110 />
109 111 {statusBar}
110 useragent={service.userAgent}
111
112 disablewebsecurity
113 allowpopups
114 />
115 {statusBar}
116 </div>
117 )}
118 </div> 112 </div>
119 ); 113 );
120 } 114 }
diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js
index 5230508f7..b1322afc2 100644
--- a/src/components/services/content/Services.js
+++ b/src/components/services/content/Services.js
@@ -26,6 +26,7 @@ export default class Services extends Component {
26 handleIPCMessage: PropTypes.func.isRequired, 26 handleIPCMessage: PropTypes.func.isRequired,
27 openWindow: PropTypes.func.isRequired, 27 openWindow: PropTypes.func.isRequired,
28 reload: PropTypes.func.isRequired, 28 reload: PropTypes.func.isRequired,
29 isAppMuted: PropTypes.bool.isRequired,
29 update: PropTypes.func.isRequired, 30 update: PropTypes.func.isRequired,
30 }; 31 };
31 32
@@ -45,6 +46,7 @@ export default class Services extends Component {
45 setWebviewReference, 46 setWebviewReference,
46 openWindow, 47 openWindow,
47 reload, 48 reload,
49 isAppMuted,
48 update, 50 update,
49 } = this.props; 51 } = this.props;
50 const { intl } = this.context; 52 const { intl } = this.context;
@@ -78,6 +80,7 @@ export default class Services extends Component {
78 setWebviewReference={setWebviewReference} 80 setWebviewReference={setWebviewReference}
79 openWindow={openWindow} 81 openWindow={openWindow}
80 reload={() => reload({ serviceId: service.id })} 82 reload={() => reload({ serviceId: service.id })}
83 isAppMuted={isAppMuted}
81 enable={() => update({ 84 enable={() => update({
82 serviceId: service.id, 85 serviceId: service.id,
83 serviceData: { 86 serviceData: {
diff --git a/src/components/services/tabs/TabBarSortableList.js b/src/components/services/tabs/TabBarSortableList.js
index 3340cbbbb..2daf55676 100644
--- a/src/components/services/tabs/TabBarSortableList.js
+++ b/src/components/services/tabs/TabBarSortableList.js
@@ -2,17 +2,8 @@ import React, { Component } from 'react';
2import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; 2import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
3import PropTypes from 'prop-types'; 3import PropTypes from 'prop-types';
4import { SortableContainer } from 'react-sortable-hoc'; 4import { SortableContainer } from 'react-sortable-hoc';
5import { defineMessages, intlShape } from 'react-intl';
6 5
7import TabItem from './TabItem'; 6import TabItem from './TabItem';
8import { ctrlKey } from '../../../environment';
9
10const messages = defineMessages({
11 addNewService: {
12 id: 'sidebar.addNewService',
13 defaultMessage: '!!!Add new service',
14 },
15});
16 7
17@observer 8@observer
18class TabBarSortableList extends Component { 9class TabBarSortableList extends Component {
@@ -22,29 +13,25 @@ class TabBarSortableList extends Component {
22 openSettings: PropTypes.func.isRequired, 13 openSettings: PropTypes.func.isRequired,
23 reload: PropTypes.func.isRequired, 14 reload: PropTypes.func.isRequired,
24 toggleNotifications: PropTypes.func.isRequired, 15 toggleNotifications: PropTypes.func.isRequired,
16 toggleAudio: PropTypes.func.isRequired,
25 deleteService: PropTypes.func.isRequired, 17 deleteService: PropTypes.func.isRequired,
26 disableService: PropTypes.func.isRequired, 18 disableService: PropTypes.func.isRequired,
27 enableService: PropTypes.func.isRequired, 19 enableService: PropTypes.func.isRequired,
28 } 20 }
29 21
30 static contextTypes = {
31 intl: intlShape,
32 };
33
34 render() { 22 render() {
35 const { 23 const {
36 services, 24 services,
37 setActive, 25 setActive,
38 reload, 26 reload,
39 toggleNotifications, 27 toggleNotifications,
28 toggleAudio,
40 deleteService, 29 deleteService,
41 disableService, 30 disableService,
42 enableService, 31 enableService,
43 openSettings, 32 openSettings,
44 } = this.props; 33 } = this.props;
45 34
46 const { intl } = this.context;
47
48 return ( 35 return (
49 <ul 36 <ul
50 className="tabs" 37 className="tabs"
@@ -58,13 +45,14 @@ class TabBarSortableList extends Component {
58 shortcutIndex={index + 1} 45 shortcutIndex={index + 1}
59 reload={() => reload({ serviceId: service.id })} 46 reload={() => reload({ serviceId: service.id })}
60 toggleNotifications={() => toggleNotifications({ serviceId: service.id })} 47 toggleNotifications={() => toggleNotifications({ serviceId: service.id })}
48 toggleAudio={() => toggleAudio({ serviceId: service.id })}
61 deleteService={() => deleteService({ serviceId: service.id })} 49 deleteService={() => deleteService({ serviceId: service.id })}
62 disableService={() => disableService({ serviceId: service.id })} 50 disableService={() => disableService({ serviceId: service.id })}
63 enableService={() => enableService({ serviceId: service.id })} 51 enableService={() => enableService({ serviceId: service.id })}
64 openSettings={openSettings} 52 openSettings={openSettings}
65 /> 53 />
66 ))} 54 ))}
67 <li> 55 {/* <li>
68 <button 56 <button
69 className="sidebar__add-service" 57 className="sidebar__add-service"
70 onClick={() => openSettings({ path: 'recipes' })} 58 onClick={() => openSettings({ path: 'recipes' })}
@@ -72,7 +60,7 @@ class TabBarSortableList extends Component {
72 > 60 >
73 <span className="mdi mdi-plus" /> 61 <span className="mdi mdi-plus" />
74 </button> 62 </button>
75 </li> 63 </li> */}
76 </ul> 64 </ul>
77 ); 65 );
78 } 66 }
diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js
index 638e17d95..a7136c43f 100644
--- a/src/components/services/tabs/TabItem.js
+++ b/src/components/services/tabs/TabItem.js
@@ -28,6 +28,14 @@ const messages = defineMessages({
28 id: 'tabs.item.enableNotification', 28 id: 'tabs.item.enableNotification',
29 defaultMessage: '!!!Enable notifications', 29 defaultMessage: '!!!Enable notifications',
30 }, 30 },
31 disableAudio: {
32 id: 'tabs.item.disableAudio',
33 defaultMessage: '!!!Disable audio',
34 },
35 enableAudio: {
36 id: 'tabs.item.enableAudio',
37 defaultMessage: '!!!Enable audio',
38 },
31 disableService: { 39 disableService: {
32 id: 'tabs.item.disableService', 40 id: 'tabs.item.disableService',
33 defaultMessage: '!!!Disable Service', 41 defaultMessage: '!!!Disable Service',
@@ -50,6 +58,7 @@ class TabItem extends Component {
50 shortcutIndex: PropTypes.number.isRequired, 58 shortcutIndex: PropTypes.number.isRequired,
51 reload: PropTypes.func.isRequired, 59 reload: PropTypes.func.isRequired,
52 toggleNotifications: PropTypes.func.isRequired, 60 toggleNotifications: PropTypes.func.isRequired,
61 toggleAudio: PropTypes.func.isRequired,
53 openSettings: PropTypes.func.isRequired, 62 openSettings: PropTypes.func.isRequired,
54 deleteService: PropTypes.func.isRequired, 63 deleteService: PropTypes.func.isRequired,
55 disableService: PropTypes.func.isRequired, 64 disableService: PropTypes.func.isRequired,
@@ -67,6 +76,7 @@ class TabItem extends Component {
67 shortcutIndex, 76 shortcutIndex,
68 reload, 77 reload,
69 toggleNotifications, 78 toggleNotifications,
79 toggleAudio,
70 deleteService, 80 deleteService,
71 disableService, 81 disableService,
72 enableService, 82 enableService,
@@ -96,6 +106,11 @@ class TabItem extends Component {
96 : intl.formatMessage(messages.enableNotifications), 106 : intl.formatMessage(messages.enableNotifications),
97 click: () => toggleNotifications(), 107 click: () => toggleNotifications(),
98 }, { 108 }, {
109 label: service.isMuted
110 ? intl.formatMessage(messages.enableAudio)
111 : intl.formatMessage(messages.disableAudio),
112 click: () => toggleAudio(),
113 }, {
99 label: intl.formatMessage(service.isEnabled ? messages.disableService : messages.enableService), 114 label: intl.formatMessage(service.isEnabled ? messages.disableService : messages.enableService),
100 click: () => (service.isEnabled ? disableService() : enableService()), 115 click: () => (service.isEnabled ? disableService() : enableService()),
101 }, { 116 }, {
diff --git a/src/components/services/tabs/Tabbar.js b/src/components/services/tabs/Tabbar.js
index 5f63aed16..fd4325107 100644
--- a/src/components/services/tabs/Tabbar.js
+++ b/src/components/services/tabs/Tabbar.js
@@ -15,6 +15,7 @@ export default class TabBar extends Component {
15 reorder: PropTypes.func.isRequired, 15 reorder: PropTypes.func.isRequired,
16 reload: PropTypes.func.isRequired, 16 reload: PropTypes.func.isRequired,
17 toggleNotifications: PropTypes.func.isRequired, 17 toggleNotifications: PropTypes.func.isRequired,
18 toggleAudio: PropTypes.func.isRequired,
18 deleteService: PropTypes.func.isRequired, 19 deleteService: PropTypes.func.isRequired,
19 updateService: PropTypes.func.isRequired, 20 updateService: PropTypes.func.isRequired,
20 } 21 }
@@ -59,6 +60,7 @@ export default class TabBar extends Component {
59 disableToolTip, 60 disableToolTip,
60 reload, 61 reload,
61 toggleNotifications, 62 toggleNotifications,
63 toggleAudio,
62 deleteService, 64 deleteService,
63 } = this.props; 65 } = this.props;
64 66
@@ -71,6 +73,7 @@ export default class TabBar extends Component {
71 onSortStart={disableToolTip} 73 onSortStart={disableToolTip}
72 reload={reload} 74 reload={reload}
73 toggleNotifications={toggleNotifications} 75 toggleNotifications={toggleNotifications}
76 toggleAudio={toggleAudio}
74 deleteService={deleteService} 77 deleteService={deleteService}
75 disableService={args => this.disableService(args)} 78 disableService={args => this.disableService(args)}
76 enableService={args => this.enableService(args)} 79 enableService={args => this.enableService(args)}
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js
index 9b359a78e..753781507 100644
--- a/src/components/settings/services/EditServiceForm.js
+++ b/src/components/settings/services/EditServiceForm.js
@@ -61,7 +61,11 @@ const messages = defineMessages({
61 }, 61 },
62 indirectMessageInfo: { 62 indirectMessageInfo: {
63 id: 'settings.service.form.indirectMessageInfo', 63 id: 'settings.service.form.indirectMessageInfo',
64 defaultMessage: '!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...', // eslint-disable-line 64 defaultMessage: '!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...',
65 },
66 isMutedInfo: {
67 id: 'settings.service.form.isMutedInfo',
68 defaultMessage: '!!!When disabled, all notification sounds and audio playback are muted',
65 }, 69 },
66}); 70});
67 71
@@ -231,11 +235,15 @@ export default class EditServiceForm extends Component {
231 {recipe.hasIndirectMessages && ( 235 {recipe.hasIndirectMessages && (
232 <div> 236 <div>
233 <Toggle field={form.$('isIndirectMessageBadgeEnabled')} /> 237 <Toggle field={form.$('isIndirectMessageBadgeEnabled')} />
234 <p className="settings__indirect-message-help"> 238 <p className="settings__help">
235 {intl.formatMessage(messages.indirectMessageInfo)} 239 {intl.formatMessage(messages.indirectMessageInfo)}
236 </p> 240 </p>
237 </div> 241 </div>
238 )} 242 )}
243 <Toggle field={form.$('isMuted')} />
244 <p className="settings__help">
245 {intl.formatMessage(messages.isMutedInfo)}
246 </p>
239 <Toggle field={form.$('isEnabled')} /> 247 <Toggle field={form.$('isEnabled')} />
240 </div> 248 </div>
241 {recipe.message && ( 249 {recipe.message && (
diff --git a/src/components/settings/services/ServiceItem.js b/src/components/settings/services/ServiceItem.js
index 20d8581d0..9743315b0 100644
--- a/src/components/settings/services/ServiceItem.js
+++ b/src/components/settings/services/ServiceItem.js
@@ -16,6 +16,10 @@ const messages = defineMessages({
16 id: 'settings.services.tooltip.notificationsDisabled', 16 id: 'settings.services.tooltip.notificationsDisabled',
17 defaultMessage: '!!!Notifications are disabled', 17 defaultMessage: '!!!Notifications are disabled',
18 }, 18 },
19 tooltipIsMuted: {
20 id: 'settings.services.tooltip.isMuted',
21 defaultMessage: '!!!All sounds are muted',
22 },
19}); 23});
20 24
21@observer 25@observer
@@ -66,6 +70,17 @@ export default class ServiceItem extends Component {
66 className="service-table__column-info" 70 className="service-table__column-info"
67 onClick={goToServiceForm} 71 onClick={goToServiceForm}
68 > 72 >
73 {service.isMuted && (
74 <span
75 className="mdi mdi-bell-off"
76 data-tip={intl.formatMessage(messages.tooltipIsMuted)}
77 />
78 )}
79 </td>
80 <td
81 className="service-table__column-info"
82 onClick={goToServiceForm}
83 >
69 {!service.isEnabled && ( 84 {!service.isEnabled && (
70 <span 85 <span
71 className="mdi mdi-power" 86 className="mdi mdi-power"
@@ -85,13 +100,6 @@ export default class ServiceItem extends Component {
85 )} 100 )}
86 <ReactTooltip place="top" type="dark" effect="solid" /> 101 <ReactTooltip place="top" type="dark" effect="solid" />
87 </td> 102 </td>
88 {/* <td className="service-table__column-action">
89 <input
90 type="checkbox"
91 onChange={toggleAction}
92 checked={service.isEnabled}
93 />
94 </td> */}
95 </tr> 103 </tr>
96 ); 104 );
97 } 105 }
diff --git a/src/config.js b/src/config.js
index 868c0cdf1..1627993f5 100644
--- a/src/config.js
+++ b/src/config.js
@@ -13,4 +13,5 @@ export const DEFAULT_APP_SETTINGS = {
13 showDisabledServices: true, 13 showDisabledServices: true,
14 locale: 'en-US', 14 locale: 'en-US',
15 beta: false, 15 beta: false,
16 isAppMuted: false,
16}; 17};
diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js
index cd3251082..8e5b3d2ed 100644
--- a/src/containers/layout/AppLayoutContainer.js
+++ b/src/containers/layout/AppLayoutContainer.js
@@ -7,7 +7,7 @@ import RecipesStore from '../../stores/RecipesStore';
7import ServicesStore from '../../stores/ServicesStore'; 7import ServicesStore from '../../stores/ServicesStore';
8import UIStore from '../../stores/UIStore'; 8import UIStore from '../../stores/UIStore';
9import NewsStore from '../../stores/NewsStore'; 9import NewsStore from '../../stores/NewsStore';
10import UserStore from '../../stores/UserStore'; 10import SettingsStore from '../../stores/SettingsStore';
11import RequestStore from '../../stores/RequestStore'; 11import RequestStore from '../../stores/RequestStore';
12import GlobalErrorStore from '../../stores/GlobalErrorStore'; 12import GlobalErrorStore from '../../stores/GlobalErrorStore';
13 13
@@ -29,8 +29,8 @@ export default class AppLayoutContainer extends Component {
29 services, 29 services,
30 ui, 30 ui,
31 news, 31 news,
32 settings,
32 globalError, 33 globalError,
33 user,
34 requests, 34 requests,
35 } = this.props.stores; 35 } = this.props.stores;
36 36
@@ -43,6 +43,7 @@ export default class AppLayoutContainer extends Component {
43 reorder, 43 reorder,
44 reload, 44 reload,
45 toggleNotifications, 45 toggleNotifications,
46 toggleAudio,
46 deleteService, 47 deleteService,
47 updateService, 48 updateService,
48 } = this.props.actions.service; 49 } = this.props.actions.service;
@@ -53,6 +54,7 @@ export default class AppLayoutContainer extends Component {
53 54
54 const { 55 const {
55 installUpdate, 56 installUpdate,
57 toggleMuteApp,
56 } = this.props.actions.app; 58 } = this.props.actions.app;
57 59
58 const { 60 const {
@@ -78,14 +80,16 @@ export default class AppLayoutContainer extends Component {
78 <Sidebar 80 <Sidebar
79 services={services.allDisplayed} 81 services={services.allDisplayed}
80 setActive={setActive} 82 setActive={setActive}
83 isAppMuted={Boolean(app.isSystemMuted) || Boolean(settings.all.isMuted)}
81 openSettings={openSettings} 84 openSettings={openSettings}
82 closeSettings={closeSettings} 85 closeSettings={closeSettings}
83 reorder={reorder} 86 reorder={reorder}
84 reload={reload} 87 reload={reload}
85 toggleNotifications={toggleNotifications} 88 toggleNotifications={toggleNotifications}
89 toggleAudio={toggleAudio}
86 deleteService={deleteService} 90 deleteService={deleteService}
87 updateService={updateService} 91 updateService={updateService}
88 isPremiumUser={user.data.isPremium} 92 toggleMuteApp={toggleMuteApp}
89 /> 93 />
90 ); 94 );
91 95
@@ -96,6 +100,7 @@ export default class AppLayoutContainer extends Component {
96 setWebviewReference={setWebviewReference} 100 setWebviewReference={setWebviewReference}
97 openWindow={openWindow} 101 openWindow={openWindow}
98 reload={reload} 102 reload={reload}
103 isAppMuted={settings.all.isMuted || false}
99 update={updateService} 104 update={updateService}
100 /> 105 />
101 ); 106 );
@@ -130,7 +135,7 @@ AppLayoutContainer.wrappedComponent.propTypes = {
130 app: PropTypes.instanceOf(AppStore).isRequired, 135 app: PropTypes.instanceOf(AppStore).isRequired,
131 ui: PropTypes.instanceOf(UIStore).isRequired, 136 ui: PropTypes.instanceOf(UIStore).isRequired,
132 news: PropTypes.instanceOf(NewsStore).isRequired, 137 news: PropTypes.instanceOf(NewsStore).isRequired,
133 user: PropTypes.instanceOf(UserStore).isRequired, 138 settings: PropTypes.instanceOf(SettingsStore).isRequired,
134 requests: PropTypes.instanceOf(RequestStore).isRequired, 139 requests: PropTypes.instanceOf(RequestStore).isRequired,
135 globalError: PropTypes.instanceOf(GlobalErrorStore).isRequired, 140 globalError: PropTypes.instanceOf(GlobalErrorStore).isRequired,
136 }).isRequired, 141 }).isRequired,
@@ -139,6 +144,7 @@ AppLayoutContainer.wrappedComponent.propTypes = {
139 setActive: PropTypes.func.isRequired, 144 setActive: PropTypes.func.isRequired,
140 reload: PropTypes.func.isRequired, 145 reload: PropTypes.func.isRequired,
141 toggleNotifications: PropTypes.func.isRequired, 146 toggleNotifications: PropTypes.func.isRequired,
147 toggleAudio: PropTypes.func.isRequired,
142 handleIPCMessage: PropTypes.func.isRequired, 148 handleIPCMessage: PropTypes.func.isRequired,
143 setWebviewReference: PropTypes.func.isRequired, 149 setWebviewReference: PropTypes.func.isRequired,
144 openWindow: PropTypes.func.isRequired, 150 openWindow: PropTypes.func.isRequired,
@@ -156,7 +162,7 @@ AppLayoutContainer.wrappedComponent.propTypes = {
156 }).isRequired, 162 }).isRequired,
157 app: PropTypes.shape({ 163 app: PropTypes.shape({
158 installUpdate: PropTypes.func.isRequired, 164 installUpdate: PropTypes.func.isRequired,
159 healthCheck: PropTypes.func.isRequired, 165 toggleMuteApp: PropTypes.func.isRequired,
160 }).isRequired, 166 }).isRequired,
161 requests: PropTypes.shape({ 167 requests: PropTypes.shape({
162 retryRequiredRequests: PropTypes.func.isRequired, 168 retryRequiredRequests: PropTypes.func.isRequired,
diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js
index 6c614b941..191ef447b 100644
--- a/src/containers/settings/EditServiceScreen.js
+++ b/src/containers/settings/EditServiceScreen.js
@@ -9,7 +9,6 @@ import ServicesStore from '../../stores/ServicesStore';
9import Form from '../../lib/Form'; 9import Form from '../../lib/Form';
10import { gaPage } from '../../lib/analytics'; 10import { gaPage } from '../../lib/analytics';
11 11
12
13import ServiceError from '../../components/settings/services/ServiceError'; 12import ServiceError from '../../components/settings/services/ServiceError';
14import EditServiceForm from '../../components/settings/services/EditServiceForm'; 13import EditServiceForm from '../../components/settings/services/EditServiceForm';
15import { required, url, oneRequired } from '../../helpers/validation-helpers'; 14import { required, url, oneRequired } from '../../helpers/validation-helpers';
@@ -27,6 +26,10 @@ const messages = defineMessages({
27 id: 'settings.service.form.enableNotification', 26 id: 'settings.service.form.enableNotification',
28 defaultMessage: '!!!Enable Notifications', 27 defaultMessage: '!!!Enable Notifications',
29 }, 28 },
29 enableAudio: {
30 id: 'settings.service.form.enableAudio',
31 defaultMessage: '!!!Enable audio',
32 },
30 team: { 33 team: {
31 id: 'settings.service.form.team', 34 id: 'settings.service.form.team',
32 defaultMessage: '!!!Team', 35 defaultMessage: '!!!Team',
@@ -51,11 +54,14 @@ export default class EditServiceScreen extends Component {
51 gaPage('Settings/Service/Edit'); 54 gaPage('Settings/Service/Edit');
52 } 55 }
53 56
54 onSubmit(serviceData) { 57 onSubmit(data) {
55 const { action } = this.props.router.params; 58 const { action } = this.props.router.params;
56 const { recipes, services } = this.props.stores; 59 const { recipes, services } = this.props.stores;
57 const { createService, updateService } = this.props.actions.service; 60 const { createService, updateService } = this.props.actions.service;
58 61
62 const serviceData = data;
63 serviceData.isMuted = !serviceData.isMuted;
64
59 if (action === 'edit') { 65 if (action === 'edit') {
60 updateService({ serviceId: services.activeSettings.id, serviceData }); 66 updateService({ serviceId: services.activeSettings.id, serviceData });
61 } else { 67 } else {
@@ -82,6 +88,11 @@ export default class EditServiceScreen extends Component {
82 value: service.isNotificationEnabled, 88 value: service.isNotificationEnabled,
83 default: true, 89 default: true,
84 }, 90 },
91 isMuted: {
92 label: intl.formatMessage(messages.enableAudio),
93 value: !service.isMuted,
94 default: true,
95 },
85 }, 96 },
86 }; 97 };
87 98
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 505e19f0f..fd8ba0fef 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -61,6 +61,8 @@
61 "infobar.requiredRequestsFailed": "Could not load services and user information", 61 "infobar.requiredRequestsFailed": "Could not load services and user information",
62 "sidebar.settings": "Settings", 62 "sidebar.settings": "Settings",
63 "sidebar.addNewService": "Add new service", 63 "sidebar.addNewService": "Add new service",
64 "sidebar.mute": "Disable audio",
65 "sidebar.unmute": "Enable audio",
64 "services.welcome": "Welcome to Franz", 66 "services.welcome": "Welcome to Franz",
65 "services.getStarted": "Get started", 67 "services.getStarted": "Get started",
66 "settings.account.headline": "Account", 68 "settings.account.headline": "Account",
@@ -106,11 +108,20 @@
106 "settings.service.form.customUrlPremiumInfo": "To add self hosted services, you need a Franz Premium Supporter Account.", 108 "settings.service.form.customUrlPremiumInfo": "To add self hosted services, you need a Franz Premium Supporter Account.",
107 "settings.service.form.customUrlUpgradeAccount": "Upgrade your account", 109 "settings.service.form.customUrlUpgradeAccount": "Upgrade your account",
108 "settings.service.form.indirectMessageInfo": "You will be notified about all new messages in a channel, not just @username, @channel, @here, ...", 110 "settings.service.form.indirectMessageInfo": "You will be notified about all new messages in a channel, not just @username, @channel, @here, ...",
111 "settings.service.form.name": "Name",
112 "settings.service.form.enableService": "Enable service",
113 "settings.service.form.enableNotification": "Enable notifications",
114 "settings.service.form.team": "Team",
115 "settings.service.form.customUrl": "Custom server",
116 "settings.service.form.indirectMessages": "Show message badge for all new messages",
117 "settings.service.form.enableAudio": "Enable audio",
118 "settings.service.form.isMutedInfo": "When disabled, all notification sounds and audio playback are muted",
109 "settings.service.error.headline": "Error", 119 "settings.service.error.headline": "Error",
110 "settings.service.error.goBack": "Back to services", 120 "settings.service.error.goBack": "Back to services",
111 "settings.service.error.message": "Could not load service recipe.", 121 "settings.service.error.message": "Could not load service recipe.",
112 "settings.services.tooltip.isDisabled": "Service is disabled", 122 "settings.services.tooltip.isDisabled": "Service is disabled",
113 "settings.services.tooltip.notificationsDisabled": "Notifications are disabled", 123 "settings.services.tooltip.notificationsDisabled": "Notifications are disabled",
124 "settings.services.tooltip.isMuted": "All sounds are muted",
114 "settings.services.headline": "Your services", 125 "settings.services.headline": "Your services",
115 "settings.services.noServicesAdded": "You haven't added any services yet.", 126 "settings.services.noServicesAdded": "You haven't added any services yet.",
116 "settings.services.discoverServices": "Discover services", 127 "settings.services.discoverServices": "Discover services",
@@ -135,12 +146,6 @@
135 "settings.app.form.showDisabledServices": "Display disabled services tabs", 146 "settings.app.form.showDisabledServices": "Display disabled services tabs",
136 "settings.app.form.beta": "Include beta versions", 147 "settings.app.form.beta": "Include beta versions",
137 "settings.app.currentVersion": "Current version:", 148 "settings.app.currentVersion": "Current version:",
138 "settings.service.form.name": "Name",
139 "settings.service.form.enableService": "Enable service",
140 "settings.service.form.enableNotification": "Enable notifications",
141 "settings.service.form.team": "Team",
142 "settings.service.form.customUrl": "Custom server",
143 "settings.service.form.indirectMessages": "Show message badge for all new messages",
144 "settings.user.form.firstname": "Firstname", 149 "settings.user.form.firstname": "Firstname",
145 "settings.user.form.lastname": "Lastname", 150 "settings.user.form.lastname": "Lastname",
146 "settings.user.form.email": "Email", 151 "settings.user.form.email": "Email",
@@ -166,6 +171,8 @@
166 "tabs.item.edit": "Edit", 171 "tabs.item.edit": "Edit",
167 "tabs.item.disableNotifications": "Disable notifications", 172 "tabs.item.disableNotifications": "Disable notifications",
168 "tabs.item.enableNotification": "Enable notifications", 173 "tabs.item.enableNotification": "Enable notifications",
174 "tabs.item.disableAudio": "Disable audio",
175 "tabs.item.enableAudio": "Enable audio",
169 "tabs.item.disableService": "Disable service", 176 "tabs.item.disableService": "Disable service",
170 "tabs.item.enableService": "Enable service", 177 "tabs.item.enableService": "Enable service",
171 "tabs.item.deleteService": "Delete service", 178 "tabs.item.deleteService": "Delete service",
diff --git a/src/models/Service.js b/src/models/Service.js
index dc53807f7..eb68493fe 100644
--- a/src/models/Service.js
+++ b/src/models/Service.js
@@ -18,6 +18,7 @@ export default class Service {
18 18
19 @observable order = 99; 19 @observable order = 99;
20 @observable isEnabled = true; 20 @observable isEnabled = true;
21 @observable isMuted = false;
21 @observable team = ''; 22 @observable team = '';
22 @observable customUrl = ''; 23 @observable customUrl = '';
23 @observable isNotificationEnabled = true; 24 @observable isNotificationEnabled = true;
@@ -54,6 +55,8 @@ export default class Service {
54 this.isIndirectMessageBadgeEnabled = data.isIndirectMessageBadgeEnabled !== undefined 55 this.isIndirectMessageBadgeEnabled = data.isIndirectMessageBadgeEnabled !== undefined
55 ? data.isIndirectMessageBadgeEnabled : this.isIndirectMessageBadgeEnabled; 56 ? data.isIndirectMessageBadgeEnabled : this.isIndirectMessageBadgeEnabled;
56 57
58 this.isMuted = data.isMuted !== undefined ? data.isMuted : this.isMuted;
59
57 this.recipe = recipe; 60 this.recipe = recipe;
58 } 61 }
59 62
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js
index e9faad911..14bdab094 100644
--- a/src/stores/AppStore.js
+++ b/src/stores/AppStore.js
@@ -2,7 +2,7 @@ import { remote, ipcRenderer, shell } from 'electron';
2import { action, observable } from 'mobx'; 2import { action, observable } from 'mobx';
3import moment from 'moment'; 3import moment from 'moment';
4import key from 'keymaster'; 4import key from 'keymaster';
5// import path from 'path'; 5import { getDoNotDisturb } from '@meetfranz/electron-notification-state';
6import idleTimer from '@paulcbetts/system-idle-time'; 6import idleTimer from '@paulcbetts/system-idle-time';
7import AutoLaunch from 'auto-launch'; 7import AutoLaunch from 'auto-launch';
8 8
@@ -45,6 +45,8 @@ export default class AppStore extends Store {
45 miner = null; 45 miner = null;
46 @observable minerHashrate = 0.0; 46 @observable minerHashrate = 0.0;
47 47
48 @observable isSystemMuted = false;
49
48 constructor(...args) { 50 constructor(...args) {
49 super(...args); 51 super(...args);
50 52
@@ -57,6 +59,8 @@ export default class AppStore extends Store {
57 this.actions.app.installUpdate.listen(this._installUpdate.bind(this)); 59 this.actions.app.installUpdate.listen(this._installUpdate.bind(this));
58 this.actions.app.resetUpdateStatus.listen(this._resetUpdateStatus.bind(this)); 60 this.actions.app.resetUpdateStatus.listen(this._resetUpdateStatus.bind(this));
59 this.actions.app.healthCheck.listen(this._healthCheck.bind(this)); 61 this.actions.app.healthCheck.listen(this._healthCheck.bind(this));
62 this.actions.app.muteApp.listen(this._muteApp.bind(this));
63 this.actions.app.toggleMuteApp.listen(this._toggleMuteApp.bind(this));
60 64
61 this.registerReactions([ 65 this.registerReactions([
62 this._offlineCheck.bind(this), 66 this._offlineCheck.bind(this),
@@ -81,6 +85,11 @@ export default class AppStore extends Store {
81 // Needs to be delayed a bit 85 // Needs to be delayed a bit
82 this._autoStart(); 86 this._autoStart();
83 87
88 // Check if system is muted
89 // There are no events to subscribe so we need to poll everey 5s
90 this._systemDND();
91 setInterval(() => this._systemDND(), 5000);
92
84 // Check for updates once every 4 hours 93 // Check for updates once every 4 hours
85 setInterval(() => this._checkForUpdates(), CHECK_INTERVAL); 94 setInterval(() => this._checkForUpdates(), CHECK_INTERVAL);
86 // Check for an update in 30s (need a delay to prevent Squirrel Installer lock file issues) 95 // Check for an update in 30s (need a delay to prevent Squirrel Installer lock file issues)
@@ -202,6 +211,18 @@ export default class AppStore extends Store {
202 this.healthCheckRequest.execute(); 211 this.healthCheckRequest.execute();
203 } 212 }
204 213
214 @action _muteApp({ isMuted }) {
215 this.actions.settings.update({
216 settings: {
217 isMuted,
218 },
219 });
220 }
221
222 @action _toggleMuteApp() {
223 this._muteApp({ isMuted: !this.stores.settings.all.isMuted });
224 }
225
205 // Reactions 226 // Reactions
206 _offlineCheck() { 227 _offlineCheck() {
207 if (!this.isOnline) { 228 if (!this.isOnline) {
@@ -297,4 +318,8 @@ export default class AppStore extends Store {
297 async _checkAutoStart() { 318 async _checkAutoStart() {
298 return autoLauncher.isEnabled() || false; 319 return autoLauncher.isEnabled() || false;
299 } 320 }
321
322 _systemDND() {
323 this.isSystemMuted = getDoNotDisturb();
324 }
300} 325}
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index 6c41c22cf..269cd1526 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -48,6 +48,7 @@ export default class ServicesStore extends Store {
48 this.actions.service.reloadUpdatedServices.listen(this._reloadUpdatedServices.bind(this)); 48 this.actions.service.reloadUpdatedServices.listen(this._reloadUpdatedServices.bind(this));
49 this.actions.service.reorder.listen(this._reorder.bind(this)); 49 this.actions.service.reorder.listen(this._reorder.bind(this));
50 this.actions.service.toggleNotifications.listen(this._toggleNotifications.bind(this)); 50 this.actions.service.toggleNotifications.listen(this._toggleNotifications.bind(this));
51 this.actions.service.toggleAudio.listen(this._toggleAudio.bind(this));
51 this.actions.service.openDevTools.listen(this._openDevTools.bind(this)); 52 this.actions.service.openDevTools.listen(this._openDevTools.bind(this));
52 this.actions.service.openDevToolsForActiveService.listen(this._openDevToolsForActiveService.bind(this)); 53 this.actions.service.openDevToolsForActiveService.listen(this._openDevToolsForActiveService.bind(this));
53 54
@@ -293,7 +294,7 @@ export default class ServicesStore extends Store {
293 }); 294 });
294 } else if (channel === 'notification') { 295 } else if (channel === 'notification') {
295 const options = args[0].options; 296 const options = args[0].options;
296 if (service.recipe.hasNotificationSound) { 297 if (service.recipe.hasNotificationSound || service.isMuted) {
297 Object.assign(options, { 298 Object.assign(options, {
298 silent: true, 299 silent: true,
299 }); 300 });
@@ -405,11 +406,25 @@ export default class ServicesStore extends Store {
405 @action _toggleNotifications({ serviceId }) { 406 @action _toggleNotifications({ serviceId }) {
406 const service = this.one(serviceId); 407 const service = this.one(serviceId);
407 408
409 this.actions.service.updateService({
410 serviceId,
411 serviceData: {
412 isNotificationEnabled: !service.isNotificationEnabled,
413 },
414 redirect: false,
415 });
416 }
417
418 @action _toggleAudio({ serviceId }) {
419 const service = this.one(serviceId);
420
408 service.isNotificationEnabled = !service.isNotificationEnabled; 421 service.isNotificationEnabled = !service.isNotificationEnabled;
409 422
410 this.actions.service.updateService({ 423 this.actions.service.updateService({
411 serviceId, 424 serviceId,
412 serviceData: service, 425 serviceData: {
426 isMuted: !service.isMuted,
427 },
413 redirect: false, 428 redirect: false,
414 }); 429 });
415 } 430 }
diff --git a/src/styles/layout.scss b/src/styles/layout.scss
index d87df2684..afdd7dec7 100644
--- a/src/styles/layout.scss
+++ b/src/styles/layout.scss
@@ -42,6 +42,7 @@ html {
42 z-index: 200; 42 z-index: 200;
43 text-align: center; 43 text-align: center;
44 color: $theme-text-color; 44 color: $theme-text-color;
45 padding-bottom: 10px;
45 46
46 .sidebar__add-service { 47 .sidebar__add-service {
47 width: 32px; 48 width: 32px;
@@ -52,26 +53,28 @@ html {
52 color: $theme-gray-light; 53 color: $theme-gray-light;
53 } 54 }
54 55
55 .sidebar__settings-button { 56 .sidebar__button {
56 height: auto; 57 width: $theme-sidebar-width;
57 padding: 20px 0; 58 padding: 7px 0;
58 font-size: 12px; 59 font-size: 24px;
59 position: relative; 60 position: relative;
61 color: $theme-gray-light;
60 62
61 .emoji { 63 &:hover {
62 position: absolute; 64 color: darken($theme-gray-light, 10%);
63 top: 18px; 65 }
64 right: 12px;
65 66
66 img { 67 &:active {
67 width: 18px; 68 color: lighten($theme-gray-light, 10%);
68 }
69 } 69 }
70 }
71 70
72 .sidebar__logo { 71 &.is-muted {
73 width: 40px; 72 color: $theme-brand-primary;
74 height: auto; 73 }
74
75 &--new-service {
76 padding-bottom: 6px;
77 }
75 } 78 }
76 79
77 & > div { 80 & > div {
diff --git a/src/styles/settings.scss b/src/styles/settings.scss
index 9b19deb4e..48d12a807 100644
--- a/src/styles/settings.scss
+++ b/src/styles/settings.scss
@@ -169,7 +169,7 @@
169 } 169 }
170 } 170 }
171 171
172 .settings__indirect-message-help { 172 .settings__help {
173 margin: -10px 0 20px 55px;; 173 margin: -10px 0 20px 55px;;
174 font-size: 12px; 174 font-size: 12px;
175 color: $theme-gray-light; 175 color: $theme-gray-light;
diff --git a/src/styles/tabs.scss b/src/styles/tabs.scss
index 3e5904d2c..8347de3b4 100644
--- a/src/styles/tabs.scss
+++ b/src/styles/tabs.scss
@@ -20,7 +20,7 @@
20 align-items: center; 20 align-items: center;
21 position: relative; 21 position: relative;
22 width: $theme-sidebar-width; 22 width: $theme-sidebar-width;
23 height: $theme-sidebar-width; 23 height: 65px;
24 min-height: 50px; 24 min-height: 50px;
25 transition: background $theme-transition-time; 25 transition: background $theme-transition-time;
26 26
@@ -47,6 +47,12 @@
47 } 47 }
48 } 48 }
49 49
50 &:active {
51 .tab-item__icon {
52 opacity: 0.7;
53 }
54 }
55
50 .tab-item__icon { 56 .tab-item__icon {
51 width: 30px; 57 width: 30px;
52 height: auto; 58 height: auto;
diff --git a/yarn.lock b/yarn.lock
index 0bedbac27..6c3f807a4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -22,6 +22,14 @@
22 "7zip-bin-mac" "^1.0.1" 22 "7zip-bin-mac" "^1.0.1"
23 "7zip-bin-win" "^2.1.0" 23 "7zip-bin-win" "^2.1.0"
24 24
25"@meetfranz/electron-notification-state@^1.0.0":
26 version "1.0.0"
27 resolved "https://registry.yarnpkg.com/@meetfranz/electron-notification-state/-/electron-notification-state-1.0.0.tgz#75e9d774bdaf15991eacd92cde8469b348259d8c"
28 dependencies:
29 macos-notification-state "^1.1.0"
30 windows-notification-state "^1.3.0"
31 windows-quiet-hours "^1.2.2"
32
25"@paulcbetts/cld@^2.4.6": 33"@paulcbetts/cld@^2.4.6":
26 version "2.4.6" 34 version "2.4.6"
27 resolved "https://registry.yarnpkg.com/@paulcbetts/cld/-/cld-2.4.6.tgz#a992f6bc43cab212ac2c4488a671cf302f8b62e7" 35 resolved "https://registry.yarnpkg.com/@paulcbetts/cld/-/cld-2.4.6.tgz#a992f6bc43cab212ac2c4488a671cf302f8b62e7"
@@ -1168,6 +1176,10 @@ binary@^0.3.0:
1168 buffers "~0.1.1" 1176 buffers "~0.1.1"
1169 chainsaw "~0.1.0" 1177 chainsaw "~0.1.0"
1170 1178
1179bindings@^1.2.1, bindings@^1.3.0:
1180 version "1.3.0"
1181 resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7"
1182
1171bindings@~1.2.1: 1183bindings@~1.2.1:
1172 version "1.2.1" 1184 version "1.2.1"
1173 resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" 1185 resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11"
@@ -4012,6 +4024,13 @@ macaddress@^0.2.7:
4012 version "0.2.8" 4024 version "0.2.8"
4013 resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" 4025 resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"
4014 4026
4027macos-notification-state@^1.1.0:
4028 version "1.1.0"
4029 resolved "https://registry.yarnpkg.com/macos-notification-state/-/macos-notification-state-1.1.0.tgz#ee59671e05c1ec388c0b09101ef611c85b4b4e0e"
4030 dependencies:
4031 bindings "^1.2.1"
4032 nan "^2.4.0"
4033
4015make-dir@^1.0.0: 4034make-dir@^1.0.0:
4016 version "1.0.0" 4035 version "1.0.0"
4017 resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.0.0.tgz#97a011751e91dd87cfadef58832ebb04936de978" 4036 resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.0.0.tgz#97a011751e91dd87cfadef58832ebb04936de978"
@@ -4243,7 +4262,7 @@ mute-stream@0.0.7, mute-stream@~0.0.4:
4243 version "0.0.7" 4262 version "0.0.7"
4244 resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" 4263 resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
4245 4264
4246nan@^2.0.0, nan@^2.0.5, nan@^2.3.0, nan@^2.3.2: 4265nan@^2.0.0, nan@^2.0.5, nan@^2.3.0, nan@^2.3.2, nan@^2.4.0, nan@^2.7.0:
4247 version "2.7.0" 4266 version "2.7.0"
4248 resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46" 4267 resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"
4249 4268
@@ -6155,6 +6174,20 @@ window-size@^0.1.4:
6155 version "0.1.4" 6174 version "0.1.4"
6156 resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" 6175 resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876"
6157 6176
6177windows-notification-state@^1.3.0:
6178 version "1.3.0"
6179 resolved "https://registry.yarnpkg.com/windows-notification-state/-/windows-notification-state-1.3.0.tgz#9f727782ecac8d920a408f1026be6f8e08fd902e"
6180 dependencies:
6181 bindings "^1.2.1"
6182 nan "^2.4.0"
6183
6184windows-quiet-hours@^1.2.2:
6185 version "1.2.4"
6186 resolved "https://registry.yarnpkg.com/windows-quiet-hours/-/windows-quiet-hours-1.2.4.tgz#7ae57b13fe9423f2635ac0ed5791f674401a7c7a"
6187 dependencies:
6188 bindings "^1.3.0"
6189 nan "^2.7.0"
6190
6158winreg@1.2.2: 6191winreg@1.2.2:
6159 version "1.2.2" 6192 version "1.2.2"
6160 resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.2.tgz#8509afa3b71c5bbd110a6d7c6247ec67736c598f" 6193 resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.2.tgz#8509afa3b71c5bbd110a6d7c6247ec67736c598f"