import type { ToastData } from '@decky/api'; import { Focusable, Navigation, findClassModule, joinClassNames } from '@decky/ui'; import { FC, memo } from 'react'; import Logger from '../logger'; const logger = new Logger('ToastRenderer'); // TODO there are more of these export enum ToastLocation { /** Big Picture popup toasts */ GAMEPADUI_POPUP = 1, /** QAM Notifications tab */ GAMEPADUI_QAM = 3, } interface ToastProps { toast: ToastData; newIndicator?: boolean; } interface ToastRendererProps extends ToastProps { location: ToastLocation; } const templateClasses = findClassModule((m) => m.ShortTemplate) || {}; // These are memoized as they like to randomly rerender const GamepadUIPopupToast: FC> = memo(({ toast }) => { return (
{toast.logo &&
{toast.logo}
}
{toast.icon &&
{toast.icon}
}
{toast.title}
{toast.body}
); }); const GamepadUIQAMToast: FC = memo(({ toast, newIndicator }) => { // The fields aren't mismatched, the logic for these is just a bit weird. return ( { toast.onClick?.(); Navigation.CloseSideMenus(); }} className={joinClassNames( templateClasses.StandardTemplateContainer, toast.className || '', 'DeckyGamepadUIQAMToast', )} >
{toast.logo &&
{toast.logo}
}
{toast.icon &&
{toast.icon}
} {toast.title &&
{toast.title}
} {/* timestamp should always be defined by toaster */} {/* TODO check how valve does this */} {toast.timestamp && (
{toast.timestamp.toLocaleTimeString(undefined, { timeStyle: 'short' })}
)}
{toast.body &&
{toast.body}
} {toast.subtext &&
{toast.subtext}
}
{newIndicator && (
)}
); }); export const ToastRenderer: FC = memo(({ toast, location, newIndicator }) => { switch (location) { default: logger.warn(`Toast UI not implemented for location ${location}! Falling back to GamepadUIQAMToast.`); return ; case ToastLocation.GAMEPADUI_POPUP: return ; case ToastLocation.GAMEPADUI_QAM: return ; } }); export default ToastRenderer;