From f5a9aa21e2ab958f60c143668f4836bc47e2b539 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Fri, 10 Nov 2017 12:08:35 +0100 Subject: feat(App): Add option to mute all services in sidebar Closes #8 #162 --- src/actions/app.js | 4 ++ src/actions/service.js | 3 ++ src/components/layout/Sidebar.js | 52 ++++++++++++++-------- src/components/services/content/ServiceWebview.js | 4 +- src/components/services/content/Services.js | 3 ++ src/components/services/tabs/TabBarSortableList.js | 22 +++------ src/components/services/tabs/TabItem.js | 15 +++++++ src/components/services/tabs/Tabbar.js | 3 ++ src/config.js | 1 + src/containers/layout/AppLayoutContainer.js | 16 ++++--- src/i18n/locales/en-US.json | 4 ++ src/stores/AppStore.js | 14 ++++++ src/stores/ServicesStore.js | 17 ++++++- src/styles/layout.scss | 32 ++++++------- src/styles/tabs.scss | 7 +++ 15 files changed, 139 insertions(+), 58 deletions(-) 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 { resetUpdateStatus: {}, installUpdate: {}, healthCheck: {}, + muteApp: { + isMuted: PropTypes.bool.isRequired, + }, + toggleMuteApp: {}, }; 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 { toggleNotifications: { serviceId: PropTypes.string.isRequired, }, + toggleAudio: { + serviceId: PropTypes.string.isRequired, + }, openDevTools: { serviceId: PropTypes.string.isRequired, }, diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js index 6a5c0f365..72ee2b3b7 100644 --- a/src/components/layout/Sidebar.js +++ b/src/components/layout/Sidebar.js @@ -11,16 +11,25 @@ const messages = defineMessages({ id: 'sidebar.settings', defaultMessage: '!!!Settings', }, + addNewService: { + id: 'sidebar.addNewService', + defaultMessage: '!!!Add new service', + }, + mute: { + id: 'sidebar.mute', + defaultMessage: '!!!Disable audio', + }, + unmute: { + id: 'sidebar.unmute', + defaultMessage: '!!!Enable audio', + }, }); export default class Sidebar extends Component { static propTypes = { openSettings: PropTypes.func.isRequired, - isPremiumUser: PropTypes.bool, - } - - static defaultProps = { - isPremiumUser: false, + toggleMuteApp: PropTypes.func.isRequired, + isAppMuted: PropTypes.bool.isRequired, } static contextTypes = { @@ -40,8 +49,9 @@ export default class Sidebar extends Component { } render() { - const { openSettings, isPremiumUser } = this.props; + const { openSettings, toggleMuteApp, isAppMuted } = this.props; const { intl } = this.context; + return (
this.disableToolTip()} /> + + {this.state.tooltipEnabled && ( diff --git a/src/components/services/content/ServiceWebview.js b/src/components/services/content/ServiceWebview.js index d7e0a4f38..60bdf7e47 100644 --- a/src/components/services/content/ServiceWebview.js +++ b/src/components/services/content/ServiceWebview.js @@ -15,6 +15,7 @@ export default class ServiceWebview extends Component { service: PropTypes.instanceOf(ServiceModel).isRequired, setWebviewReference: PropTypes.func.isRequired, reload: PropTypes.func.isRequired, + isAppMuted: PropTypes.bool.isRequired, }; static defaultProps = { @@ -56,6 +57,7 @@ export default class ServiceWebview extends Component { service, setWebviewReference, reload, + isAppMuted, } = this.props; const webviewClasses = classnames({ @@ -92,7 +94,7 @@ export default class ServiceWebview extends Component { })} onUpdateTargetUrl={this.updateTargetUrl} useragent={service.userAgent} - muted={service.isMuted} + muted={isAppMuted || service.isMuted} disablewebsecurity allowpopups /> diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js index bad525d22..55a47cdd3 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 { handleIPCMessage: PropTypes.func.isRequired, openWindow: PropTypes.func.isRequired, reload: PropTypes.func.isRequired, + isAppMuted: PropTypes.bool.isRequired, }; static defaultProps = { @@ -44,6 +45,7 @@ export default class Services extends Component { setWebviewReference, openWindow, reload, + isAppMuted, } = this.props; const { intl } = this.context; @@ -76,6 +78,7 @@ export default class Services extends Component { setWebviewReference={setWebviewReference} openWindow={openWindow} reload={() => reload({ serviceId: service.id })} + isAppMuted={isAppMuted} /> ))}
diff --git a/src/components/services/tabs/TabBarSortableList.js b/src/components/services/tabs/TabBarSortableList.js index e5ae36419..0146f5b35 100644 --- a/src/components/services/tabs/TabBarSortableList.js +++ b/src/components/services/tabs/TabBarSortableList.js @@ -2,17 +2,8 @@ import React, { Component } from 'react'; import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; import PropTypes from 'prop-types'; import { SortableContainer } from 'react-sortable-hoc'; -import { defineMessages, intlShape } from 'react-intl'; import TabItem from './TabItem'; -import { ctrlKey } from '../../../environment'; - -const messages = defineMessages({ - addNewService: { - id: 'sidebar.addNewService', - defaultMessage: '!!!Add new service', - }, -}); @observer class TabBarSortableList extends Component { @@ -22,27 +13,23 @@ class TabBarSortableList extends Component { openSettings: PropTypes.func.isRequired, reload: PropTypes.func.isRequired, toggleNotifications: PropTypes.func.isRequired, + toggleAudio: PropTypes.func.isRequired, deleteService: PropTypes.func.isRequired, disableService: PropTypes.func.isRequired, } - static contextTypes = { - intl: intlShape, - }; - render() { const { services, setActive, reload, toggleNotifications, + toggleAudio, deleteService, disableService, openSettings, } = this.props; - const { intl } = this.context; - return (
    reload({ serviceId: service.id })} toggleNotifications={() => toggleNotifications({ serviceId: service.id })} + toggleAudio={() => toggleAudio({ serviceId: service.id })} deleteService={() => deleteService({ serviceId: service.id })} disableService={() => disableService({ serviceId: service.id })} openSettings={openSettings} /> ))} -
  • + {/*
  • -
  • + */}
); } diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js index 9e03d2e21..7b001f6ee 100644 --- a/src/components/services/tabs/TabItem.js +++ b/src/components/services/tabs/TabItem.js @@ -28,6 +28,14 @@ const messages = defineMessages({ id: 'tabs.item.enableNotification', defaultMessage: '!!!Enable notifications', }, + disableAudio: { + id: 'tabs.item.disableAudio', + defaultMessage: '!!!Disable audio', + }, + enableAudio: { + id: 'tabs.item.enableAudio', + defaultMessage: '!!!Enable audio', + }, disableService: { id: 'tabs.item.disableService', defaultMessage: '!!!Disable Service', @@ -46,6 +54,7 @@ class TabItem extends Component { shortcutIndex: PropTypes.number.isRequired, reload: PropTypes.func.isRequired, toggleNotifications: PropTypes.func.isRequired, + toggleAudio: PropTypes.func.isRequired, openSettings: PropTypes.func.isRequired, deleteService: PropTypes.func.isRequired, disableService: PropTypes.func.isRequired, @@ -62,6 +71,7 @@ class TabItem extends Component { shortcutIndex, reload, toggleNotifications, + toggleAudio, deleteService, disableService, openSettings, @@ -89,6 +99,11 @@ class TabItem extends Component { ? intl.formatMessage(messages.disableNotifications) : intl.formatMessage(messages.enableNotifications), click: () => toggleNotifications(), + }, { + label: service.isMuted + ? intl.formatMessage(messages.enableAudio) + : intl.formatMessage(messages.disableAudio), + click: () => toggleAudio(), }, { label: intl.formatMessage(messages.disableService), click: () => disableService(), diff --git a/src/components/services/tabs/Tabbar.js b/src/components/services/tabs/Tabbar.js index fdb2c0a59..e8cd80e33 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 { reorder: PropTypes.func.isRequired, reload: PropTypes.func.isRequired, toggleNotifications: PropTypes.func.isRequired, + toggleAudio: PropTypes.func.isRequired, deleteService: PropTypes.func.isRequired, updateService: PropTypes.func.isRequired, } @@ -51,6 +52,7 @@ export default class TabBar extends Component { disableToolTip, reload, toggleNotifications, + toggleAudio, deleteService, } = this.props; @@ -63,6 +65,7 @@ export default class TabBar extends Component { onSortStart={disableToolTip} reload={reload} toggleNotifications={toggleNotifications} + toggleAudio={toggleAudio} deleteService={deleteService} disableService={this.disableService} openSettings={openSettings} diff --git a/src/config.js b/src/config.js index 0a4856ece..651f2e174 100644 --- a/src/config.js +++ b/src/config.js @@ -12,4 +12,5 @@ export const DEFAULT_APP_SETTINGS = { minimizeToSystemTray: false, locale: 'en-us', // TODO: Replace with proper solution once translations are in beta: false, + isAppMuted: false, }; diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js index 68ad1039e..5ef4fbb35 100644 --- a/src/containers/layout/AppLayoutContainer.js +++ b/src/containers/layout/AppLayoutContainer.js @@ -7,7 +7,7 @@ import RecipesStore from '../../stores/RecipesStore'; import ServicesStore from '../../stores/ServicesStore'; import UIStore from '../../stores/UIStore'; import NewsStore from '../../stores/NewsStore'; -import UserStore from '../../stores/UserStore'; +import SettingsStore from '../../stores/SettingsStore'; import RequestStore from '../../stores/RequestStore'; import GlobalErrorStore from '../../stores/GlobalErrorStore'; @@ -29,8 +29,8 @@ export default class AppLayoutContainer extends Component { services, ui, news, + settings, globalError, - user, requests, } = this.props.stores; @@ -43,6 +43,7 @@ export default class AppLayoutContainer extends Component { reorder, reload, toggleNotifications, + toggleAudio, deleteService, updateService, } = this.props.actions.service; @@ -53,6 +54,7 @@ export default class AppLayoutContainer extends Component { const { installUpdate, + toggleMuteApp, } = this.props.actions.app; const { @@ -79,14 +81,16 @@ export default class AppLayoutContainer extends Component { ); @@ -97,6 +101,7 @@ export default class AppLayoutContainer extends Component { setWebviewReference={setWebviewReference} openWindow={openWindow} reload={reload} + isAppMuted={settings.all.isMuted} /> ); @@ -130,7 +135,7 @@ AppLayoutContainer.wrappedComponent.propTypes = { app: PropTypes.instanceOf(AppStore).isRequired, ui: PropTypes.instanceOf(UIStore).isRequired, news: PropTypes.instanceOf(NewsStore).isRequired, - user: PropTypes.instanceOf(UserStore).isRequired, + settings: PropTypes.instanceOf(SettingsStore).isRequired, requests: PropTypes.instanceOf(RequestStore).isRequired, globalError: PropTypes.instanceOf(GlobalErrorStore).isRequired, }).isRequired, @@ -139,6 +144,7 @@ AppLayoutContainer.wrappedComponent.propTypes = { setActive: PropTypes.func.isRequired, reload: PropTypes.func.isRequired, toggleNotifications: PropTypes.func.isRequired, + toggleAudio: PropTypes.func.isRequired, handleIPCMessage: PropTypes.func.isRequired, setWebviewReference: PropTypes.func.isRequired, openWindow: PropTypes.func.isRequired, @@ -156,7 +162,7 @@ AppLayoutContainer.wrappedComponent.propTypes = { }).isRequired, app: PropTypes.shape({ installUpdate: PropTypes.func.isRequired, - healthCheck: PropTypes.func.isRequired, + toggleMuteApp: PropTypes.func.isRequired, }).isRequired, requests: PropTypes.shape({ retryRequiredRequests: PropTypes.func.isRequired, diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index e298728d1..aa66d4bd0 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -61,6 +61,8 @@ "infobar.requiredRequestsFailed": "Could not load services and user information", "sidebar.settings": "Settings", "sidebar.addNewService": "Add new service", + "sidebar.mute": "Disable audio", + "sidebar.unmute": "Enable audio", "services.welcome": "Welcome to Franz", "services.getStarted": "Get started", "settings.account.headline": "Account", @@ -167,6 +169,8 @@ "tabs.item.edit": "Edit", "tabs.item.disableNotifications": "Disable notifications", "tabs.item.enableNotification": "Enable notifications", + "tabs.item.disableAudio": "Disable audio", + "tabs.item.enableAudio": "Enable audio", "tabs.item.disableService": "Disable service", "tabs.item.deleteService": "Delete service", "service.crashHandler.headline": "Oh no!", diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js index ecfd621d3..6580157d4 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js @@ -57,6 +57,8 @@ export default class AppStore extends Store { this.actions.app.installUpdate.listen(this._installUpdate.bind(this)); this.actions.app.resetUpdateStatus.listen(this._resetUpdateStatus.bind(this)); this.actions.app.healthCheck.listen(this._healthCheck.bind(this)); + this.actions.app.muteApp.listen(this._muteApp.bind(this)); + this.actions.app.toggleMuteApp.listen(this._toggleMuteApp.bind(this)); this.registerReactions([ this._offlineCheck.bind(this), @@ -202,6 +204,18 @@ export default class AppStore extends Store { this.healthCheckRequest.execute(); } + @action _muteApp({ isMuted }) { + this.actions.settings.update({ + settings: { + isMuted, + }, + }); + } + + @action _toggleMuteApp() { + this._muteApp({ isMuted: !this.stores.settings.all.isMuted }); + } + // Reactions _offlineCheck() { if (!this.isOnline) { diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 96c503510..a20718eca 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js @@ -48,6 +48,7 @@ export default class ServicesStore extends Store { this.actions.service.reloadUpdatedServices.listen(this._reloadUpdatedServices.bind(this)); this.actions.service.reorder.listen(this._reorder.bind(this)); this.actions.service.toggleNotifications.listen(this._toggleNotifications.bind(this)); + this.actions.service.toggleAudio.listen(this._toggleAudio.bind(this)); this.actions.service.openDevTools.listen(this._openDevTools.bind(this)); this.actions.service.openDevToolsForActiveService.listen(this._openDevToolsForActiveService.bind(this)); @@ -399,11 +400,25 @@ export default class ServicesStore extends Store { @action _toggleNotifications({ serviceId }) { const service = this.one(serviceId); + this.actions.service.updateService({ + serviceId, + serviceData: { + isNotificationEnabled: !service.isNotificationEnabled, + }, + redirect: false, + }); + } + + @action _toggleAudio({ serviceId }) { + const service = this.one(serviceId); + service.isNotificationEnabled = !service.isNotificationEnabled; this.actions.service.updateService({ serviceId, - serviceData: service, + serviceData: { + isMuted: !service.isMuted, + }, redirect: false, }); } diff --git a/src/styles/layout.scss b/src/styles/layout.scss index d87df2684..9f32bf2c4 100644 --- a/src/styles/layout.scss +++ b/src/styles/layout.scss @@ -42,6 +42,7 @@ html { z-index: 200; text-align: center; color: $theme-text-color; + padding-bottom: 5px; .sidebar__add-service { width: 32px; @@ -52,26 +53,27 @@ html { color: $theme-gray-light; } - .sidebar__settings-button { - height: auto; - padding: 20px 0; - font-size: 12px; + .sidebar__button { + width: $theme-sidebar-width; + padding: 10px 0; + font-size: 24px; position: relative; + color: $theme-gray-light; + transition: color 0.25s, transform 0.25s; - .emoji { - position: absolute; - top: 18px; - right: 12px; + &:hover { + transform: scale(1.15); + color: darken($theme-gray-light, 10%); + } - img { - width: 18px; - } + &:active { + transition: transform 0.1s; + transform: scale(1); } - } - .sidebar__logo { - width: 40px; - height: auto; + &.is-muted { + color: $theme-brand-primary; + } } & > div { diff --git a/src/styles/tabs.scss b/src/styles/tabs.scss index 75568898b..abafdb53c 100644 --- a/src/styles/tabs.scss +++ b/src/styles/tabs.scss @@ -41,9 +41,16 @@ } } + &:hover { + .tab-item__icon { + transform: scale(1.1); + } + } + .tab-item__icon { width: 30px; height: auto; + transition: transform 0.25s; } .tab-item__message-count { -- cgit v1.2.3-54-g00ecf