diff options
| -rw-r--r-- | backend/browser.py | 8 | ||||
| -rw-r--r-- | backend/main.py | 2 | ||||
| -rw-r--r-- | frontend/package.json | 2 | ||||
| -rw-r--r-- | frontend/pnpm-lock.yaml | 8 | ||||
| -rw-r--r-- | frontend/src/components/modals/PluginInstallModal.tsx | 42 | ||||
| -rw-r--r-- | frontend/src/index.tsx | 2 | ||||
| -rw-r--r-- | frontend/src/plugin-loader.tsx | 31 |
7 files changed, 69 insertions, 26 deletions
diff --git a/backend/browser.py b/backend/browser.py index 52f5233f..c58a97e5 100644 --- a/backend/browser.py +++ b/backend/browser.py @@ -10,6 +10,7 @@ from asyncio import get_event_loop from time import time from hashlib import sha256 from subprocess import Popen +from injector import inject_to_tab import json @@ -23,9 +24,10 @@ class PluginInstallContext: self.hash = hash class PluginBrowser: - def __init__(self, plugin_path, server_instance) -> None: + def __init__(self, plugin_path, server_instance, plugins) -> None: self.log = getLogger("browser") self.plugin_path = plugin_path + self.plugins = plugins self.install_requests = {} server_instance.add_routes([ @@ -64,6 +66,9 @@ class PluginBrowser: self.log.info("uninstalling " + name) self.log.info(" at dir " + self.find_plugin_folder(name)) await tab.evaluate_js(f"DeckyPluginLoader.unloadPlugin('{name}')") + if self.plugins[name]: + self.plugins[name].stop() + self.plugins.pop(name, None) rmtree(self.find_plugin_folder(name)) except FileNotFoundError: self.log.warning(f"Plugin {name} not installed, skipping uninstallation") @@ -95,6 +100,7 @@ class PluginBrowser: ) if ret: self.log.info(f"Installed {name} (Version: {version})") + await inject_to_tab("SP", "window.syncDeckyPlugins()") else: self.log.fatal(f"SHA-256 Mismatch!!!! {name} (Version: {version})") else: diff --git a/backend/main.py b/backend/main.py index 52e0ce06..ce503e8e 100644 --- a/backend/main.py +++ b/backend/main.py @@ -46,7 +46,7 @@ class PluginManager: allow_headers="*") }) self.plugin_loader = Loader(self.web_app, CONFIG["plugin_path"], self.loop, CONFIG["live_reload"]) - self.plugin_browser = PluginBrowser(CONFIG["plugin_path"], self.web_app) + self.plugin_browser = PluginBrowser(CONFIG["plugin_path"], self.web_app, self.plugin_loader.plugins) self.utilities = Utilities(self) self.updater = Updater(self) diff --git a/frontend/package.json b/frontend/package.json index b16452e1..422ba3c3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,7 +37,7 @@ } }, "dependencies": { - "decky-frontend-lib": "^1.2.1", + "decky-frontend-lib": "^1.2.4", "react-icons": "^4.4.0" } } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 21bddef3..932f5e18 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -9,7 +9,7 @@ specifiers: '@types/react': 16.14.0 '@types/react-router': 5.1.18 '@types/webpack': ^5.28.0 - decky-frontend-lib: ^1.2.1 + decky-frontend-lib: ^1.2.4 husky: ^8.0.1 import-sort-style-module: ^6.0.0 inquirer: ^8.2.4 @@ -23,7 +23,7 @@ specifiers: typescript: ^4.7.4 dependencies: - decky-frontend-lib: 1.2.1 + decky-frontend-lib: 1.2.4 react-icons: 4.4.0_react@16.14.0 devDependencies: @@ -806,8 +806,8 @@ packages: ms: 2.1.2 dev: true - /decky-frontend-lib/1.2.1: - resolution: {integrity: sha512-aJmjOSMwQN9LTquYaMhSqW+FhmKLRgLb75JkcGRWKuIe8rjDfwwbAB/ckJseIC8UMzPKhspvcznfxyp+c72B5Q==} + /decky-frontend-lib/1.2.4: + resolution: {integrity: sha512-r3mLEey9KUkF68geJVSjNlOz/Fg4vpMKUzoutSceyd8o/J5l+QR+Vf0b3gwK3UN9Sp4Pj4XQ1eB82+/W0ApsFg==} dev: false /deepmerge/4.2.2: diff --git a/frontend/src/components/modals/PluginInstallModal.tsx b/frontend/src/components/modals/PluginInstallModal.tsx new file mode 100644 index 00000000..992ce748 --- /dev/null +++ b/frontend/src/components/modals/PluginInstallModal.tsx @@ -0,0 +1,42 @@ +import { ModalRoot, QuickAccessTab, Router, Spinner, sleep, staticClasses } from 'decky-frontend-lib'; +import { FC, useState } from 'react'; + +interface PluginInstallModalProps { + artifact: string; + version: string; + hash: string; + // reinstall: boolean; + onOK(): void; + onCancel(): void; + closeModal?(): void; +} + +const PluginInstallModal: FC<PluginInstallModalProps> = ({ artifact, version, hash, onOK, onCancel, closeModal }) => { + const [loading, setLoading] = useState<boolean>(false); + return ( + <ModalRoot + bOKDisabled={loading} + closeModal={closeModal} + onOK={async () => { + setLoading(true); + await onOK(); + Router.NavigateBackOrOpenMenu(); + await sleep(250); + setTimeout(() => Router.OpenQuickAccessMenu(QuickAccessTab.Decky), 1000); + }} + onCancel={async () => { + await onCancel(); + }} + > + <div className={staticClasses.Title} style={{ flexDirection: 'column' }}> + {hash == 'False' ? <h3 style={{ color: 'red' }}>!!!!NO HASH PROVIDED!!!!</h3> : null} + <div style={{ flexDirection: 'row' }}> + {loading && <Spinner style={{ width: '20px' }} />} {loading ? 'Installing' : 'Install'} {artifact} + {version ? ' version ' + version : null}? + </div> + </div> + </ModalRoot> + ); +}; + +export default PluginInstallModal; diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 364ccb1b..188c1330 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -21,7 +21,7 @@ window.importDeckyPlugin = function (name: string) { window.syncDeckyPlugins = async function () { const plugins = await (await fetch('http://127.0.0.1:1337/plugins')).json(); for (const plugin of plugins) { - window.DeckyPluginLoader?.importPlugin(plugin); + if (!window.DeckyPluginLoader.hasPlugin(plugin)) window.DeckyPluginLoader?.importPlugin(plugin); } }; diff --git a/frontend/src/plugin-loader.tsx b/frontend/src/plugin-loader.tsx index 02febcdb..98cb3c06 100644 --- a/frontend/src/plugin-loader.tsx +++ b/frontend/src/plugin-loader.tsx @@ -1,8 +1,9 @@ -import { ModalRoot, QuickAccessTab, Router, showModal, sleep, staticClasses } from 'decky-frontend-lib'; +import { ModalRoot, QuickAccessTab, showModal, staticClasses } from 'decky-frontend-lib'; import { FaPlug } from 'react-icons/fa'; import { DeckyState, DeckyStateContextProvider } from './components/DeckyState'; import LegacyPlugin from './components/LegacyPlugin'; +import PluginInstallModal from './components/modals/PluginInstallModal'; import PluginView from './components/PluginView'; import SettingsPage from './components/settings'; import StorePage from './components/store/Store'; @@ -55,23 +56,13 @@ class PluginLoader extends Logger { public addPluginInstallPrompt(artifact: string, version: string, request_id: string, hash: string) { showModal( - <ModalRoot - onOK={async () => { - await this.callServerMethod('confirm_plugin_install', { request_id }); - Router.NavigateBackOrOpenMenu(); - await sleep(250); - setTimeout(() => Router.OpenQuickAccessMenu(QuickAccessTab.Decky), 1000); - }} - onCancel={() => { - this.callServerMethod('cancel_plugin_install', { request_id }); - }} - > - <div className={staticClasses.Title} style={{ flexDirection: 'column' }}> - {hash == 'False' ? <h3 style={{ color: 'red' }}>!!!!NO HASH PROVIDED!!!!</h3> : null} - Install {artifact} - {version ? ' version ' + version : null}? - </div> - </ModalRoot>, + <PluginInstallModal + artifact={artifact} + version={version} + hash={hash} + onOK={() => this.callServerMethod('confirm_plugin_install', { request_id })} + onCancel={() => this.callServerMethod('cancel_plugin_install', { request_id })} + />, ); } @@ -97,6 +88,10 @@ class PluginLoader extends Logger { ); } + public hasPlugin(name: string) { + return Boolean(this.plugins.find((plugin) => plugin.name == name)); + } + public dismountAll() { for (const plugin of this.plugins) { this.log(`Dismounting ${plugin.name}`); |
