mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-07-11 01:21:57 +00: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": {
|
"updates": {
|
||||||
"header": "Updates"
|
"header": "Updates"
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"header": "Notifications",
|
||||||
|
"decky_updates_label": "Decky update available",
|
||||||
|
"plugin_updates_label": "Plugin updates available"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SettingsIndex": {
|
"SettingsIndex": {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { FC, createContext, useContext, useEffect, useState } from 'react';
|
import { FC, createContext, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { DEFAULT_NOTIFICATION_SETTINGS, NotificationSettings } from '../notification-service';
|
||||||
import { Plugin } from '../plugin';
|
import { Plugin } from '../plugin';
|
||||||
import { PluginUpdateMapping } from '../store';
|
import { PluginUpdateMapping } from '../store';
|
||||||
import { VerInfo } from '../updater';
|
import { VerInfo } from '../updater';
|
||||||
@@ -13,6 +14,7 @@ interface PublicDeckyState {
|
|||||||
hasLoaderUpdate?: boolean;
|
hasLoaderUpdate?: boolean;
|
||||||
isLoaderUpdating: boolean;
|
isLoaderUpdating: boolean;
|
||||||
versionInfo: VerInfo | null;
|
versionInfo: VerInfo | null;
|
||||||
|
notificationSettings: NotificationSettings;
|
||||||
userInfo: UserInfo | null;
|
userInfo: UserInfo | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +32,7 @@ export class DeckyState {
|
|||||||
private _hasLoaderUpdate: boolean = false;
|
private _hasLoaderUpdate: boolean = false;
|
||||||
private _isLoaderUpdating: boolean = false;
|
private _isLoaderUpdating: boolean = false;
|
||||||
private _versionInfo: VerInfo | null = null;
|
private _versionInfo: VerInfo | null = null;
|
||||||
|
private _notificationSettings = DEFAULT_NOTIFICATION_SETTINGS;
|
||||||
private _userInfo: UserInfo | null = null;
|
private _userInfo: UserInfo | null = null;
|
||||||
|
|
||||||
public eventBus = new EventTarget();
|
public eventBus = new EventTarget();
|
||||||
@@ -44,6 +47,7 @@ export class DeckyState {
|
|||||||
hasLoaderUpdate: this._hasLoaderUpdate,
|
hasLoaderUpdate: this._hasLoaderUpdate,
|
||||||
isLoaderUpdating: this._isLoaderUpdating,
|
isLoaderUpdating: this._isLoaderUpdating,
|
||||||
versionInfo: this._versionInfo,
|
versionInfo: this._versionInfo,
|
||||||
|
notificationSettings: this._notificationSettings,
|
||||||
userInfo: this._userInfo,
|
userInfo: this._userInfo,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -93,6 +97,11 @@ export class DeckyState {
|
|||||||
this.notifyUpdate();
|
this.notifyUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setNotificationSettings(notificationSettings: NotificationSettings) {
|
||||||
|
this._notificationSettings = notificationSettings;
|
||||||
|
this.notifyUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
setUserInfo(userInfo: UserInfo) {
|
setUserInfo(userInfo: UserInfo) {
|
||||||
this._userInfo = userInfo;
|
this._userInfo = userInfo;
|
||||||
this.notifyUpdate();
|
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 { useDeckyState } from '../../../DeckyState';
|
||||||
import BranchSelect from './BranchSelect';
|
import BranchSelect from './BranchSelect';
|
||||||
|
import NotificationSettings from './NotificationSettings';
|
||||||
import StoreSelect from './StoreSelect';
|
import StoreSelect from './StoreSelect';
|
||||||
import UpdaterSettings from './Updater';
|
import UpdaterSettings from './Updater';
|
||||||
|
|
||||||
@@ -27,6 +28,10 @@ export default function GeneralSettings({
|
|||||||
<BranchSelect />
|
<BranchSelect />
|
||||||
<StoreSelect />
|
<StoreSelect />
|
||||||
</DialogControlsSection>
|
</DialogControlsSection>
|
||||||
|
<DialogControlsSection>
|
||||||
|
<DialogControlsSectionHeader>{t('SettingsGeneralIndex.notifications.header')}</DialogControlsSectionHeader>
|
||||||
|
<NotificationSettings />
|
||||||
|
</DialogControlsSection>
|
||||||
<DialogControlsSection>
|
<DialogControlsSection>
|
||||||
<DialogControlsSectionHeader>{t('SettingsGeneralIndex.other.header')}</DialogControlsSectionHeader>
|
<DialogControlsSectionHeader>{t('SettingsGeneralIndex.other.header')}</DialogControlsSectionHeader>
|
||||||
<Field label={t('SettingsGeneralIndex.developer_mode.label')}>
|
<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 WithSuspense from './components/WithSuspense';
|
||||||
import { HiddenPluginsService } from './hidden-plugins-service';
|
import { HiddenPluginsService } from './hidden-plugins-service';
|
||||||
import Logger from './logger';
|
import Logger from './logger';
|
||||||
|
import { NotificationService } from './notification-service';
|
||||||
import { InstallType, Plugin } from './plugin';
|
import { InstallType, Plugin } from './plugin';
|
||||||
import RouterHook from './router-hook';
|
import RouterHook from './router-hook';
|
||||||
import { deinitSteamFixes, initSteamFixes } from './steamfixes';
|
import { deinitSteamFixes, initSteamFixes } from './steamfixes';
|
||||||
@@ -47,7 +48,9 @@ class PluginLoader extends Logger {
|
|||||||
private routerHook: RouterHook = new RouterHook();
|
private routerHook: RouterHook = new RouterHook();
|
||||||
public toaster: Toaster = new Toaster();
|
public toaster: Toaster = new Toaster();
|
||||||
private deckyState: DeckyState = new DeckyState();
|
private deckyState: DeckyState = new DeckyState();
|
||||||
|
|
||||||
public hiddenPluginsService = new HiddenPluginsService(this.deckyState);
|
public hiddenPluginsService = new HiddenPluginsService(this.deckyState);
|
||||||
|
public notificationService = new NotificationService(this.deckyState);
|
||||||
|
|
||||||
private reloadLock: boolean = false;
|
private reloadLock: boolean = false;
|
||||||
// stores a list of plugin names which requested to be reloaded
|
// stores a list of plugin names which requested to be reloaded
|
||||||
@@ -121,18 +124,20 @@ class PluginLoader extends Logger {
|
|||||||
public async notifyUpdates() {
|
public async notifyUpdates() {
|
||||||
const versionInfo = await this.updateVersion();
|
const versionInfo = await this.updateVersion();
|
||||||
if (versionInfo?.remote && versionInfo?.remote?.tag_name != versionInfo?.current) {
|
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);
|
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 sleep(7000);
|
||||||
await this.notifyPluginUpdates();
|
await this.notifyPluginUpdates();
|
||||||
@@ -146,7 +151,7 @@ class PluginLoader extends Logger {
|
|||||||
|
|
||||||
public async notifyPluginUpdates() {
|
public async notifyPluginUpdates() {
|
||||||
const updates = await this.checkPluginUpdates();
|
const updates = await this.checkPluginUpdates();
|
||||||
if (updates?.size > 0) {
|
if (updates?.size > 0 && this.notificationService.shouldNotify('pluginUpdates')) {
|
||||||
this.toaster.toast({
|
this.toaster.toast({
|
||||||
title: <TranslationHelper trans_class={TranslationClass.PLUGIN_LOADER} trans_text="decky_title" />,
|
title: <TranslationHelper trans_class={TranslationClass.PLUGIN_LOADER} trans_text="decky_title" />,
|
||||||
body: (
|
body: (
|
||||||
@@ -220,6 +225,7 @@ class PluginLoader extends Logger {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.hiddenPluginsService.init();
|
this.hiddenPluginsService.init();
|
||||||
|
this.notificationService.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public deinit() {
|
public deinit() {
|
||||||
|
|||||||
Reference in New Issue
Block a user