From dd307e3deb14f1738029ea38f7c1de7893455283 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Tue, 7 Nov 2017 15:29:31 +0100 Subject: feature(Service): Add webview crash handler to display a user friendly message --- src/i18n/locales/en-US.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/i18n/locales') diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index b1d260f0a..0493b547f 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -165,5 +165,9 @@ "tabs.item.disableNotifications": "Disable notifications", "tabs.item.enableNotification": "Enable notifications", "tabs.item.disableService": "Disable service", - "tabs.item.deleteService": "Delete service" + "tabs.item.deleteService": "Delete service", + "service.crashHandler.headline": "Oh no!", + "service.crashHandler.text": "{name} has caused an error.", + "service.crashHandler.action": "Reload {name}", + "service.crashHandler.autoReload": "Trying to automatically restore {name} in {seconds} seconds" } -- cgit v1.2.3-70-g09d2 From 1839eff4fcad186871672499b6c3cc68e9539ce2 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Thu, 9 Nov 2017 12:11:16 +0100 Subject: feat(Service): Add option to display disabled services in tabs --- src/components/services/content/ServiceDisabled.js | 48 +++++++++++++++++++ src/components/services/content/ServiceWebview.js | 56 ++++++++++++++-------- src/components/services/content/Services.js | 9 ++++ .../services/content/WebviewCrashHandler.js | 2 +- src/components/services/tabs/TabBarSortableList.js | 3 ++ src/components/services/tabs/TabItem.js | 11 ++++- src/components/services/tabs/Tabbar.js | 15 ++++-- .../settings/settings/EditSettingsForm.js | 6 +++ src/config.js | 3 +- src/containers/layout/AppLayoutContainer.js | 6 +-- src/containers/settings/EditSettingsScreen.js | 10 ++++ src/i18n/locales/en-US.json | 7 ++- src/stores/ServicesStore.js | 34 +++++++------ src/stores/SettingsStore.js | 3 +- src/styles/services.scss | 4 ++ src/styles/tabs.scss | 6 +++ 16 files changed, 172 insertions(+), 51 deletions(-) create mode 100644 src/components/services/content/ServiceDisabled.js (limited to 'src/i18n/locales') diff --git a/src/components/services/content/ServiceDisabled.js b/src/components/services/content/ServiceDisabled.js new file mode 100644 index 000000000..732b6c003 --- /dev/null +++ b/src/components/services/content/ServiceDisabled.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { observer } from 'mobx-react'; +import { defineMessages, intlShape } from 'react-intl'; + +import Button from '../../ui/Button'; + +const messages = defineMessages({ + headline: { + id: 'service.disabledHandler.headline', + defaultMessage: '!!!{name} is disabled', + }, + action: { + id: 'service.disabledHandler.action', + defaultMessage: '!!!Enable {name}', + }, +}); + +@observer +export default class ServiceDisabled extends Component { + static propTypes = { + name: PropTypes.string.isRequired, + enable: PropTypes.func.isRequired, + }; + + static contextTypes = { + intl: intlShape, + }; + + countdownInterval = null; + countdownIntervalTimeout = 1000; + + render() { + const { name, enable } = this.props; + const { intl } = this.context; + + return ( +
+

{intl.formatMessage(messages.headline, { name })}

+
+ ); + } +} diff --git a/src/components/services/content/ServiceWebview.js b/src/components/services/content/ServiceWebview.js index cd59e0a8a..a71017a6e 100644 --- a/src/components/services/content/ServiceWebview.js +++ b/src/components/services/content/ServiceWebview.js @@ -8,6 +8,7 @@ import classnames from 'classnames'; import ServiceModel from '../../../models/Service'; import StatusBarTargetUrl from '../../ui/StatusBarTargetUrl'; import WebviewCrashHandler from './WebviewCrashHandler'; +import ServiceDisabled from './ServiceDisabled'; @observer export default class ServiceWebview extends Component { @@ -15,6 +16,7 @@ export default class ServiceWebview extends Component { service: PropTypes.instanceOf(ServiceModel).isRequired, setWebviewReference: PropTypes.func.isRequired, reload: PropTypes.func.isRequired, + enable: PropTypes.func.isRequired, }; static defaultProps = { @@ -56,6 +58,7 @@ export default class ServiceWebview extends Component { service, setWebviewReference, reload, + enable, } = this.props; const webviewClasses = classnames({ @@ -80,27 +83,38 @@ export default class ServiceWebview extends Component { reload={reload} /> )} - { this.webview = element; }} - - autosize - src={service.url} - preload="./webview/plugin.js" - partition={`persist:service-${service.id}`} - - onDidAttach={() => setWebviewReference({ - serviceId: service.id, - webview: this.webview.view, - })} - - onUpdateTargetUrl={this.updateTargetUrl} - - useragent={service.userAgent} - - disablewebsecurity - allowpopups - /> - {statusBar} + {!service.isEnabled && ( + + )} + {service.isEnabled && ( +
+ { this.webview = element; }} + + autosize + src={service.url} + preload="./webview/plugin.js" + partition={`persist:service-${service.id}`} + + onDidAttach={() => setWebviewReference({ + serviceId: service.id, + webview: this.webview.view, + })} + + onUpdateTargetUrl={this.updateTargetUrl} + + useragent={service.userAgent} + + disablewebsecurity + allowpopups + /> + {statusBar} +
+ )} ); } diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js index bad525d22..5230508f7 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, + update: PropTypes.func.isRequired, }; static defaultProps = { @@ -44,6 +45,7 @@ export default class Services extends Component { setWebviewReference, openWindow, reload, + update, } = this.props; const { intl } = this.context; @@ -76,6 +78,13 @@ export default class Services extends Component { setWebviewReference={setWebviewReference} openWindow={openWindow} reload={() => reload({ serviceId: service.id })} + enable={() => update({ + serviceId: service.id, + serviceData: { + isEnabled: true, + }, + redirect: false, + })} /> ))} diff --git a/src/components/services/content/WebviewCrashHandler.js b/src/components/services/content/WebviewCrashHandler.js index 24903f3c5..d48152c18 100644 --- a/src/components/services/content/WebviewCrashHandler.js +++ b/src/components/services/content/WebviewCrashHandler.js @@ -25,7 +25,7 @@ const messages = defineMessages({ }); @observer -export default class ServiceWebview extends Component { +export default class WebviewCrashHandler extends Component { static propTypes = { name: PropTypes.string.isRequired, reload: PropTypes.func.isRequired, diff --git a/src/components/services/tabs/TabBarSortableList.js b/src/components/services/tabs/TabBarSortableList.js index e5ae36419..3340cbbbb 100644 --- a/src/components/services/tabs/TabBarSortableList.js +++ b/src/components/services/tabs/TabBarSortableList.js @@ -24,6 +24,7 @@ class TabBarSortableList extends Component { toggleNotifications: PropTypes.func.isRequired, deleteService: PropTypes.func.isRequired, disableService: PropTypes.func.isRequired, + enableService: PropTypes.func.isRequired, } static contextTypes = { @@ -38,6 +39,7 @@ class TabBarSortableList extends Component { toggleNotifications, deleteService, disableService, + enableService, openSettings, } = this.props; @@ -58,6 +60,7 @@ class TabBarSortableList extends Component { toggleNotifications={() => toggleNotifications({ serviceId: service.id })} deleteService={() => deleteService({ serviceId: service.id })} disableService={() => disableService({ serviceId: service.id })} + enableService={() => enableService({ serviceId: service.id })} openSettings={openSettings} /> ))} diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js index 9e03d2e21..638e17d95 100644 --- a/src/components/services/tabs/TabItem.js +++ b/src/components/services/tabs/TabItem.js @@ -32,6 +32,10 @@ const messages = defineMessages({ id: 'tabs.item.disableService', defaultMessage: '!!!Disable Service', }, + enableService: { + id: 'tabs.item.enableService', + defaultMessage: '!!!Enable Service', + }, deleteService: { id: 'tabs.item.deleteService', defaultMessage: '!!!Delete Service', @@ -49,6 +53,7 @@ class TabItem extends Component { openSettings: PropTypes.func.isRequired, deleteService: PropTypes.func.isRequired, disableService: PropTypes.func.isRequired, + enableService: PropTypes.func.isRequired, }; static contextTypes = { @@ -64,6 +69,7 @@ class TabItem extends Component { toggleNotifications, deleteService, disableService, + enableService, openSettings, } = this.props; const { intl } = this.context; @@ -90,8 +96,8 @@ class TabItem extends Component { : intl.formatMessage(messages.enableNotifications), click: () => toggleNotifications(), }, { - label: intl.formatMessage(messages.disableService), - click: () => disableService(), + label: intl.formatMessage(service.isEnabled ? messages.disableService : messages.enableService), + click: () => (service.isEnabled ? disableService() : enableService()), }, { type: 'separator', }, { @@ -106,6 +112,7 @@ class TabItem extends Component { 'tab-item': true, 'is-active': service.isActive, 'has-custom-icon': service.hasCustomIcon, + 'is-disabled': !service.isEnabled, })} onClick={clickHandler} onContextMenu={() => menu.popup(remote.getCurrentWindow())} diff --git a/src/components/services/tabs/Tabbar.js b/src/components/services/tabs/Tabbar.js index fdb2c0a59..5f63aed16 100644 --- a/src/components/services/tabs/Tabbar.js +++ b/src/components/services/tabs/Tabbar.js @@ -29,20 +29,28 @@ export default class TabBar extends Component { reorder({ oldIndex, newIndex }); }; - disableService = ({ serviceId }) => { + toggleService = ({ serviceId, isEnabled }) => { const { updateService } = this.props; if (serviceId) { updateService({ serviceId, serviceData: { - isEnabled: false, + isEnabled, }, redirect: false, }); } } + disableService({ serviceId }) { + this.toggleService({ serviceId, isEnabled: false }); + } + + enableService({ serviceId }) { + this.toggleService({ serviceId, isEnabled: true }); + } + render() { const { services, @@ -64,7 +72,8 @@ export default class TabBar extends Component { reload={reload} toggleNotifications={toggleNotifications} deleteService={deleteService} - disableService={this.disableService} + disableService={args => this.disableService(args)} + enableService={args => this.enableService(args)} openSettings={openSettings} distance={20} axis="y" diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index 5675fecf4..ba07b1a5b 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js @@ -26,6 +26,10 @@ const messages = defineMessages({ id: 'settings.app.headlineUpdates', defaultMessage: '!!!Updates', }, + headlineAppearance: { + id: 'settings.app.headlineAppearance', + defaultMessage: '!!!Appearance', + }, buttonSearchForUpdate: { id: 'settings.app.buttonSearchForUpdate', defaultMessage: '!!!Check for updates', @@ -119,6 +123,8 @@ export default class EditSettingsForm extends Component { {process.platform === 'win32' && ( )} +

{intl.formatMessage(messages.headlineAppearance)}

+

{intl.formatMessage(messages.headlineLanguage)}

- */} ); } 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'; import Form from '../../lib/Form'; import { gaPage } from '../../lib/analytics'; - import ServiceError from '../../components/settings/services/ServiceError'; import EditServiceForm from '../../components/settings/services/EditServiceForm'; import { required, url, oneRequired } from '../../helpers/validation-helpers'; @@ -27,6 +26,10 @@ const messages = defineMessages({ id: 'settings.service.form.enableNotification', defaultMessage: '!!!Enable Notifications', }, + enableAudio: { + id: 'settings.service.form.enableAudio', + defaultMessage: '!!!Enable audio', + }, team: { id: 'settings.service.form.team', defaultMessage: '!!!Team', @@ -51,11 +54,14 @@ export default class EditServiceScreen extends Component { gaPage('Settings/Service/Edit'); } - onSubmit(serviceData) { + onSubmit(data) { const { action } = this.props.router.params; const { recipes, services } = this.props.stores; const { createService, updateService } = this.props.actions.service; + const serviceData = data; + serviceData.isMuted = !serviceData.isMuted; + if (action === 'edit') { updateService({ serviceId: services.activeSettings.id, serviceData }); } else { @@ -82,6 +88,11 @@ export default class EditServiceScreen extends Component { value: service.isNotificationEnabled, default: true, }, + isMuted: { + label: intl.formatMessage(messages.enableAudio), + value: !service.isMuted, + default: true, + }, }, }; diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 0493b547f..e298728d1 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -106,11 +106,20 @@ "settings.service.form.customUrlPremiumInfo": "To add self hosted services, you need a Franz Premium Supporter Account.", "settings.service.form.customUrlUpgradeAccount": "Upgrade your account", "settings.service.form.indirectMessageInfo": "You will be notified about all new messages in a channel, not just @username, @channel, @here, ...", + "settings.service.form.name": "Name", + "settings.service.form.enableService": "Enable service", + "settings.service.form.enableNotification": "Enable notifications", + "settings.service.form.team": "Team", + "settings.service.form.customUrl": "Custom server", + "settings.service.form.indirectMessages": "Show message badge for all new messages", + "settings.service.form.enableAudio": "Enable audio", + "settings.service.form.isMutedInfo": "When disabled, all notification sounds and audio playback are muted", "settings.service.error.headline": "Error", "settings.service.error.goBack": "Back to services", "settings.service.error.message": "Could not load service recipe.", "settings.services.tooltip.isDisabled": "Service is disabled", "settings.services.tooltip.notificationsDisabled": "Notifications are disabled", + "settings.services.tooltip.isMuted": "All sounds are muted", "settings.services.headline": "Your services", "settings.services.noServicesAdded": "You haven't added any services yet.", "settings.services.discoverServices": "Discover services", @@ -133,12 +142,6 @@ "settings.app.form.language": "Language", "settings.app.form.beta": "Include beta versions", "settings.app.currentVersion": "Current version:", - "settings.service.form.name": "Name", - "settings.service.form.enableService": "Enable service", - "settings.service.form.enableNotification": "Enable notifications", - "settings.service.form.team": "Team", - "settings.service.form.customUrl": "Custom server", - "settings.service.form.indirectMessages": "Show message badge for all new messages", "settings.user.form.firstname": "Firstname", "settings.user.form.lastname": "Lastname", "settings.user.form.email": "Email", 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 { @observable order = 99; @observable isEnabled = true; + @observable isMuted = false; @observable team = ''; @observable customUrl = ''; @observable isNotificationEnabled = true; @@ -54,6 +55,8 @@ export default class Service { this.isIndirectMessageBadgeEnabled = data.isIndirectMessageBadgeEnabled !== undefined ? data.isIndirectMessageBadgeEnabled : this.isIndirectMessageBadgeEnabled; + this.isMuted = data.isMuted !== undefined ? data.isMuted : this.isMuted; + this.recipe = recipe; } diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 1d895d532..96c503510 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js @@ -287,7 +287,7 @@ export default class ServicesStore extends Store { }); } else if (channel === 'notification') { const options = args[0].options; - if (service.recipe.hasNotificationSound) { + if (service.recipe.hasNotificationSound || service.isMuted) { Object.assign(options, { silent: true, }); 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 @@ } } - .settings__indirect-message-help { + .settings__help { margin: -10px 0 20px 55px;; font-size: 12px; color: $theme-gray-light; -- cgit v1.2.3-70-g09d2 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(-) (limited to 'src/i18n/locales') 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-70-g09d2 From dcab45a323f53a10ff5f419daa5ba6442817eebc Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Tue, 14 Nov 2017 21:04:41 +0100 Subject: feat(App): Add option to enable/disable spell checker --- .../settings/navigation/SettingsNavigation.js | 1 - .../settings/settings/EditSettingsForm.js | 33 +++++++++++----- src/config.js | 1 + src/containers/settings/EditSettingsScreen.js | 38 +++++++++++++++--- src/i18n/languages.js | 45 +++++++++++++++++++++- src/i18n/locales/en-US.json | 3 ++ src/i18n/translations.js | 4 +- src/stores/ServicesStore.js | 1 + src/webview/plugin.js | 16 +++++++- src/webview/spellchecker.js | 40 +++++++++++++------ 10 files changed, 149 insertions(+), 33 deletions(-) (limited to 'src/i18n/locales') diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js index 3b21a7765..fea8d682d 100644 --- a/src/components/settings/navigation/SettingsNavigation.js +++ b/src/components/settings/navigation/SettingsNavigation.js @@ -74,7 +74,6 @@ export default class SettingsNavigation extends Component { {intl.formatMessage(messages.logout)} diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index e711ad402..601d57c81 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js @@ -30,9 +30,9 @@ const messages = defineMessages({ id: 'settings.app.headlineAppearance', defaultMessage: '!!!Appearance', }, - headlineMessaging: { - id: 'settings.app.headlineMessaging', - defaultMessage: '!!!Messaging', + headlineAdvanced: { + id: 'settings.app.headlineAdvanced', + defaultMessage: '!!!Advanced', }, buttonSearchForUpdate: { id: 'settings.app.buttonSearchForUpdate', @@ -58,6 +58,10 @@ const messages = defineMessages({ id: 'settings.app.currentVersion', defaultMessage: '!!!Current version:', }, + restartRequired: { + id: 'settings.app.restartRequired', + defaultMessage: '!!!Changes require restart', + }, }); @observer @@ -120,20 +124,31 @@ export default class EditSettingsForm extends Component { onChange={e => this.submit(e)} id="form" > -

{intl.formatMessage(messages.headlineGeneral)}

+ {/* General */} +

{intl.formatMessage(messages.headlineGeneral)}

{process.platform === 'win32' && ( )} -

{intl.formatMessage(messages.headlineAppearance)}

+ + {/* Appearance */} +

{intl.formatMessage(messages.headlineAppearance)}

-

{intl.formatMessage(messages.headlineMessaging)}

- -

{intl.formatMessage(messages.headlineLanguage)}

+ + {/* Language */} +

{intl.formatMessage(messages.headlineLanguage)}

*/} + + {/* Updates */} +

{intl.formatMessage(messages.headlineUpdates)}

{updateIsReadyToInstall ? (