Add plugin reordering (#378)

* feat: started work on saving plugin order

* feat: implemented local ReorderableList

* feat: reoder complete except for usage of DFL

* switched to using dfl reorderableList

* fix: added missing file and removed frag

* updated to newest dfl

* Update defsettings.json

* fix: plugin order was missing on init

* fix: now await pluginOrder

* fix: moved the plugin-order load to plugin-loader

* chore: v6 and dfl bump
This commit is contained in:
Travis Lane
2023-04-03 17:21:31 -04:00
committed by GitHub
parent fd325ef1cc
commit 0f36e87cce
10 changed files with 920 additions and 803 deletions
@@ -2,24 +2,93 @@ import {
DialogBody,
DialogButton,
DialogControlsSection,
Focusable,
GamepadEvent,
Menu,
MenuItem,
ReorderableEntry,
ReorderableList,
showContextMenu,
} from 'decky-frontend-lib';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { FaDownload, FaEllipsisH } from 'react-icons/fa';
import { requestPluginInstall } from '../../../../store';
import { StorePluginVersion, requestPluginInstall } from '../../../../store';
import { useSetting } from '../../../../utils/hooks/useSetting';
import { useDeckyState } from '../../../DeckyState';
function PluginInteractables(props: { entry: ReorderableEntry<PluginData> }) {
const data = props.entry.data;
const showCtxMenu = (e: MouseEvent | GamepadEvent) => {
showContextMenu(
<Menu label="Plugin Actions">
<MenuItem onSelected={() => window.DeckyPluginLoader.importPlugin(props.entry.label, data?.version)}>
Reload
</MenuItem>
<MenuItem onSelected={() => window.DeckyPluginLoader.uninstallPlugin(props.entry.label)}>Uninstall</MenuItem>
</Menu>,
e.currentTarget ?? window,
);
};
return (
<>
{data?.update && (
<DialogButton
style={{ height: '40px', minWidth: '60px', marginRight: '10px' }}
onClick={() => requestPluginInstall(props.entry.label, data?.update as StorePluginVersion)}
onOKButton={() => requestPluginInstall(props.entry.label, data?.update as StorePluginVersion)}
>
<div style={{ display: 'flex', flexDirection: 'row' }}>
Update to {data?.update?.name}
<FaDownload style={{ paddingLeft: '2rem' }} />
</div>
</DialogButton>
)}
<DialogButton
style={{ height: '40px', width: '40px', padding: '10px 12px', minWidth: '40px' }}
onClick={showCtxMenu}
onOKButton={showCtxMenu}
>
<FaEllipsisH />
</DialogButton>
</>
);
}
type PluginData = {
update?: StorePluginVersion;
version?: string;
};
export default function PluginList() {
const { plugins, updates } = useDeckyState();
const { plugins, updates, pluginOrder, setPluginOrder } = useDeckyState();
const [_, setPluginOrderSetting] = useSetting<string[]>(
'pluginOrder',
plugins.map((plugin) => plugin.name),
);
useEffect(() => {
window.DeckyPluginLoader.checkPluginUpdates();
}, []);
const [pluginEntries, setPluginEntries] = useState<ReorderableEntry<PluginData>[]>([]);
useEffect(() => {
setPluginEntries(
plugins.map((plugin) => {
return {
label: plugin.name,
data: {
update: updates?.get(plugin.name),
version: plugin.version,
},
position: pluginOrder.indexOf(plugin.name),
};
}),
);
}, [plugins, updates]);
if (plugins.length === 0) {
return (
<div>
@@ -28,52 +97,17 @@ export default function PluginList() {
);
}
function onSave(entries: ReorderableEntry<PluginData>[]) {
const newOrder = entries.map((entry) => entry.label);
console.log(newOrder);
setPluginOrder(newOrder);
setPluginOrderSetting(newOrder);
}
return (
<DialogBody>
<DialogControlsSection>
<ul style={{ listStyleType: 'none', padding: '0' }}>
{plugins.map(({ name, version }) => {
const update = updates?.get(name);
return (
<li style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', paddingBottom: '10px' }}>
<span>
{name} <span style={{ opacity: '50%' }}>{'(' + version + ')'}</span>
</span>
<Focusable style={{ marginLeft: 'auto', boxShadow: 'none', display: 'flex', justifyContent: 'right' }}>
{update && (
<DialogButton
style={{ height: '40px', minWidth: '60px', marginRight: '10px' }}
onClick={() => requestPluginInstall(name, update)}
>
<div style={{ display: 'flex', flexDirection: 'row' }}>
Update to {update.name}
<FaDownload style={{ paddingLeft: '2rem' }} />
</div>
</DialogButton>
)}
<DialogButton
style={{ height: '40px', width: '40px', padding: '10px 12px', minWidth: '40px' }}
onClick={(e: MouseEvent) =>
showContextMenu(
<Menu label="Plugin Actions">
<MenuItem onSelected={() => window.DeckyPluginLoader.importPlugin(name, version)}>
Reload
</MenuItem>
<MenuItem onSelected={() => window.DeckyPluginLoader.uninstallPlugin(name)}>
Uninstall
</MenuItem>
</Menu>,
e.currentTarget ?? window,
)
}
>
<FaEllipsisH />
</DialogButton>
</Focusable>
</li>
);
})}
</ul>
<ReorderableList<PluginData> entries={pluginEntries} onSave={onSave} interactables={PluginInteractables} />
</DialogControlsSection>
</DialogBody>
);