diff options
| author | AAGaming <aagaming@riseup.net> | 2026-10-26 23:50:59 -0400 |
|---|---|---|
| committer | AAGaming <aagaming@riseup.net> | 2026-10-26 23:50:59 -0400 |
| commit | e82a55e9582fbd6d84f3f4510b04480c35e41fe4 (patch) | |
| tree | 1fe05c2296bbedbf301137a67a28ef542804fd69 /frontend/src/hooks/toaster.tsx | |
| parent | 6301508037b30489c42ee9ea8acee97cdea76297 (diff) | |
| download | decky-loader-e82a55e9582fbd6d84f3f4510b04480c35e41fe4.tar.gz decky-loader-e82a55e9582fbd6d84f3f4510b04480c35e41fe4.zip | |
feat: initial VERY VERY incomplete steam frame support
Diffstat (limited to 'frontend/src/hooks/toaster.tsx')
| -rw-r--r-- | frontend/src/hooks/toaster.tsx | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/frontend/src/hooks/toaster.tsx b/frontend/src/hooks/toaster.tsx new file mode 100644 index 00000000..9108d9ec --- /dev/null +++ b/frontend/src/hooks/toaster.tsx @@ -0,0 +1,125 @@ +import type { ToastData, ToastNotification } from '@decky/api'; +import { + ESteamNotificationAction, + ErrorBoundary, + Patch, + callOriginal, + findModuleExport, + injectFCTrampoline, + replacePatch, +} from '@decky/ui'; + +import Toast from '../components/Toast'; +import Logger from '../logger'; + +declare global { + interface Window { + __TOASTER_INSTANCE: any; + } +} + +class Toaster extends Logger { + private toastPatch?: Patch; + + constructor() { + super('Toaster'); + + window.__TOASTER_INSTANCE?.deinit?.(); + window.__TOASTER_INSTANCE = this; + + const ValveToastRenderer = findModuleExport((e) => e?.toString?.()?.includes(`controller:"notification",method:`)); + // TODO find a way to undo this if possible? + const patchedRenderer = injectFCTrampoline(ValveToastRenderer); + this.toastPatch = replacePatch(patchedRenderer, 'component', (args: any[]) => { + if (args?.[0]?.group?.decky || args?.[0]?.group?.notifications?.[0]?.decky) { + return args[0].group.notifications.map((notification: any) => ( + <ErrorBoundary> + <Toast toast={notification.data} newIndicator={notification.bNewIndicator} location={args?.[0]?.location} /> + </ErrorBoundary> + )); + } + return callOriginal; + }); + + this.log('Initialized'); + } + + toast(toast: ToastData): ToastNotification { + if (toast.sound === undefined) toast.sound = 6; + if (toast.playSound === undefined) toast.playSound = true; + if (toast.showToast === undefined) toast.showToast = true; + if (toast.timestamp === undefined) toast.timestamp = new Date(); + if (toast.showNewIndicator === undefined) toast.showNewIndicator = true; + /* eType 13 + 13: { + proto: m.mu, + fnTray: null, + showToast: !0, + sound: f.PN.ToastMisc, + eFeature: l.uX + } + */ + let toastData = { + nNotificationID: window.NotificationStore.m_nNextTestNotificationID++, + bNewIndicator: toast.showNewIndicator, + rtCreated: Date.now(), + eType: toast.eType || 31, + eSource: 1, // Client + nToastDurationMS: toast.duration || (toast.duration = 5e3), + data: toast, + decky: true, + }; + let group: any; + function fnTray(toast: any, tray: any) { + group = { + eType: toast.eType, + notifications: [toast], + }; + tray.unshift(group); + } + const info = { + showToast: toast.showToast, + sound: toast.sound, + playSound: toast.playSound, + eFeature: 0, + toastDurationMS: toastData.nToastDurationMS, + bCritical: toast.critical, + fnTray, + }; + const self = this; + let expirationTimeout: number; + const toastResult: ToastNotification = { + data: toast, + dismiss() { + // it checks against the id of notifications[0] + try { + expirationTimeout && clearTimeout(expirationTimeout); + group && window.NotificationStore.RemoveGroupFromTray(group); + } catch (e) { + self.error('Error while dismissing toast:', e); + } + }, + }; + if (toast.expiration) { + expirationTimeout = setTimeout(() => { + try { + group && window.NotificationStore.RemoveGroupFromTray(group); + } catch (e) { + this.error('Error while dismissing expired toast:', e); + } + }, toast.expiration); + } + try { + window.NotificationStore.ProcessNotification(info, toastData, ESteamNotificationAction.New); + } catch (e) { + this.error('Error while sending toast:', e); + } + return toastResult; + } + + deinit() { + this.toastPatch?.unpatch(); + } +} + +export default Toaster; |
