add settings page with install from URL option

This commit is contained in:
AAGaming
2022-06-22 23:22:27 -04:00
parent 80b223180e
commit 9619c52720
11 changed files with 152 additions and 70 deletions
+3 -23
View File
@@ -1,37 +1,17 @@
import { ButtonItem, DialogButton, PanelSection, PanelSectionRow, Router } from 'decky-frontend-lib';
import { ButtonItem, PanelSection, PanelSectionRow } from 'decky-frontend-lib';
import { VFC } from 'react';
import { FaArrowLeft, FaStore } from 'react-icons/fa';
import { useDeckyState } from './DeckyState';
const PluginView: VFC = () => {
const { plugins, activePlugin, setActivePlugin, closeActivePlugin } = useDeckyState();
const onStoreClick = () => {
Router.CloseSideMenus();
Router.Navigate('/decky/store');
};
const { plugins, activePlugin, setActivePlugin } = useDeckyState();
if (activePlugin) {
return (
<div style={{ height: '100%' }}>
<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>
);
return <div style={{ height: '100%' }}>{activePlugin.content}</div>;
}
return (
<PanelSection>
<div style={{ position: 'absolute', top: '3px', right: '16px', zIndex: 20 }}>
<DialogButton style={{ minWidth: 0, padding: '10px 12px' }} onClick={onStoreClick}>
<FaStore style={{ display: 'block' }} />
</DialogButton>
</div>
{plugins.map(({ name, icon }) => (
<PanelSectionRow key={name}>
<ButtonItem layout="below" onClick={() => setActivePlugin(name)}>
+47 -6
View File
@@ -1,18 +1,59 @@
import { staticClasses } from 'decky-frontend-lib';
import { VFC } from 'react';
import { DialogButton, Focusable, Router, staticClasses } from 'decky-frontend-lib';
import { CSSProperties, VFC } from 'react';
import { FaArrowLeft, FaCog, FaStore } from 'react-icons/fa';
import { useDeckyState } from './DeckyState';
const titleStyles: CSSProperties = {
display: 'flex',
paddingTop: '3px',
paddingBottom: '14px',
paddingRight: '16px',
boxShadow: 'unset',
};
const TitleView: VFC = () => {
const { activePlugin } = useDeckyState();
const { activePlugin, closeActivePlugin } = useDeckyState();
const onSettingsClick = () => {
Router.CloseSideMenus();
Router.Navigate('/decky/settings');
};
const onStoreClick = () => {
Router.CloseSideMenus();
Router.Navigate('/decky/store');
};
if (activePlugin === null) {
return <div className={staticClasses.Title}>Decky</div>;
return (
<Focusable style={titleStyles} className={staticClasses.Title}>
<DialogButton
style={{ height: '28px', width: '40px', minWidth: 0, padding: '10px 12px' }}
onClick={onSettingsClick}
>
<FaCog style={{ marginTop: '-4px', display: 'block' }} />
</DialogButton>
<div style={{ marginRight: 'auto', flex: 0.9 }}>Decky</div>
<DialogButton
style={{ height: '28px', width: '40px', minWidth: 0, padding: '10px 12px' }}
onClick={onStoreClick}
>
<FaStore style={{ marginTop: '-4px', display: 'block' }} />
</DialogButton>
</Focusable>
);
}
return (
<div className={staticClasses.Title} style={{ paddingLeft: '60px' }}>
{activePlugin.name}
<div className={staticClasses.Title} style={titleStyles}>
<DialogButton
style={{ height: '28px', width: '40px', minWidth: 0, padding: '10px 12px' }}
onClick={closeActivePlugin}
>
<FaArrowLeft style={{ marginTop: '-4px', display: 'block' }} />
</DialogButton>
<div style={{ flex: 0.9 }}>{activePlugin.name}</div>
</div>
);
};
@@ -0,0 +1,19 @@
import { SidebarNavigation } from 'decky-frontend-lib';
import GeneralSettings from './pages/GeneralSettings';
export default function SettingsPage() {
return (
<SidebarNavigation
title="Decky Settings"
showTitle
pages={[
{
title: 'General',
content: <GeneralSettings />,
route: '/decky/settings/general',
},
]}
/>
);
}
@@ -0,0 +1,30 @@
import { DialogButton, Field, TextField } from 'decky-frontend-lib';
import { useState } from 'react';
import { FaShapes } from 'react-icons/fa';
import { installFromURL } from '../../store/Store';
export default function GeneralSettings() {
const [pluginURL, setPluginURL] = useState('');
// const [checked, setChecked] = useState(false); // store these in some kind of State instead
return (
<div>
{/* <Field
label="A Toggle with an icon"
icon={<FaShapes style={{ display: 'block' }} />}
>
<Toggle
value={checked}
onChange={(e) => setChecked(e)}
/>
</Field> */}
<Field
label="Manual plugin install"
description={<TextField label={'URL'} value={pluginURL} onChange={(e) => setPluginURL(e?.target.value)} />}
icon={<FaShapes style={{ display: 'block' }} />}
>
<DialogButton onClick={() => installFromURL(pluginURL)}>Install</DialogButton>
</Field>
</div>
);
}
+8 -15
View File
@@ -2,6 +2,7 @@ import {
DialogButton,
Dropdown,
Focusable,
QuickAccessTab,
Router,
SingleDropdownOption,
SuspensefulImage,
@@ -9,7 +10,7 @@ import {
} from 'decky-frontend-lib';
import { FC, useRef, useState } from 'react';
import { StorePlugin } from './Store';
import { StorePlugin, requestPluginInstall } from './Store';
interface PluginCardProps {
plugin: StorePlugin;
@@ -19,17 +20,6 @@ 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<PluginCardProps> = ({ plugin }) => {
const [selectedOption, setSelectedOption] = useState<number>(0);
const buttonRef = useRef<HTMLDivElement>(null);
@@ -50,9 +40,12 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
buttonRef.current!.focus();
}}
onCancel={(e: CustomEvent) => {
containerRef.current!.querySelectorAll('* :focus').length === 0
? Router.NavigateBackOrOpenMenu()
: containerRef.current!.focus();
if (containerRef.current!.querySelectorAll('* :focus').length === 0) {
Router.NavigateBackOrOpenMenu();
setTimeout(() => Router.OpenQuickAccessMenu(QuickAccessTab.Decky), 1000);
} else {
containerRef.current!.focus();
}
}}
style={{
display: 'flex',
+20
View File
@@ -13,6 +13,26 @@ export interface StorePlugin {
tags: string[];
}
export async function installFromURL(url: string) {
const formData = new FormData();
formData.append('artifact', url);
await fetch('http://localhost:1337/browser/install_plugin', {
method: 'POST',
body: formData,
});
}
export async function requestPluginInstall(plugin: StorePlugin, selectedVer: string) {
const formData = new FormData();
formData.append('artifact', `https://github.com/${plugin.artifact}/archive/refs/tags/${selectedVer}.zip`);
formData.append('version', selectedVer);
formData.append('hash', plugin.versions[selectedVer]);
await fetch('http://localhost:1337/browser/install_plugin', {
method: 'POST',
body: formData,
});
}
const StorePage: FC<{}> = () => {
const [data, setData] = useState<StorePlugin[] | null>(null);
+12 -12
View File
@@ -1,9 +1,10 @@
import { ModalRoot, showModal, 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 PluginView from './components/PluginView';
import SettingsPage from './components/settings';
import StorePage from './components/store/Store';
import TitleView from './components/TitleView';
import Logger from './logger';
@@ -31,14 +32,11 @@ class PluginLoader extends Logger {
this.log('Initialized');
this.tabsHook.add({
id: 'main',
title: (
<DeckyStateContextProvider deckyState={this.deckyState}>
<TitleView />
</DeckyStateContextProvider>
),
id: QuickAccessTab.Decky,
title: null,
content: (
<DeckyStateContextProvider deckyState={this.deckyState}>
<TitleView />
<PluginView />
</DeckyStateContextProvider>
),
@@ -46,22 +44,23 @@ class PluginLoader extends Logger {
});
this.routerHook.addRoute('/decky/store', () => <StorePage />);
this.routerHook.addRoute('/decky/settings', () => <SettingsPage />);
}
public addPluginInstallPrompt(artifact: string, version: string, request_id: string) {
public addPluginInstallPrompt(artifact: string, version: string, request_id: string, hash: string) {
showModal(
<ModalRoot
onOK={() => {
console.log('ok');
this.callServerMethod('confirm_plugin_install', { request_id });
}}
onCancel={() => {
console.log('nope');
this.callServerMethod('cancel_plugin_install', { request_id });
}}
>
<div className={staticClasses.Title}>
Install {artifact} version {version}?
<div className={staticClasses.Title} style={{ flexDirection: 'column' }}>
{hash == 'False' ? <h1 style={{ color: 'red' }}>!!!!NO HASH PROVIDED!!!!</h1> : null}
Install {artifact}
{version ? ' version ' + version : null}?
</div>
</ModalRoot>,
);
@@ -76,6 +75,7 @@ class PluginLoader extends Logger {
public deinit() {
this.routerHook.removeRoute('/decky/store');
this.routerHook.removeRoute('/decky/settings');
}
public async importPlugin(name: string) {
+2 -2
View File
@@ -1,4 +1,4 @@
import { afterPatch, sleep, unpatch } from 'decky-frontend-lib';
import { QuickAccessTab, afterPatch, sleep, unpatch } from 'decky-frontend-lib';
import { memo } from 'react';
import Logger from './logger';
@@ -18,7 +18,7 @@ const isTabsArray = (tabs: any) => {
};
interface Tab {
id: string;
id: QuickAccessTab | number;
title: any;
content: any;
icon: any;