Merge remote-tracking branch 'origin/main'

This commit is contained in:
Weblate
2023-06-08 14:40:08 +00:00
16 changed files with 304 additions and 59 deletions
+1 -1
View File
@@ -48,7 +48,7 @@ For more information about Decky Loader as well as documentation and development
1. Press the <img src="./docs/images/light/steam.svg#gh-dark-mode-only" height=16><img src="./docs/images/dark/steam.svg#gh-light-mode-only" height=16> button and open the Power menu.
1. Select "Switch to Desktop".
1. Navigate to this Github page on a browser of your choice.
1. Download the [installer file](https://github.com/SteamDeckHomebrew/decky-installer/releases/latest/download/decky_installer.desktop).
1. Download the [installer file](https://github.com/SteamDeckHomebrew/decky-installer/releases/latest/download/decky_installer.desktop). (If using firefox, it will be named `decky_installer.desktop.download`. Rename it to `decky_installer.desktop` before running it)
1. Drag the file onto your desktop and double click it to run it.
1. Either type your admin password or allow Decky to temporarily set your admin password to `Decky!` (this password will be removed after the installer finishes)
1. Choose the version of Decky Loader you want to install.
+16 -4
View File
@@ -122,10 +122,7 @@ class PluginBrowser:
logger.debug("Plugin %s was stopped", name)
del self.plugins[name]
logger.debug("Plugin %s was removed from the dictionary", name)
current_plugin_order = self.settings.getSetting("pluginOrder")
current_plugin_order.remove(name)
self.settings.setSetting("pluginOrder", current_plugin_order)
logger.debug("Plugin %s was removed from the pluginOrder setting", name)
self.cleanup_plugin_settings(name)
logger.debug("removing files %s" % str(name))
rmtree(plugin_dir)
except FileNotFoundError:
@@ -234,3 +231,18 @@ class PluginBrowser:
def cancel_plugin_install(self, request_id):
self.install_requests.pop(request_id)
def cleanup_plugin_settings(self, name):
"""Removes any settings related to a plugin. Propably called when a plugin is uninstalled.
Args:
name (string): The name of the plugin
"""
hidden_plugins = self.settings.getSetting("hiddenPlugins", [])
hidden_plugins.remove(name)
self.settings.setSetting("hiddenPlugins", hidden_plugins)
plugin_order = self.settings.getSetting("pluginOrder")
plugin_order.remove(name)
self.settings.setSetting("pluginOrder", plugin_order)
logger.debug("Removed any settings for plugin %s", name)
+2 -1
View File
@@ -395,6 +395,7 @@ async def get_tab_lambda(test) -> Tab:
return tab
SHARED_CTX_NAMES = ["SharedJSContext", "Steam Shared Context presented by Valve™", "Steam", "SP"]
CLOSEABLE_URLS = ["about:blank", "data:text/html,%3Cbody%3E%3C%2Fbody%3E"] # Closing anything other than these *really* likes to crash Steam
DO_NOT_CLOSE_URL = "Valve Steam Gamepad/default" # Steam Big Picture Mode tab
def tab_is_gamepadui(t: Tab) -> bool:
@@ -415,7 +416,7 @@ async def inject_to_tab(tab_name, js, run_async=False):
async def close_old_tabs():
tabs = await get_tabs()
for t in tabs:
if not t.title or (t.title not in SHARED_CTX_NAMES and DO_NOT_CLOSE_URL not in t.url):
if not t.title or (t.title not in SHARED_CTX_NAMES and any(url in t.url for url in CLOSEABLE_URLS) and DO_NOT_CLOSE_URL not in t.url):
logger.debug("Closing tab: " + getattr(t, "title", "Untitled"))
await t.close()
await sleep(0.5)
+9
View File
@@ -17,6 +17,13 @@
"select": "Use this folder"
}
},
"PluginView": {
"hidden_one": "1 plugin is hidden from this list",
"hidden_other": "{{count}} plugins are hidden from this list"
},
"PluginListLabel": {
"hidden": "Hidden from the quick access menu"
},
"PluginCard": {
"plugin_full_access": "This plugin has full access to your Steam Deck.",
"plugin_install": "Install",
@@ -73,6 +80,8 @@
"reload": "Reload",
"uninstall": "Uninstall",
"update_to": "Update to {{name}}",
"show": "Quick access: Show",
"hide": "Quick access: Hide",
"update_all_one": "Update 1 plugin",
"update_all_other": "Update {{count}} plugins"
},
+1 -1
View File
@@ -22,7 +22,7 @@ class SettingsManager:
for file in listdir(wrong_dir):
if file.endswith(".json"):
rename(path.join(wrong_dir,file),
path.join(settings_directory, file))
path.join(settings_directory, file))
self.path = path.join(settings_directory, name + ".json")
+2
View File
@@ -2,3 +2,5 @@ node_modules/
.yalc
yalc.lock
stats.html
+1
View File
@@ -33,6 +33,7 @@
"rollup-plugin-delete": "^2.0.0",
"rollup-plugin-external-globals": "^0.6.1",
"rollup-plugin-polyfill-node": "^0.10.2",
"rollup-plugin-visualizer": "^5.9.0",
"tslib": "^2.5.2",
"typescript": "^4.9.5"
},
+94
View File
@@ -93,6 +93,9 @@ devDependencies:
rollup-plugin-polyfill-node:
specifier: ^0.10.2
version: 0.10.2(rollup@2.79.1)
rollup-plugin-visualizer:
specifier: ^5.9.0
version: 5.9.0(rollup@2.79.1)
tslib:
specifier: ^2.5.2
version: 2.5.2
@@ -1235,6 +1238,15 @@ packages:
engines: {node: '>= 10'}
dev: true
/cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
dev: true
/clone-buffer@1.0.0:
resolution: {integrity: sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==}
engines: {node: '>= 0.10'}
@@ -1414,6 +1426,11 @@ packages:
clone: 1.0.4
dev: true
/define-lazy-prop@2.0.0:
resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
engines: {node: '>=8'}
dev: true
/define-properties@1.2.0:
resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
engines: {node: '>= 0.4'}
@@ -1770,6 +1787,11 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
/get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
dev: true
/get-intrinsic@1.2.1:
resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==}
dependencies:
@@ -2126,6 +2148,12 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/is-docker@2.2.1:
resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
engines: {node: '>=8'}
hasBin: true
dev: true
/is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
@@ -2222,6 +2250,13 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/is-wsl@2.2.0:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'}
dependencies:
is-docker: 2.2.1
dev: true
/isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
dev: true
@@ -2891,6 +2926,15 @@ packages:
mimic-fn: 2.1.0
dev: true
/open@8.4.2:
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
engines: {node: '>=12'}
dependencies:
define-lazy-prop: 2.0.0
is-docker: 2.2.1
is-wsl: 2.2.0
dev: true
/ora@5.4.1:
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
engines: {node: '>=10'}
@@ -3237,6 +3281,11 @@ packages:
engines: {node: '>= 10'}
dev: true
/require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
dev: true
/resolve-from@3.0.0:
resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==}
engines: {node: '>=4'}
@@ -3318,6 +3367,23 @@ packages:
rollup: 2.79.1
dev: true
/rollup-plugin-visualizer@5.9.0(rollup@2.79.1):
resolution: {integrity: sha512-bbDOv47+Bw4C/cgs0czZqfm8L82xOZssk4ayZjG40y9zbXclNk7YikrZTDao6p7+HDiGxrN0b65SgZiVm9k1Cg==}
engines: {node: '>=14'}
hasBin: true
peerDependencies:
rollup: 2.x || 3.x
peerDependenciesMeta:
rollup:
optional: true
dependencies:
open: 8.4.2
picomatch: 2.3.1
rollup: 2.79.1
source-map: 0.7.4
yargs: 17.7.2
dev: true
/rollup@2.79.1:
resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==}
engines: {node: '>=10.0.0'}
@@ -3425,6 +3491,11 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/source-map@0.7.4:
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
engines: {node: '>= 8'}
dev: true
/sourcemap-codec@1.4.8:
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
deprecated: Please use @jridgewell/sourcemap-codec instead
@@ -3958,10 +4029,33 @@ packages:
engines: {node: '>=0.4'}
dev: true
/y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
dev: true
/yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
dev: true
/yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
dev: true
/yargs@17.7.2:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'}
dependencies:
cliui: 8.0.1
escalade: 3.1.1
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 21.1.1
dev: true
/zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
dev: false
+4 -2
View File
@@ -7,6 +7,7 @@ import typescript from '@rollup/plugin-typescript';
import { defineConfig } from 'rollup';
import del from 'rollup-plugin-delete';
import externalGlobals from 'rollup-plugin-external-globals';
import { visualizer } from 'rollup-plugin-visualizer';
const hiddenWarnings = ['THIS_IS_UNDEFINED', 'EVAL'];
@@ -16,7 +17,7 @@ export default defineConfig({
del({ targets: '../backend/static/*', force: true }),
commonjs(),
nodeResolve({
browser: true
browser: true,
}),
externalGlobals({
react: 'SP_REACT',
@@ -33,6 +34,7 @@ export default defineConfig({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
image(),
visualizer(),
],
preserveEntrySignatures: false,
output: {
@@ -46,4 +48,4 @@ export default defineConfig({
if (hiddenWarnings.some((warning) => message.code === warning)) return;
handleWarning(message);
},
});
});
+13 -5
View File
@@ -7,6 +7,7 @@ import { VerInfo } from '../updater';
interface PublicDeckyState {
plugins: Plugin[];
pluginOrder: string[];
hiddenPlugins: string[];
activePlugin: Plugin | null;
updates: PluginUpdateMapping | null;
hasLoaderUpdate?: boolean;
@@ -17,6 +18,7 @@ interface PublicDeckyState {
export class DeckyState {
private _plugins: Plugin[] = [];
private _pluginOrder: string[] = [];
private _hiddenPlugins: string[] = [];
private _activePlugin: Plugin | null = null;
private _updates: PluginUpdateMapping | null = null;
private _hasLoaderUpdate: boolean = false;
@@ -29,6 +31,7 @@ export class DeckyState {
return {
plugins: this._plugins,
pluginOrder: this._pluginOrder,
hiddenPlugins: this._hiddenPlugins,
activePlugin: this._activePlugin,
updates: this._updates,
hasLoaderUpdate: this._hasLoaderUpdate,
@@ -52,6 +55,11 @@ export class DeckyState {
this.notifyUpdate();
}
setHiddenPlugins(hiddenPlugins: string[]) {
this._hiddenPlugins = hiddenPlugins;
this.notifyUpdate();
}
setActivePlugin(name: string) {
this._activePlugin = this._plugins.find((plugin) => plugin.name === name) ?? null;
this.notifyUpdate();
@@ -111,11 +119,11 @@ export const DeckyStateContextProvider: FC<Props> = ({ children, deckyState }) =
return () => deckyState.eventBus.removeEventListener('update', onUpdate);
}, []);
const setIsLoaderUpdating = (hasUpdate: boolean) => deckyState.setIsLoaderUpdating(hasUpdate);
const setVersionInfo = (versionInfo: VerInfo) => deckyState.setVersionInfo(versionInfo);
const setActivePlugin = (name: string) => deckyState.setActivePlugin(name);
const closeActivePlugin = () => deckyState.closeActivePlugin();
const setPluginOrder = (pluginOrder: string[]) => deckyState.setPluginOrder(pluginOrder);
const setIsLoaderUpdating = deckyState.setIsLoaderUpdating.bind(deckyState);
const setVersionInfo = deckyState.setVersionInfo.bind(deckyState);
const setActivePlugin = deckyState.setActivePlugin.bind(deckyState);
const closeActivePlugin = deckyState.closeActivePlugin.bind(deckyState);
const setPluginOrder = deckyState.setPluginOrder.bind(deckyState);
return (
<DeckyStateContext.Provider
+11
View File
@@ -8,6 +8,8 @@ import {
staticClasses,
} from 'decky-frontend-lib';
import { VFC, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaEyeSlash } from 'react-icons/fa';
import { Plugin } from '../plugin';
import { useDeckyState } from './DeckyState';
@@ -16,8 +18,10 @@ import { useQuickAccessVisible } from './QuickAccessVisibleState';
import TitleView from './TitleView';
const PluginView: VFC = () => {
const { hiddenPlugins } = useDeckyState();
const { plugins, updates, activePlugin, pluginOrder, setActivePlugin, closeActivePlugin } = useDeckyState();
const visible = useQuickAccessVisible();
const { t } = useTranslation();
const [pluginList, setPluginList] = useState<Plugin[]>(
plugins.sort((a, b) => pluginOrder.indexOf(a.name) - pluginOrder.indexOf(b.name)),
@@ -48,6 +52,7 @@ const PluginView: VFC = () => {
<PanelSection>
{pluginList
.filter((p) => p.content)
.filter(({ name }) => !hiddenPlugins.includes(name))
.map(({ name, icon }) => (
<PanelSectionRow key={name}>
<ButtonItem layout="below" onClick={() => setActivePlugin(name)}>
@@ -59,6 +64,12 @@ const PluginView: VFC = () => {
</ButtonItem>
</PanelSectionRow>
))}
{hiddenPlugins.length > 0 && (
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', fontSize: '0.8rem', marginTop: '10px' }}>
<FaEyeSlash />
<div>{t('PluginView.hidden', { count: hiddenPlugins.length })}</div>
</div>
)}
</PanelSection>
</div>
</>
@@ -0,0 +1,30 @@
import { ConfirmModal } from 'decky-frontend-lib';
import { FC } from 'react';
interface PluginUninstallModalProps {
name: string;
title: string;
buttonText: string;
description: string;
closeModal?(): void;
}
const PluginUninstallModal: FC<PluginUninstallModalProps> = ({ name, title, buttonText, description, closeModal }) => {
return (
<ConfirmModal
closeModal={closeModal}
onOK={async () => {
await window.DeckyPluginLoader.callServerMethod('uninstall_plugin', { name });
// 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 window.DeckyPluginLoader.hiddenPluginsService.invalidate();
}}
strTitle={title}
strOKButtonText={buttonText}
>
{description}
</ConfirmModal>
);
};
export default PluginUninstallModal;
@@ -0,0 +1,34 @@
import { FC } from 'react';
import { useTranslation } from 'react-i18next';
import { FaEyeSlash } from 'react-icons/fa';
interface PluginListLabelProps {
hidden: boolean;
name: string;
version?: string;
}
const PluginListLabel: FC<PluginListLabelProps> = ({ name, hidden, version }) => {
const { t } = useTranslation();
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
<div>{version ? `${name} - ${version}` : name}</div>
{hidden && (
<div
style={{
fontSize: '0.8rem',
color: '#dcdedf',
display: 'flex',
alignItems: 'center',
gap: '10px',
}}
>
<FaEyeSlash />
{t('PluginListLabel.hidden')}
</div>
)}
</div>
);
};
export default PluginListLabel;
@@ -22,10 +22,7 @@ import {
} from '../../../../store';
import { useSetting } from '../../../../utils/hooks/useSetting';
import { useDeckyState } from '../../../DeckyState';
function labelToName(pluginLabel: string, pluginVersion?: string): string {
return pluginVersion ? pluginLabel.substring(0, pluginLabel.indexOf(` - ${pluginVersion}`)) : pluginLabel;
}
import PluginListLabel from './PluginListLabel';
async function reinstallPlugin(pluginName: string, currentVersion?: string) {
const serverData = await getPluginList();
@@ -36,10 +33,17 @@ async function reinstallPlugin(pluginName: string, currentVersion?: string) {
}
}
function PluginInteractables(props: { entry: ReorderableEntry<PluginData> }) {
const data = props.entry.data;
type PluginTableData = PluginData & { name: string; hidden: boolean; onHide(): void; onShow(): void };
function PluginInteractables(props: { entry: ReorderableEntry<PluginTableData> }) {
const { t } = useTranslation();
let pluginName = labelToName(props.entry.label, data?.version);
// nothing to display without this data...
if (!props.entry.data) {
return null;
}
const { name, update, version, onHide, onShow, hidden } = props.entry.data;
const showCtxMenu = (e: MouseEvent | GamepadEvent) => {
showContextMenu(
@@ -47,7 +51,7 @@ function PluginInteractables(props: { entry: ReorderableEntry<PluginData> }) {
<MenuItem
onSelected={() => {
try {
fetch(`http://127.0.0.1:1337/plugins/${pluginName}/reload`, {
fetch(`http://127.0.0.1:1337/plugins/${name}/reload`, {
method: 'POST',
credentials: 'include',
headers: {
@@ -58,7 +62,7 @@ function PluginInteractables(props: { entry: ReorderableEntry<PluginData> }) {
console.error('Error Reloading Plugin Backend', err);
}
window.DeckyPluginLoader.importPlugin(pluginName, data?.version);
window.DeckyPluginLoader.importPlugin(name, version);
}}
>
{t('PluginListIndex.reload')}
@@ -66,15 +70,20 @@ function PluginInteractables(props: { entry: ReorderableEntry<PluginData> }) {
<MenuItem
onSelected={() =>
window.DeckyPluginLoader.uninstallPlugin(
pluginName,
t('PluginLoader.plugin_uninstall.title', { name: pluginName }),
name,
t('PluginLoader.plugin_uninstall.title', { name }),
t('PluginLoader.plugin_uninstall.button'),
t('PluginLoader.plugin_uninstall.desc', { name: pluginName }),
t('PluginLoader.plugin_uninstall.desc', { name }),
)
}
>
{t('PluginListIndex.uninstall')}
</MenuItem>
{hidden ? (
<MenuItem onSelected={onShow}>{t('PluginListIndex.show')}</MenuItem>
) : (
<MenuItem onSelected={onHide}>{t('PluginListIndex.hide')}</MenuItem>
)}
</Menu>,
e.currentTarget ?? window,
);
@@ -82,22 +91,22 @@ function PluginInteractables(props: { entry: ReorderableEntry<PluginData> }) {
return (
<>
{data?.update ? (
{update ? (
<DialogButton
style={{ height: '40px', minWidth: '60px', marginRight: '10px' }}
onClick={() => requestPluginInstall(pluginName, data?.update as StorePluginVersion, InstallType.UPDATE)}
onOKButton={() => requestPluginInstall(pluginName, data?.update as StorePluginVersion, InstallType.UPDATE)}
onClick={() => requestPluginInstall(name, update, InstallType.UPDATE)}
onOKButton={() => requestPluginInstall(name, update, InstallType.UPDATE)}
>
<div style={{ display: 'flex', minWidth: '180px', justifyContent: 'space-between', alignItems: 'center' }}>
{t('PluginListIndex.update_to', { name: data?.update?.name })}
{t('PluginListIndex.update_to', { name: update.name })}
<FaDownload style={{ paddingLeft: '1rem' }} />
</div>
</DialogButton>
) : (
<DialogButton
style={{ height: '40px', minWidth: '60px', marginRight: '10px' }}
onClick={() => reinstallPlugin(pluginName, data?.version)}
onOKButton={() => reinstallPlugin(pluginName, data?.version)}
onClick={() => reinstallPlugin(name, version)}
onOKButton={() => reinstallPlugin(name, version)}
>
<div style={{ display: 'flex', minWidth: '180px', justifyContent: 'space-between', alignItems: 'center' }}>
{t('PluginListIndex.reinstall')}
@@ -130,7 +139,7 @@ type PluginData = {
};
export default function PluginList() {
const { plugins, updates, pluginOrder, setPluginOrder } = useDeckyState();
const { plugins, updates, pluginOrder, setPluginOrder, hiddenPlugins } = useDeckyState();
const [_, setPluginOrderSetting] = useSetting<string[]>(
'pluginOrder',
plugins.map((plugin) => plugin.name),
@@ -141,22 +150,29 @@ export default function PluginList() {
window.DeckyPluginLoader.checkPluginUpdates();
}, []);
const [pluginEntries, setPluginEntries] = useState<ReorderableEntry<PluginData>[]>([]);
const [pluginEntries, setPluginEntries] = useState<ReorderableEntry<PluginTableData>[]>([]);
const hiddenPluginsService = window.DeckyPluginLoader.hiddenPluginsService;
useEffect(() => {
setPluginEntries(
plugins.map((plugin) => {
plugins.map(({ name, version }) => {
const hidden = hiddenPlugins.includes(name);
return {
label: plugin.version ? `${plugin.name} - ${plugin.version}` : plugin.name,
label: <PluginListLabel name={name} hidden={hidden} version={version} />,
position: pluginOrder.indexOf(name),
data: {
update: updates?.get(plugin.name),
version: plugin.version,
name,
hidden,
version,
update: updates?.get(name),
onHide: () => hiddenPluginsService.update([...hiddenPlugins, name]),
onShow: () => hiddenPluginsService.update(hiddenPlugins.filter((pluginName) => name !== pluginName)),
},
position: pluginOrder.indexOf(plugin.name),
};
}),
);
}, [plugins, updates]);
}, [plugins, updates, hiddenPlugins]);
if (plugins.length === 0) {
return (
@@ -166,8 +182,8 @@ export default function PluginList() {
);
}
function onSave(entries: ReorderableEntry<PluginData>[]) {
const newOrder = entries.map((entry) => labelToName(entry.label, entry?.data?.version));
function onSave(entries: ReorderableEntry<PluginTableData>[]) {
const newOrder = entries.map((entry) => entry.data!.name);
console.log(newOrder);
setPluginOrder(newOrder);
setPluginOrderSetting(newOrder);
@@ -200,7 +216,7 @@ export default function PluginList() {
</DialogButton>
)}
<DialogControlsSection style={{ marginTop: 0 }}>
<ReorderableList<PluginData> entries={pluginEntries} onSave={onSave} interactables={PluginInteractables} />
<ReorderableList<PluginTableData> entries={pluginEntries} onSave={onSave} interactables={PluginInteractables} />
</DialogControlsSection>
</DialogBody>
);
+34
View File
@@ -0,0 +1,34 @@
import { DeckyState } from './components/DeckyState';
import { getSetting, setSetting } from './utils/settings';
/**
* A Service class for managing the state and actions related to the hidden plugins feature
*
* It's mostly responsible for sending setting updates to the server and keeping the local state in sync.
*/
export class HiddenPluginsService {
constructor(private deckyState: DeckyState) {}
init() {
getSetting<string[]>('hiddenPlugins', []).then((hiddenPlugins) => {
this.deckyState.setHiddenPlugins(hiddenPlugins);
});
}
/**
* Sends the new hidden plugins list to the server and persists it locally in the decky state
*
* @param hiddenPlugins The new list of hidden plugins
*/
async update(hiddenPlugins: string[]) {
await setSetting('hiddenPlugins', hiddenPlugins);
this.deckyState.setHiddenPlugins(hiddenPlugins);
}
/**
* Refreshes the state of hidden plugins in the local state
*/
async invalidate() {
this.deckyState.setHiddenPlugins(await getSetting('hiddenPlugins', []));
}
}
+7 -16
View File
@@ -1,5 +1,4 @@
import {
ConfirmModal,
ModalRoot,
PanelSection,
PanelSectionRow,
@@ -18,9 +17,11 @@ import LegacyPlugin from './components/LegacyPlugin';
import { deinitFilepickerPatches, initFilepickerPatches } from './components/modals/filepicker/patches';
import MultiplePluginsInstallModal from './components/modals/MultiplePluginsInstallModal';
import PluginInstallModal from './components/modals/PluginInstallModal';
import PluginUninstallModal from './components/modals/PluginUninstallModal';
import NotificationBadge from './components/NotificationBadge';
import PluginView from './components/PluginView';
import WithSuspense from './components/WithSuspense';
import { HiddenPluginsService } from './hidden-plugins-service';
import Logger from './logger';
import { InstallType, Plugin } from './plugin';
import RouterHook from './router-hook';
@@ -45,6 +46,7 @@ class PluginLoader extends Logger {
private routerHook: RouterHook = new RouterHook();
public toaster: Toaster = new Toaster();
private deckyState: DeckyState = new DeckyState();
public hiddenPluginsService = new HiddenPluginsService(this.deckyState);
private reloadLock: boolean = false;
// stores a list of plugin names which requested to be reloaded
@@ -182,21 +184,8 @@ class PluginLoader extends Logger {
);
}
public uninstallPlugin(name: string, title: string, button_text: string, description: string) {
showModal(
<ConfirmModal
onOK={async () => {
await this.callServerMethod('uninstall_plugin', { name });
}}
onCancel={() => {
// do nothing
}}
strTitle={title}
strOKButtonText={button_text}
>
{description}
</ConfirmModal>,
);
public uninstallPlugin(name: string, title: string, buttonText: string, description: string) {
showModal(<PluginUninstallModal name={name} title={title} buttonText={buttonText} description={description} />);
}
public hasPlugin(name: string) {
@@ -220,6 +209,8 @@ class PluginLoader extends Logger {
console.log(pluginOrder);
this.deckyState.setPluginOrder(pluginOrder);
});
this.hiddenPluginsService.init();
}
public deinit() {