diff options
| author | Jonas Dellinger <jonas@dellinger.dev> | 2023-06-24 12:59:39 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-06-24 12:59:39 +0200 |
| commit | ef9afa8cbca29a3dd83454f44264a4682e968c59 (patch) | |
| tree | 5eb5033ba579ab6159d593cb01d8cf9b011f1942 /frontend/src | |
| parent | 143461d59793665f9e54d05ba00b16c55dfec57f (diff) | |
| download | decky-loader-ef9afa8cbca29a3dd83454f44264a4682e968c59.tar.gz decky-loader-ef9afa8cbca29a3dd83454f44264a4682e968c59.zip | |
Add notification settings, which allows muting decky/plugin toast notifications (#479)
* Add notification settings, which allows muting decky/plugin toast notifications
* Fix typos
Diffstat (limited to 'frontend/src')
| -rw-r--r-- | frontend/src/components/DeckyState.tsx | 9 | ||||
| -rw-r--r-- | frontend/src/components/settings/pages/general/NotificationSettings.tsx | 35 | ||||
| -rw-r--r-- | frontend/src/components/settings/pages/general/index.tsx | 5 | ||||
| -rw-r--r-- | frontend/src/notification-service.tsx | 51 | ||||
| -rw-r--r-- | frontend/src/plugin-loader.tsx | 30 |
5 files changed, 118 insertions, 12 deletions
diff --git a/frontend/src/components/DeckyState.tsx b/frontend/src/components/DeckyState.tsx index 920985b3..d20c8d86 100644 --- a/frontend/src/components/DeckyState.tsx +++ b/frontend/src/components/DeckyState.tsx @@ -1,5 +1,6 @@ import { FC, createContext, useContext, useEffect, useState } from 'react'; +import { DEFAULT_NOTIFICATION_SETTINGS, NotificationSettings } from '../notification-service'; import { Plugin } from '../plugin'; import { PluginUpdateMapping } from '../store'; import { VerInfo } from '../updater'; @@ -13,6 +14,7 @@ interface PublicDeckyState { hasLoaderUpdate?: boolean; isLoaderUpdating: boolean; versionInfo: VerInfo | null; + notificationSettings: NotificationSettings; userInfo: UserInfo | null; } @@ -30,6 +32,7 @@ export class DeckyState { private _hasLoaderUpdate: boolean = false; private _isLoaderUpdating: boolean = false; private _versionInfo: VerInfo | null = null; + private _notificationSettings = DEFAULT_NOTIFICATION_SETTINGS; private _userInfo: UserInfo | null = null; public eventBus = new EventTarget(); @@ -44,6 +47,7 @@ export class DeckyState { hasLoaderUpdate: this._hasLoaderUpdate, isLoaderUpdating: this._isLoaderUpdating, versionInfo: this._versionInfo, + notificationSettings: this._notificationSettings, userInfo: this._userInfo, }; } @@ -93,6 +97,11 @@ export class DeckyState { this.notifyUpdate(); } + setNotificationSettings(notificationSettings: NotificationSettings) { + this._notificationSettings = notificationSettings; + this.notifyUpdate(); + } + setUserInfo(userInfo: UserInfo) { this._userInfo = userInfo; this.notifyUpdate(); diff --git a/frontend/src/components/settings/pages/general/NotificationSettings.tsx b/frontend/src/components/settings/pages/general/NotificationSettings.tsx new file mode 100644 index 00000000..21c2fd82 --- /dev/null +++ b/frontend/src/components/settings/pages/general/NotificationSettings.tsx @@ -0,0 +1,35 @@ +import { Field, Toggle } from 'decky-frontend-lib'; +import { FC } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { useDeckyState } from '../../../DeckyState'; + +const NotificationSettings: FC = () => { + const { notificationSettings } = useDeckyState(); + const notificationService = window.DeckyPluginLoader.notificationService; + + const { t } = useTranslation(); + + return ( + <> + <Field label={t('SettingsGeneralIndex.notifications.decky_updates_label')}> + <Toggle + value={notificationSettings.deckyUpdates} + onChange={(deckyUpdates) => { + notificationService.update({ ...notificationSettings, deckyUpdates }); + }} + /> + </Field> + <Field label={t('SettingsGeneralIndex.notifications.plugin_updates_label')}> + <Toggle + value={notificationSettings.pluginUpdates} + onChange={(pluginUpdates) => { + notificationService.update({ ...notificationSettings, pluginUpdates }); + }} + /> + </Field> + </> + ); +}; + +export default NotificationSettings; diff --git a/frontend/src/components/settings/pages/general/index.tsx b/frontend/src/components/settings/pages/general/index.tsx index 96ae6782..6fc62a46 100644 --- a/frontend/src/components/settings/pages/general/index.tsx +++ b/frontend/src/components/settings/pages/general/index.tsx @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'; import { useDeckyState } from '../../../DeckyState'; import BranchSelect from './BranchSelect'; +import NotificationSettings from './NotificationSettings'; import StoreSelect from './StoreSelect'; import UpdaterSettings from './Updater'; @@ -28,6 +29,10 @@ export default function GeneralSettings({ <StoreSelect /> </DialogControlsSection> <DialogControlsSection> + <DialogControlsSectionHeader>{t('SettingsGeneralIndex.notifications.header')}</DialogControlsSectionHeader> + <NotificationSettings /> + </DialogControlsSection> + <DialogControlsSection> <DialogControlsSectionHeader>{t('SettingsGeneralIndex.other.header')}</DialogControlsSectionHeader> <Field label={t('SettingsGeneralIndex.developer_mode.label')}> <Toggle diff --git a/frontend/src/notification-service.tsx b/frontend/src/notification-service.tsx new file mode 100644 index 00000000..188cebd3 --- /dev/null +++ b/frontend/src/notification-service.tsx @@ -0,0 +1,51 @@ +import { DeckyState } from './components/DeckyState'; +import { getSetting, setSetting } from './utils/settings'; + +export interface NotificationSettings { + deckyUpdates: boolean; + pluginUpdates: boolean; +} + +export const DEFAULT_NOTIFICATION_SETTINGS: NotificationSettings = { + deckyUpdates: true, + pluginUpdates: true, +}; + +/** + * A Service class for managing the notification settings + * + * It's mostly responsible for sending setting updates to the server and keeping the local state in sync. + */ +export class NotificationService { + constructor(private deckyState: DeckyState) {} + + async init() { + const notificationSettings = await getSetting<Partial<NotificationSettings>>('notificationSettings', {}); + + // Adding a fallback to the default settings to be backwards compatible if we ever add new notification settings + this.deckyState.setNotificationSettings({ + ...DEFAULT_NOTIFICATION_SETTINGS, + ...notificationSettings, + }); + } + + /** + * Sends the new notification settings to the server and persists it locally in the decky state + * + * @param notificationSettings The new notification settings + */ + async update(notificationSettings: NotificationSettings) { + await setSetting('notificationSettings', notificationSettings); + this.deckyState.setNotificationSettings(notificationSettings); + } + + /** + * For a specific event, returns true if a notification should be shown + * + * @param event The notification event that should be checked + * @returns true if the notification should be shown + */ + shouldNotify(event: keyof NotificationSettings) { + return this.deckyState.publicState().notificationSettings[event]; + } +} diff --git a/frontend/src/plugin-loader.tsx b/frontend/src/plugin-loader.tsx index e2ffc44a..b27a19bb 100644 --- a/frontend/src/plugin-loader.tsx +++ b/frontend/src/plugin-loader.tsx @@ -24,6 +24,7 @@ import PluginView from './components/PluginView'; import WithSuspense from './components/WithSuspense'; import { HiddenPluginsService } from './hidden-plugins-service'; import Logger from './logger'; +import { NotificationService } from './notification-service'; import { InstallType, Plugin } from './plugin'; import RouterHook from './router-hook'; import { deinitSteamFixes, initSteamFixes } from './steamfixes'; @@ -47,7 +48,9 @@ class PluginLoader extends Logger { private routerHook: RouterHook = new RouterHook(); public toaster: Toaster = new Toaster(); private deckyState: DeckyState = new DeckyState(); + public hiddenPluginsService = new HiddenPluginsService(this.deckyState); + public notificationService = new NotificationService(this.deckyState); private reloadLock: boolean = false; // stores a list of plugin names which requested to be reloaded @@ -121,18 +124,20 @@ class PluginLoader extends Logger { public async notifyUpdates() { const versionInfo = await this.updateVersion(); if (versionInfo?.remote && versionInfo?.remote?.tag_name != versionInfo?.current) { - this.toaster.toast({ - title: <TranslationHelper trans_class={TranslationClass.PLUGIN_LOADER} trans_text="decky_title" />, - body: ( - <TranslationHelper - trans_class={TranslationClass.PLUGIN_LOADER} - trans_text="decky_update_available" - i18n_args={{ tag_name: versionInfo?.remote?.tag_name }} - /> - ), - onClick: () => Router.Navigate('/decky/settings'), - }); this.deckyState.setHasLoaderUpdate(true); + if (this.notificationService.shouldNotify('deckyUpdates')) { + this.toaster.toast({ + title: <TranslationHelper trans_class={TranslationClass.PLUGIN_LOADER} trans_text="decky_title" />, + body: ( + <TranslationHelper + trans_class={TranslationClass.PLUGIN_LOADER} + trans_text="decky_update_available" + i18n_args={{ tag_name: versionInfo?.remote?.tag_name }} + /> + ), + onClick: () => Router.Navigate('/decky/settings'), + }); + } } await sleep(7000); await this.notifyPluginUpdates(); @@ -146,7 +151,7 @@ class PluginLoader extends Logger { public async notifyPluginUpdates() { const updates = await this.checkPluginUpdates(); - if (updates?.size > 0) { + if (updates?.size > 0 && this.notificationService.shouldNotify('pluginUpdates')) { this.toaster.toast({ title: <TranslationHelper trans_class={TranslationClass.PLUGIN_LOADER} trans_text="decky_title" />, body: ( @@ -220,6 +225,7 @@ class PluginLoader extends Logger { }); this.hiddenPluginsService.init(); + this.notificationService.init(); } public deinit() { |
