summaryrefslogtreecommitdiff
path: root/frontend/src/toaster.tsx
diff options
context:
space:
mode:
authorAAGaming <aa@mail.catvibers.me>2022-08-09 21:52:03 -0400
committerAAGaming <aa@mail.catvibers.me>2022-08-09 21:52:03 -0400
commit67426af3ef73e788d99b6d2e0c730c270daea273 (patch)
tree30c1f4b33e63d38d8d5cc26f26af655f1b5a44ba /frontend/src/toaster.tsx
parent0dbdb4a143f6e4f2b08c5a38a597d5a1c49a109c (diff)
downloaddecky-loader-67426af3ef73e788d99b6d2e0c730c270daea273.tar.gz
decky-loader-67426af3ef73e788d99b6d2e0c730c270daea273.zip
Add api for showing toast notificationsv2.0.4-67426af-pre
Diffstat (limited to 'frontend/src/toaster.tsx')
-rw-r--r--frontend/src/toaster.tsx93
1 files changed, 93 insertions, 0 deletions
diff --git a/frontend/src/toaster.tsx b/frontend/src/toaster.tsx
new file mode 100644
index 00000000..b6901ed2
--- /dev/null
+++ b/frontend/src/toaster.tsx
@@ -0,0 +1,93 @@
+import { ToastData, afterPatch, findInReactTree, findModuleChild, sleep, unpatch } from 'decky-frontend-lib';
+
+import Toast from './components/Toast';
+import Logger from './logger';
+
+declare global {
+ interface Window {
+ __TOASTER_INSTANCE: any;
+ NotificationStore: any;
+ }
+}
+
+class Toaster extends Logger {
+ private instanceRet: any;
+ private node: any;
+ private settingsModule: any;
+
+ constructor() {
+ super('Toaster');
+
+ window.__TOASTER_INSTANCE?.deinit?.();
+ window.__TOASTER_INSTANCE = this;
+ this.init();
+ }
+
+ async init() {
+ this.settingsModule = findModuleChild((m) => {
+ if (typeof m !== 'object') return undefined;
+ for (let prop in m) {
+ if (typeof m[prop]?.settings?.bDisableToastsInGame !== 'undefined') return m[prop];
+ }
+ });
+
+ let instance: any;
+ while (true) {
+ instance = findInReactTree(
+ (document.getElementById('root') as any)._reactRootContainer._internalRoot.current,
+ (x) => x?.memoizedProps?.className?.startsWith('toastmanager_ToastPlaceholder'),
+ );
+ if (instance) break;
+ this.debug('finding instance');
+ await sleep(2000);
+ }
+
+ this.node = instance.return.return;
+ this.node.stateNode.render = (...args: any[]) => {
+ const ret = this.node.stateNode.__proto__.render.call(this.node.stateNode, ...args);
+ if (ret) {
+ this.instanceRet = ret;
+ afterPatch(ret, 'type', (_: any, ret: any) => {
+ if (ret?.props?.children[1]?.children?.props?.notification?.decky) {
+ const toast = ret.props.children[1].children.props.notification;
+ ret.props.children[1].children.type = () => <Toast toast={toast} />;
+ }
+ return ret;
+ });
+ }
+ return ret;
+ };
+ this.node.stateNode.forceUpdate();
+ this.log('Initialized');
+ }
+
+ toast(toast: ToastData) {
+ const settings = this.settingsModule.settings;
+ let toastData = {
+ nNotificationID: window.NotificationStore.m_nNextTestNotificationID++,
+ rtCreated: Date.now(),
+ eType: 15,
+ nToastDurationMS: toast.duration || 5e3,
+ data: toast,
+ decky: true,
+ };
+ // @ts-ignore
+ toastData.data.appid = () => 0;
+ if (
+ (settings.bDisableAllToasts && !toast.critical) ||
+ (settings.bDisableToastsInGame && !toast.critical && window.NotificationStore.BIsUserInGame())
+ )
+ return;
+ window.NotificationStore.m_rgNotificationToasts.push(toastData);
+ window.NotificationStore.DispatchNextToast();
+ window.NotificationStore.m_rgNotificationToasts.pop();
+ }
+
+ deinit() {
+ unpatch(this.instanceRet, 'type');
+ delete this.node.stateNode.render;
+ this.node.stateNode.forceUpdate();
+ }
+}
+
+export default Toaster;