mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-06-13 12:15:09 +03:00
Add notification settings, which allows muting decky/plugin toast notifications (#479)
* Add notification settings, which allows muting decky/plugin toast notifications * Fix typos
This commit is contained in:
@@ -186,6 +186,11 @@
|
||||
},
|
||||
"updates": {
|
||||
"header": "Updates"
|
||||
},
|
||||
"notifications": {
|
||||
"header": "Notifications",
|
||||
"decky_updates_label": "Decky update available",
|
||||
"plugin_updates_label": "Plugin updates available"
|
||||
}
|
||||
},
|
||||
"SettingsIndex": {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
@@ -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';
|
||||
|
||||
@@ -27,6 +28,10 @@ export default function GeneralSettings({
|
||||
<BranchSelect />
|
||||
<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')}>
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user