summaryrefslogtreecommitdiff
path: root/frontend/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components')
-rw-r--r--frontend/src/components/DeckyState.tsx74
-rw-r--r--frontend/src/components/LegacyPlugin.tsx26
-rw-r--r--frontend/src/components/PluginView.tsx69
-rw-r--r--frontend/src/components/TitleView.tsx49
4 files changed, 140 insertions, 78 deletions
diff --git a/frontend/src/components/DeckyState.tsx b/frontend/src/components/DeckyState.tsx
new file mode 100644
index 00000000..cbeeb5b4
--- /dev/null
+++ b/frontend/src/components/DeckyState.tsx
@@ -0,0 +1,74 @@
+import { FC, createContext, useContext, useEffect, useState } from 'react';
+
+import { Plugin } from '../plugin';
+
+interface PublicDeckyState {
+ plugins: Plugin[];
+ activePlugin: Plugin | null;
+}
+
+export class DeckyState {
+ private _plugins: Plugin[] = [];
+ private _activePlugin: Plugin | null = null;
+
+ public eventBus = new EventTarget();
+
+ publicState(): PublicDeckyState {
+ return { plugins: this._plugins, activePlugin: this._activePlugin };
+ }
+
+ setPlugins(plugins: Plugin[]) {
+ this._plugins = plugins;
+ this.notifyUpdate();
+ }
+
+ setActivePlugin(name: string) {
+ this._activePlugin = this._plugins.find((plugin) => plugin.name === name) ?? null;
+ this.notifyUpdate();
+ }
+
+ closeActivePlugin() {
+ this._activePlugin = null;
+ this.notifyUpdate();
+ }
+
+ private notifyUpdate() {
+ this.eventBus.dispatchEvent(new Event('update'));
+ }
+}
+
+interface DeckyStateContext extends PublicDeckyState {
+ setActivePlugin(name: string): void;
+ closeActivePlugin(): void;
+}
+
+const DeckyStateContext = createContext<DeckyStateContext>(null as any);
+
+export const useDeckyState = () => useContext(DeckyStateContext);
+
+interface Props {
+ deckyState: DeckyState;
+}
+
+export const DeckyStateContextProvider: FC<Props> = ({ children, deckyState }) => {
+ const [publicDeckyState, setPublicDeckyState] = useState<PublicDeckyState>({ ...deckyState.publicState() });
+
+ useEffect(() => {
+ function onUpdate() {
+ setPublicDeckyState({ ...deckyState.publicState() });
+ }
+
+ deckyState.eventBus.addEventListener('update', onUpdate);
+
+ return () => deckyState.eventBus.removeEventListener('update', onUpdate);
+ }, []);
+
+ const setActivePlugin = (name: string) => deckyState.setActivePlugin(name);
+ const closeActivePlugin = () => deckyState.closeActivePlugin();
+
+ return (
+ <DeckyStateContext.Provider value={{ ...publicDeckyState, setActivePlugin, closeActivePlugin }}>
+ {children}
+ </DeckyStateContext.Provider>
+ );
+};
diff --git a/frontend/src/components/LegacyPlugin.tsx b/frontend/src/components/LegacyPlugin.tsx
index 86abf2c8..40f9e85f 100644
--- a/frontend/src/components/LegacyPlugin.tsx
+++ b/frontend/src/components/LegacyPlugin.tsx
@@ -1,13 +1,21 @@
-import React from "react"
+import { VFC } from 'react';
-class LegacyPlugin extends React.Component {
- constructor(props: object) {
- super(props);
- }
+// class LegacyPlugin extends React.Component {
+// constructor(props: object) {
+// super(props);
+// }
- render() {
- return <iframe style={{ border: 'none', width: '100%', height: '100%' }} src={this.props.url}></iframe>
- }
+// render() {
+// return <iframe style={{ border: 'none', width: '100%', height: '100%' }} src={this.props.url}></iframe>
+// }
+// }
+
+interface Props {
+ url: string;
}
-export default LegacyPlugin; \ No newline at end of file
+const LegacyPlugin: VFC<Props> = () => {
+ return <div>LegacyPlugin Hello World</div>;
+};
+
+export default LegacyPlugin;
diff --git a/frontend/src/components/PluginView.tsx b/frontend/src/components/PluginView.tsx
index 27cb386a..b3640395 100644
--- a/frontend/src/components/PluginView.tsx
+++ b/frontend/src/components/PluginView.tsx
@@ -1,40 +1,39 @@
-import { Button } from "decky-frontend-lib";
-import React from "react"
+import { ButtonItem, DialogButton, PanelSection, PanelSectionRow } from 'decky-frontend-lib';
+import { VFC } from 'react';
+import { FaArrowLeft } from 'react-icons/fa';
-class PluginView extends React.Component<{}, { runningPlugin: string, plugins: Array<any> }> {
- constructor() {
- super({});
- this.state = {
- plugins: [],
- runningPlugin: ""
- }
- }
+import { useDeckyState } from './DeckyState';
- componentDidMount() {
- window.__DeckyEvLoop.addEventListener("pluginClose", (_) => { this.setState({ runningPlugin: "", plugins: this.state.plugins }) });
- window.__DeckyEvLoop.addEventListener("setPlugins", (ev) => { console.log(ev); this.setState({ plugins: ev.data, runningPlugin: this.state.runningPlugin }) });
- }
+const PluginView: VFC = () => {
+ const { plugins, activePlugin, setActivePlugin, closeActivePlugin } = useDeckyState();
- private openPlugin(name: string) {
- const ev = new Event("pluginOpen");
- ev.data = name;
- window.__DeckyEvLoop.dispatchEvent(ev);
- this.setState({ runningPlugin: name, plugins: this.state.plugins })
- }
+ if (activePlugin) {
+ return (
+ <div>
+ <div style={{ position: 'absolute', top: '3px', left: '16px', zIndex: 20 }}>
+ <DialogButton style={{ minWidth: 0, padding: '10px 12px' }} onClick={closeActivePlugin}>
+ <FaArrowLeft style={{ display: 'block' }} />
+ </DialogButton>
+ </div>
+ {activePlugin.content}
+ </div>
+ );
+ }
- render() {
- if (this.state.runningPlugin) {
- return this.state.plugins.find(x => x.name == this.state.runningPlugin).content;
- }
- else {
- let buttons = [];
- for (const plugin of this.state.plugins) {
- buttons.push(<Button layout="below" onClick={(_) => this.openPlugin(plugin.name)}>{plugin.icon}{plugin.name}</Button>)
- }
- if (buttons.length == 0) return <div className='staticClasses.Text'>No plugins...</div>;
- return buttons;
- }
- }
-}
+ return (
+ <PanelSection>
+ {plugins.map(({ name, icon }) => (
+ <PanelSectionRow key={name}>
+ <ButtonItem layout="below" onClick={() => setActivePlugin(name)}>
+ <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+ <div>{icon}</div>
+ <div>{name}</div>
+ </div>
+ </ButtonItem>
+ </PanelSectionRow>
+ ))}
+ </PanelSection>
+ );
+};
-export default PluginView; \ No newline at end of file
+export default PluginView;
diff --git a/frontend/src/components/TitleView.tsx b/frontend/src/components/TitleView.tsx
index e0a8552f..4b4a6825 100644
--- a/frontend/src/components/TitleView.tsx
+++ b/frontend/src/components/TitleView.tsx
@@ -1,39 +1,20 @@
-import { Button, staticClasses } from "decky-frontend-lib";
-import React from "react"
-import { FaArrowCircleLeft, FaShoppingBag } from "react-icons/fa"
+import { staticClasses } from 'decky-frontend-lib';
+import { VFC } from 'react';
-class TitleView extends React.Component<{}, { runningPlugin: string }> {
- constructor() {
- super({});
- this.state = {
- runningPlugin: ""
- }
- }
+import { useDeckyState } from './DeckyState';
- componentDidMount() {
- window.__DeckyEvLoop.addEventListener("pluginOpen", (ev) => this.setState({ runningPlugin: ev.data }));
- window.__DeckyEvLoop.addEventListener("pluginClose", (_) => this.setState({ runningPlugin: "" }));
- }
+const TitleView: VFC = () => {
+ const { activePlugin } = useDeckyState();
- private openPluginStore() {
- fetch("http://127.0.0.1:1337/methods/open_plugin_store", {method: "POST"})
- }
+ if (activePlugin === null) {
+ return <div className={staticClasses.Title}>Decky</div>;
+ }
- render() {
- if (this.state.runningPlugin)
- return <div className={staticClasses.Title}>
- <Button bottomSeparator={false} onClick={(_) => {
- window.__DeckyEvLoop.dispatchEvent(new Event("pluginClose"));
- this.setState({ runningPlugin: "" });
- }}><FaArrowCircleLeft /></Button>
- {this.state.runningPlugin}
- </div>
- else
- return <div className={staticClasses.Title}>
- Plugins
- <Button bottomSeparator={false} onClick={ (_) => this.openPluginStore() }><FaShoppingBag /></Button>
- </div>
- }
-}
+ return (
+ <div className={staticClasses.Title} style={{ paddingLeft: '60px' }}>
+ {activePlugin.name}
+ </div>
+ );
+};
-export default TitleView; \ No newline at end of file
+export default TitleView;