From 65f1eb052de17f21144571d932281a0484f48dfd Mon Sep 17 00:00:00 2001 From: Jesse Bofill Date: Mon, 6 Oct 2025 13:50:02 -0600 Subject: implement base frontend changes necessary for plugin disabling --- frontend/src/components/DeckyState.tsx | 15 ++++++++++++++- .../src/components/settings/pages/plugin_list/index.tsx | 11 ++++++----- frontend/src/components/store/PluginCard.tsx | 4 ++-- frontend/src/components/store/Store.tsx | 2 +- 4 files changed, 23 insertions(+), 9 deletions(-) (limited to 'frontend/src/components') diff --git a/frontend/src/components/DeckyState.tsx b/frontend/src/components/DeckyState.tsx index d2ac63ae..b2f7ac33 100644 --- a/frontend/src/components/DeckyState.tsx +++ b/frontend/src/components/DeckyState.tsx @@ -1,12 +1,14 @@ import { FC, ReactNode, createContext, useContext, useEffect, useState } from 'react'; import { DEFAULT_NOTIFICATION_SETTINGS, NotificationSettings } from '../notification-service'; -import { Plugin } from '../plugin'; +import { DisabledPlugin, Plugin } from '../plugin'; import { PluginUpdateMapping } from '../store'; import { VerInfo } from '../updater'; interface PublicDeckyState { plugins: Plugin[]; + disabled: DisabledPlugin[]; + installedPlugins: (Plugin | DisabledPlugin)[]; pluginOrder: string[]; frozenPlugins: string[]; hiddenPlugins: string[]; @@ -26,6 +28,8 @@ export interface UserInfo { export class DeckyState { private _plugins: Plugin[] = []; + private _disabledPlugins: DisabledPlugin[] = []; + private _installedPlugins: (Plugin | DisabledPlugin)[] = []; private _pluginOrder: string[] = []; private _frozenPlugins: string[] = []; private _hiddenPlugins: string[] = []; @@ -42,6 +46,8 @@ export class DeckyState { publicState(): PublicDeckyState { return { plugins: this._plugins, + disabled: this._disabledPlugins, + installedPlugins: this._disabledPlugins, pluginOrder: this._pluginOrder, frozenPlugins: this._frozenPlugins, hiddenPlugins: this._hiddenPlugins, @@ -62,6 +68,13 @@ export class DeckyState { setPlugins(plugins: Plugin[]) { this._plugins = plugins; + this._installedPlugins = [...plugins, ...this._disabledPlugins]; + this.notifyUpdate(); + } + + setDisabledPlugins(disabledPlugins: DisabledPlugin[]) { + this._disabledPlugins = disabledPlugins; + this._installedPlugins = [...this._plugins, ...disabledPlugins]; this.notifyUpdate(); } diff --git a/frontend/src/components/settings/pages/plugin_list/index.tsx b/frontend/src/components/settings/pages/plugin_list/index.tsx index 9a7cb076..e244b8a9 100644 --- a/frontend/src/components/settings/pages/plugin_list/index.tsx +++ b/frontend/src/components/settings/pages/plugin_list/index.tsx @@ -147,10 +147,11 @@ type PluginData = { }; export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) { - const { plugins, updates, pluginOrder, setPluginOrder, frozenPlugins, hiddenPlugins } = useDeckyState(); + const { installedPlugins, updates, pluginOrder, setPluginOrder, frozenPlugins, hiddenPlugins } = useDeckyState(); + const [_, setPluginOrderSetting] = useSetting( 'pluginOrder', - plugins.map((plugin) => plugin.name), + installedPlugins.map((plugin) => plugin.name), ); const { t } = useTranslation(); @@ -164,7 +165,7 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) { useEffect(() => { setPluginEntries( - plugins.map(({ name, version }) => { + installedPlugins.map(({ name, version }) => { const frozen = frozenPlugins.includes(name); const hidden = hiddenPlugins.includes(name); @@ -186,9 +187,9 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) { }; }), ); - }, [plugins, updates, hiddenPlugins]); + }, [installedPlugins, updates, hiddenPlugins]); - if (plugins.length === 0) { + if (installedPlugins.length === 0) { return (

{t('PluginListIndex.no_plugin')}

diff --git a/frontend/src/components/store/PluginCard.tsx b/frontend/src/components/store/PluginCard.tsx index f64abd09..243f846f 100644 --- a/frontend/src/components/store/PluginCard.tsx +++ b/frontend/src/components/store/PluginCard.tsx @@ -3,13 +3,13 @@ import { CSSProperties, FC, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { FaArrowDown, FaArrowUp, FaCheck, FaDownload, FaRecycle } from 'react-icons/fa'; -import { InstallType, Plugin } from '../../plugin'; +import { DisabledPlugin, InstallType, Plugin } from '../../plugin'; import { StorePlugin, requestPluginInstall } from '../../store'; import ExternalLink from '../ExternalLink'; interface PluginCardProps { storePlugin: StorePlugin; - installedPlugin: Plugin | undefined; + installedPlugin: Plugin | DisabledPlugin | undefined; } const PluginCard: FC = ({ storePlugin, installedPlugin }) => { diff --git a/frontend/src/components/store/Store.tsx b/frontend/src/components/store/Store.tsx index 3209ba08..72187cbc 100644 --- a/frontend/src/components/store/Store.tsx +++ b/frontend/src/components/store/Store.tsx @@ -105,7 +105,7 @@ const BrowseTab: FC<{ setPluginCount: Dispatch> }> })(); }, []); - const { plugins: installedPlugins } = useDeckyState(); + const { installedPlugins } = useDeckyState(); return ( <> -- cgit v1.2.3 From 1ae6519209c9bf079d8dff80d5bceb5a847b08b1 Mon Sep 17 00:00:00 2001 From: Jesse Bofill Date: Mon, 6 Oct 2025 14:29:39 -0600 Subject: implement frontend diisable functions/ modal --- .../src/components/modals/PluginDisablelModal.tsx | 46 ++++++++++++++++++++++ .../settings/pages/plugin_list/index.tsx | 22 ++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 frontend/src/components/modals/PluginDisablelModal.tsx (limited to 'frontend/src/components') diff --git a/frontend/src/components/modals/PluginDisablelModal.tsx b/frontend/src/components/modals/PluginDisablelModal.tsx new file mode 100644 index 00000000..89cda293 --- /dev/null +++ b/frontend/src/components/modals/PluginDisablelModal.tsx @@ -0,0 +1,46 @@ +import { ConfirmModal, Spinner } from '@decky/ui'; +import { FC, useState } from 'react'; + +import { disablePlugin, uninstallPlugin } from '../../plugin'; + +interface PluginUninstallModalProps { + name: string; + title: string; + buttonText: string; + description: string; + closeModal?(): void; +} + +const PluginDisableModal: FC = ({ name, title, buttonText, description, closeModal }) => { + const [disabling, setDisabling] = useState(false); + return ( + { + setDisabling(true); + await disablePlugin(name); + + //not sure about this yet + + // uninstalling a plugin resets the hidden setting for it server-side + // we invalidate here so if you re-install it, you won't have an out-of-date hidden filter + await DeckyPluginLoader.frozenPluginsService.invalidate(); + await DeckyPluginLoader.hiddenPluginsService.invalidate(); + closeModal?.(); + }} + bOKDisabled={disabling} + bCancelDisabled={disabling} + strTitle={ +
+ {title} + {disabling && } +
+ } + strOKButtonText={buttonText} + > + {description} +
+ ); +}; + +export default PluginDisableModal; diff --git a/frontend/src/components/settings/pages/plugin_list/index.tsx b/frontend/src/components/settings/pages/plugin_list/index.tsx index e244b8a9..f13cbe2b 100644 --- a/frontend/src/components/settings/pages/plugin_list/index.tsx +++ b/frontend/src/components/settings/pages/plugin_list/index.tsx @@ -35,6 +35,7 @@ async function reinstallPlugin(pluginName: string, currentVersion?: string) { type PluginTableData = PluginData & { name: string; + disabled: boolean; frozen: boolean; onFreeze(): void; onUnfreeze(): void; @@ -54,7 +55,7 @@ function PluginInteractables(props: { entry: ReorderableEntry } return null; } - const { name, update, version, onHide, onShow, hidden, onFreeze, onUnfreeze, frozen, isDeveloper } = props.entry.data; + const { name, update, version, onHide, onShow, hidden, onFreeze, onUnfreeze, frozen, isDeveloper, disabled } = props.entry.data; const showCtxMenu = (e: MouseEvent | GamepadEvent) => { showContextMenu( @@ -82,6 +83,22 @@ function PluginInteractables(props: { entry: ReorderableEntry } > {t('PluginListIndex.uninstall')} + {disabled ? + DeckyPluginLoader.disablePlugin( + name, + t('PluginLoader.plugin_disable.title', { name }), + t('PluginLoader.plugin_disable.button'), + t('PluginLoader.plugin_disable.desc', { name }), + ) + } + > + {t('PluginListIndex.plugin_disable')} + : + // implement enabler + <> + + } {hidden ? ( {t('PluginListIndex.show')} ) : ( @@ -147,7 +164,7 @@ type PluginData = { }; export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) { - const { installedPlugins, updates, pluginOrder, setPluginOrder, frozenPlugins, hiddenPlugins } = useDeckyState(); + const { installedPlugins, disabled, updates, pluginOrder, setPluginOrder, frozenPlugins, hiddenPlugins } = useDeckyState(); const [_, setPluginOrderSetting] = useSetting( 'pluginOrder', @@ -174,6 +191,7 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) { position: pluginOrder.indexOf(name), data: { name, + disabled: disabled.some(disabledPlugin => disabledPlugin.name === name), frozen, hidden, isDeveloper, -- cgit v1.2.3 From d2b6999520c7e7425a757644c30bc6b494f57b8c Mon Sep 17 00:00:00 2001 From: Jesse Bofill Date: Mon, 6 Oct 2025 15:39:59 -0600 Subject: fix mistakes --- frontend/src/components/DeckyState.tsx | 2 +- .../src/components/modals/PluginDisablelModal.tsx | 2 +- .../settings/pages/plugin_list/index.tsx | 27 +++++++++++----------- 3 files changed, 16 insertions(+), 15 deletions(-) (limited to 'frontend/src/components') diff --git a/frontend/src/components/DeckyState.tsx b/frontend/src/components/DeckyState.tsx index b2f7ac33..3d6e9dc4 100644 --- a/frontend/src/components/DeckyState.tsx +++ b/frontend/src/components/DeckyState.tsx @@ -47,7 +47,7 @@ export class DeckyState { return { plugins: this._plugins, disabled: this._disabledPlugins, - installedPlugins: this._disabledPlugins, + installedPlugins: this._installedPlugins, pluginOrder: this._pluginOrder, frozenPlugins: this._frozenPlugins, hiddenPlugins: this._hiddenPlugins, diff --git a/frontend/src/components/modals/PluginDisablelModal.tsx b/frontend/src/components/modals/PluginDisablelModal.tsx index 89cda293..c455f9fa 100644 --- a/frontend/src/components/modals/PluginDisablelModal.tsx +++ b/frontend/src/components/modals/PluginDisablelModal.tsx @@ -1,7 +1,7 @@ import { ConfirmModal, Spinner } from '@decky/ui'; import { FC, useState } from 'react'; -import { disablePlugin, uninstallPlugin } from '../../plugin'; +import { disablePlugin } from '../../plugin'; interface PluginUninstallModalProps { name: string; diff --git a/frontend/src/components/settings/pages/plugin_list/index.tsx b/frontend/src/components/settings/pages/plugin_list/index.tsx index f13cbe2b..da70f4ca 100644 --- a/frontend/src/components/settings/pages/plugin_list/index.tsx +++ b/frontend/src/components/settings/pages/plugin_list/index.tsx @@ -83,21 +83,22 @@ function PluginInteractables(props: { entry: ReorderableEntry } > {t('PluginListIndex.uninstall')} - {disabled ? - DeckyPluginLoader.disablePlugin( - name, - t('PluginLoader.plugin_disable.title', { name }), - t('PluginLoader.plugin_disable.button'), - t('PluginLoader.plugin_disable.desc', { name }), - ) - } - > - {t('PluginListIndex.plugin_disable')} - : + {disabled ? // implement enabler <> - + : + + DeckyPluginLoader.disablePlugin( + name, + t('PluginLoader.plugin_disable.title', { name }), + t('PluginLoader.plugin_disable.button'), + t('PluginLoader.plugin_disable.desc', { name }), + ) + } + > + {t('PluginListIndex.plugin_disable')} + } {hidden ? ( {t('PluginListIndex.show')} -- cgit v1.2.3