import { DialogBody, DialogButton, DialogControlsSection, Focusable, GamepadEvent, Menu, MenuItem, NavEntryPositionPreferences, ReorderableEntry, ReorderableList, showContextMenu, } from '@decky/ui'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { FaDownload, FaEllipsisH, FaRecycle } from 'react-icons/fa'; import { InstallType, enablePlugin } from '../../../../plugin'; import { StorePluginVersion, getPluginList, requestMultiplePluginInstalls, requestPluginInstall, } from '../../../../store'; import { useSetting } from '../../../../utils/hooks/useSetting'; import { getPluginDisplayName } from '../../../../utils/pluginHelpers'; import { useDeckyState } from '../../../DeckyState'; import PluginListLabel from './PluginListLabel'; async function reinstallPlugin(pluginName: string, currentVersion?: string) { const serverData = await getPluginList(); const remotePlugin = serverData?.find((x) => x.name == pluginName); if (remotePlugin && remotePlugin.versions?.length > 0) { const currentVersionData = remotePlugin.versions.find((version) => version.name == currentVersion); if (currentVersionData) requestPluginInstall(pluginName, currentVersionData, InstallType.REINSTALL); } } type PluginTableData = PluginData & { name: string; disabled: boolean; frozen: boolean; onFreeze(): void; onUnfreeze(): void; hidden: boolean; onHide(): void; onShow(): void; isDeveloper: boolean; }; const reloadPluginBackend = DeckyBackend.callable<[pluginName: string], void>('loader/reload_plugin'); function PluginInteractables(props: { entry: ReorderableEntry }) { const { t } = useTranslation(); // nothing to display without this data... if (!props.entry.data) { return null; } const { name, update, version, onHide, onShow, hidden, onFreeze, onUnfreeze, frozen, isDeveloper, disabled } = props.entry.data; const showCtxMenu = (e: MouseEvent | GamepadEvent) => { showContextMenu( {!disabled && ( { try { await reloadPluginBackend(name); } catch (err) { console.error(`Error Reloading Plugin Backend for ${getPluginDisplayName(name, version)}`, err); } }} > {t('PluginListIndex.reload')} )} DeckyPluginLoader.uninstallPlugin( name, t('PluginLoader.plugin_uninstall.title', { name }), t('PluginLoader.plugin_uninstall.button'), t('PluginLoader.plugin_uninstall.desc', { name }), ) } > {t('PluginListIndex.uninstall')} {disabled ? ( enablePlugin(name)}>{t('PluginListIndex.enable')} ) : ( DeckyPluginLoader.disablePlugin( name, t('PluginLoader.plugin_disable.title', { name }), t('PluginLoader.plugin_disable.button'), t('PluginLoader.plugin_disable.desc', { name }), ) } > {t('PluginListIndex.disable')} )} {!disabled && (hidden ? ( {t('PluginListIndex.show')} ) : ( {t('PluginListIndex.hide')} ))} {frozen ? ( {t('PluginListIndex.unfreeze')} ) : ( isDeveloper && {t('PluginListIndex.freeze')} )} , e.currentTarget ?? window, ); }; return ( {update ? ( requestPluginInstall(name, update, InstallType.UPDATE)} onOKButton={() => requestPluginInstall(name, update, InstallType.UPDATE)} >
{t('PluginListIndex.update_to', { name: update.name })}
) : ( reinstallPlugin(name, version)} onOKButton={() => reinstallPlugin(name, version)} >
{t('PluginListIndex.reinstall')}
)}
); } type PluginData = { update?: StorePluginVersion; version?: string; }; export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) { const { installedPlugins, disabledPlugins, updates, pluginOrder, setPluginOrder, frozenPlugins, hiddenPlugins } = useDeckyState(); const [_, setPluginOrderSetting] = useSetting( 'pluginOrder', installedPlugins.map((plugin) => plugin.name), ); const { t } = useTranslation(); useEffect(() => { DeckyPluginLoader.checkPluginUpdates(); }, [installedPlugins, frozenPlugins]); const [pluginEntries, setPluginEntries] = useState[]>([]); const hiddenPluginsService = DeckyPluginLoader.hiddenPluginsService; const frozenPluginsService = DeckyPluginLoader.frozenPluginsService; useEffect(() => { setPluginEntries( installedPlugins.map(({ name, version }) => { const frozen = frozenPlugins.includes(name); const hidden = hiddenPlugins.includes(name); return { label: (