From 99b4b939bdd2140aecf19ddb09a59b44e9cd117d Mon Sep 17 00:00:00 2001 From: AAGaming Date: Fri, 17 Jun 2022 18:43:53 -0400 Subject: Implement React-based plugin store (#81) Co-authored-by: TrainDoctor <11465594+TrainDoctor@users.noreply.github.com> Co-authored-by: WerWolv --- frontend/src/components/PluginView.tsx | 2 +- frontend/src/components/store/PluginCard.tsx | 172 +++++++++++++++++++++++++++ frontend/src/components/store/Store.tsx | 55 +++++++++ 3 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/store/PluginCard.tsx create mode 100644 frontend/src/components/store/Store.tsx (limited to 'frontend/src/components') diff --git a/frontend/src/components/PluginView.tsx b/frontend/src/components/PluginView.tsx index 78bb22c2..92650fec 100644 --- a/frontend/src/components/PluginView.tsx +++ b/frontend/src/components/PluginView.tsx @@ -9,7 +9,7 @@ const PluginView: VFC = () => { const onStoreClick = () => { Router.CloseSideMenus(); - Router.NavigateToExternalWeb('http://127.0.0.1:1337/browser/redirect'); + Router.Navigate('/decky/store'); }; if (activePlugin) { diff --git a/frontend/src/components/store/PluginCard.tsx b/frontend/src/components/store/PluginCard.tsx new file mode 100644 index 00000000..7816d1bb --- /dev/null +++ b/frontend/src/components/store/PluginCard.tsx @@ -0,0 +1,172 @@ +import { + DialogButton, + Dropdown, + Focusable, + Router, + SingleDropdownOption, + SuspensefulImage, + staticClasses, +} from 'decky-frontend-lib'; +import { FC, useRef, useState } from 'react'; + +import { StorePlugin } from './Store'; + +interface PluginCardProps { + plugin: StorePlugin; +} + +const classNames = (...classes: string[]) => { + return classes.join(' '); +}; + +async function requestPluginInstall(plugin: StorePlugin, selectedVer: string) { + const formData = new FormData(); + formData.append('artifact', plugin.artifact); + formData.append('version', selectedVer); + formData.append('hash', plugin.versions[selectedVer]); + await fetch('http://localhost:1337/browser/install_plugin', { + method: 'POST', + body: formData, + }); +} + +const PluginCard: FC = ({ plugin }) => { + const [selectedOption, setSelectedOption] = useState(0); + const buttonRef = useRef(null); + const containerRef = useRef(null); + return ( +
+ {/* TODO: abstract this messy focus hackiness into a custom component in lib */} + { + buttonRef.current!.focus(); + }} + onCancel={(e: CustomEvent) => { + containerRef.current!.querySelectorAll('* :focus').length === 0 + ? Router.NavigateBackOrOpenMenu() + : containerRef.current!.focus(); + }} + style={{ + display: 'flex', + flexDirection: 'column', + background: '#ACB2C924', + height: 'unset', + marginBottom: 'unset', + // boxShadow: var(--gpShadow-Medium); + scrollSnapAlign: 'start', + boxSizing: 'border-box', + }} + > + +
+ +
+

+ Author: {plugin.author} +

+

+ Tags: + {plugin.tags.map((tag: string) => ( + + {tag == 'root' ? 'Requires root' : tag} + + ))} +

+
+
+
+ +
+ requestPluginInstall(plugin, Object.keys(plugin.versions)[selectedOption])} + > + Install + +
+
+ ({ + data: k, + label: v, + })) as SingleDropdownOption[] + } + strDefaultLabel={'Select a version'} + selectedOption={selectedOption} + onChange={({ data }) => setSelectedOption(data)} + /> +
+
+
+
+
+ ); +}; + +export default PluginCard; diff --git a/frontend/src/components/store/Store.tsx b/frontend/src/components/store/Store.tsx new file mode 100644 index 00000000..ebb2bb8e --- /dev/null +++ b/frontend/src/components/store/Store.tsx @@ -0,0 +1,55 @@ +import { SteamSpinner } from 'decky-frontend-lib'; +import { FC, useEffect, useState } from 'react'; + +import PluginCard from './PluginCard'; + +export interface StorePlugin { + artifact: string; + versions: { + [version: string]: string; + }; + author: string; + description: string; + tags: string[]; +} + +const StorePage: FC<{}> = () => { + const [data, setData] = useState(null); + + useEffect(() => { + (async () => { + const res = await fetch('https://beta.deckbrew.xyz/get_plugins', { method: 'GET' }).then((r) => r.json()); + console.log(res); + setData(res); + })(); + }, []); + + return ( +
+
+ {data === null ? ( +
+ +
+ ) : ( + data.map((plugin: StorePlugin) => ) + )} +
+
+ ); +}; + +export default StorePage; -- cgit v1.2.3