import { DialogBody, DialogButton, DialogControlsSection, GamepadEvent, Menu, MenuItem, 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 } from '../../../../plugin'; import { StorePluginVersion, getPluginList, requestMultiplePluginInstalls, requestPluginInstall, } from '../../../../store'; import { useSetting } from '../../../../utils/hooks/useSetting'; 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; 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 } = props.entry.data; const showCtxMenu = (e: MouseEvent | GamepadEvent) => { showContextMenu( { try { await reloadPluginBackend(name); } catch (err) { console.error('Error Reloading Plugin Backend', 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')} {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 { plugins, updates, pluginOrder, setPluginOrder, frozenPlugins, hiddenPlugins } = useDeckyState(); const [_, setPluginOrderSetting] = useSetting( 'pluginOrder', plugins.map((plugin) => plugin.name), ); const { t } = useTranslation(); useEffect(() => { DeckyPluginLoader.checkPluginUpdates(); }, []); const [pluginEntries, setPluginEntries] = useState[]>([]); const hiddenPluginsService = DeckyPluginLoader.hiddenPluginsService; const frozenPluginsService = DeckyPluginLoader.frozenPluginsService; useEffect(() => { setPluginEntries( plugins.map(({ name, version }) => { const frozen = frozenPlugins.includes(name); const hidden = hiddenPlugins.includes(name); return { label: