summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend/decky_loader/locales/en-US.json3
-rw-r--r--frontend/src/components/DeckyState.tsx11
-rw-r--r--frontend/src/components/PluginView.tsx9
-rw-r--r--frontend/src/components/settings/pages/general/index.tsx13
-rw-r--r--frontend/src/components/settings/pages/plugin_list/index.tsx35
-rw-r--r--frontend/src/plugin-loader.tsx6
6 files changed, 67 insertions, 10 deletions
diff --git a/backend/decky_loader/locales/en-US.json b/backend/decky_loader/locales/en-US.json
index 1f87fe3b..6b6553ad 100644
--- a/backend/decky_loader/locales/en-US.json
+++ b/backend/decky_loader/locales/en-US.json
@@ -217,6 +217,9 @@
"developer_mode": {
"label": "Developer mode"
},
+ "sort_plugins": {
+ "label": "Sort plugins alphabetically"
+ },
"notifications": {
"decky_updates_label": "Decky update available",
"header": "Notifications",
diff --git a/frontend/src/components/DeckyState.tsx b/frontend/src/components/DeckyState.tsx
index d1b558c1..db938a26 100644
--- a/frontend/src/components/DeckyState.tsx
+++ b/frontend/src/components/DeckyState.tsx
@@ -10,6 +10,7 @@ interface PublicDeckyState {
disabledPlugins: DisabledPlugin[];
installedPlugins: (Plugin | DisabledPlugin)[];
pluginOrder: string[];
+ sortPlugins: boolean;
frozenPlugins: string[];
hiddenPlugins: string[];
activePlugin: Plugin | null;
@@ -31,6 +32,7 @@ export class DeckyState {
private _disabledPlugins: DisabledPlugin[] = [];
private _installedPlugins: (Plugin | DisabledPlugin)[] = [];
private _pluginOrder: string[] = [];
+ private _sortPlugins: boolean = false;
private _frozenPlugins: string[] = [];
private _hiddenPlugins: string[] = [];
private _activePlugin: Plugin | null = null;
@@ -49,6 +51,7 @@ export class DeckyState {
disabledPlugins: this._disabledPlugins,
installedPlugins: this._installedPlugins,
pluginOrder: this._pluginOrder,
+ sortPlugins: this._sortPlugins,
frozenPlugins: this._frozenPlugins,
hiddenPlugins: this._hiddenPlugins,
activePlugin: this._activePlugin,
@@ -83,6 +86,11 @@ export class DeckyState {
this.notifyUpdate();
}
+ setSortPlugins(sortPlugins: boolean) {
+ this._sortPlugins = sortPlugins;
+ this.notifyUpdate();
+ }
+
setFrozenPlugins(frozenPlugins: string[]) {
this._frozenPlugins = frozenPlugins;
this.notifyUpdate();
@@ -139,6 +147,7 @@ interface DeckyStateContext extends PublicDeckyState {
setActivePlugin(name: string): void;
setPluginOrder(pluginOrder: string[]): void;
setDisabledPlugins(disabled: DisabledPlugin[]): void;
+ setSortPlugins(sortPlugins: boolean): void;
closeActivePlugin(): void;
}
@@ -178,6 +187,7 @@ export const DeckyStateContextProvider: FC<Props> = ({ children, deckyState }) =
const closeActivePlugin = deckyState.closeActivePlugin.bind(deckyState);
const setPluginOrder = deckyState.setPluginOrder.bind(deckyState);
const setDisabledPlugins = deckyState.setDisabledPlugins.bind(deckyState);
+ const setSortPlugins = deckyState.setSortPlugins.bind(deckyState);
return (
<DeckyStateContext.Provider
@@ -189,6 +199,7 @@ export const DeckyStateContextProvider: FC<Props> = ({ children, deckyState }) =
closeActivePlugin,
setPluginOrder,
setDisabledPlugins,
+ setSortPlugins,
}}
>
{children}
diff --git a/frontend/src/components/PluginView.tsx b/frontend/src/components/PluginView.tsx
index ffaa176a..fbafaf05 100644
--- a/frontend/src/components/PluginView.tsx
+++ b/frontend/src/components/PluginView.tsx
@@ -16,6 +16,7 @@ const PluginView: FC = () => {
updates,
activePlugin,
pluginOrder,
+ sortPlugins,
setActivePlugin,
closeActivePlugin,
} = useDeckyState();
@@ -26,10 +27,14 @@ const PluginView: FC = () => {
console.log('updating PluginView after changes');
return [...plugins]
- .sort((a, b) => pluginOrder.indexOf(a.name) - pluginOrder.indexOf(b.name))
+ .sort((a, b) =>
+ sortPlugins
+ ? a.name.localeCompare(b.name, undefined, { sensitivity: 'base' })
+ : pluginOrder.indexOf(a.name) - pluginOrder.indexOf(b.name),
+ )
.filter((p) => p.content)
.filter(({ name }) => !hiddenPlugins.includes(name));
- }, [plugins, pluginOrder, hiddenPlugins]);
+ }, [plugins, pluginOrder, sortPlugins, hiddenPlugins]);
const numberOfHidden = hiddenPlugins.filter((name) => !!plugins.find((p) => p.name === name)).length;
diff --git a/frontend/src/components/settings/pages/general/index.tsx b/frontend/src/components/settings/pages/general/index.tsx
index 84ad88f1..9bd5616d 100644
--- a/frontend/src/components/settings/pages/general/index.tsx
+++ b/frontend/src/components/settings/pages/general/index.tsx
@@ -1,6 +1,7 @@
import { DialogBody, DialogControlsSection, DialogControlsSectionHeader, Field, Toggle } from '@decky/ui';
import { useTranslation } from 'react-i18next';
+import { useSetting } from '../../../../utils/hooks/useSetting';
import { useDeckyState } from '../../../DeckyState';
import BranchSelect from './BranchSelect';
import NotificationSettings from './NotificationSettings';
@@ -14,7 +15,8 @@ export default function GeneralSettings({
isDeveloper: boolean;
setIsDeveloper: (val: boolean) => void;
}) {
- const { versionInfo } = useDeckyState();
+ const { versionInfo, sortPlugins, setSortPlugins } = useDeckyState();
+ const [_, setSortPluginsSetting] = useSetting<boolean>('sortPlugins', false);
const { t } = useTranslation();
return (
@@ -42,6 +44,15 @@ export default function GeneralSettings({
}}
/>
</Field>
+ <Field label={t('SettingsGeneralIndex.sort_plugins.label')}>
+ <Toggle
+ value={sortPlugins}
+ onChange={(toggleValue) => {
+ setSortPlugins(toggleValue);
+ setSortPluginsSetting(toggleValue);
+ }}
+ />
+ </Field>
</DialogControlsSection>
<DialogControlsSection>
<DialogControlsSectionHeader>{t('SettingsGeneralIndex.about.header')}</DialogControlsSectionHeader>
diff --git a/frontend/src/components/settings/pages/plugin_list/index.tsx b/frontend/src/components/settings/pages/plugin_list/index.tsx
index 75365ba5..71a7df6c 100644
--- a/frontend/src/components/settings/pages/plugin_list/index.tsx
+++ b/frontend/src/components/settings/pages/plugin_list/index.tsx
@@ -11,7 +11,7 @@ import {
ReorderableList,
showContextMenu,
} from '@decky/ui';
-import { useEffect, useState } from 'react';
+import { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaDownload, FaEllipsisH, FaRecycle } from 'react-icons/fa';
@@ -171,9 +171,16 @@ type PluginData = {
};
export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
- const { installedPlugins, disabledPlugins, updates, pluginOrder, setPluginOrder, frozenPlugins, hiddenPlugins } =
- useDeckyState();
-
+ const {
+ installedPlugins,
+ disabledPlugins,
+ updates,
+ pluginOrder,
+ setPluginOrder,
+ frozenPlugins,
+ hiddenPlugins,
+ sortPlugins,
+ } = useDeckyState();
const [_, setPluginOrderSetting] = useSetting<string[]>(
'pluginOrder',
installedPlugins.map((plugin) => plugin.name),
@@ -188,6 +195,12 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
const hiddenPluginsService = DeckyPluginLoader.hiddenPluginsService;
const frozenPluginsService = DeckyPluginLoader.frozenPluginsService;
+ const sortedPlugins = useMemo(() => {
+ return sortPlugins
+ ? [...installedPlugins].sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }))
+ : installedPlugins;
+ }, [installedPlugins, sortPlugins]);
+
useEffect(() => {
setPluginEntries(
installedPlugins.map(({ name, version }) => {
@@ -204,7 +217,7 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
disabled={disabledPlugins.find((p) => p.name == name) !== undefined}
/>
),
- position: pluginOrder.indexOf(name),
+ position: sortPlugins ? sortedPlugins.findIndex((p) => p.name === name) : pluginOrder.indexOf(name),
data: {
name,
disabled: disabledPlugins.some((disabledPlugin) => disabledPlugin.name === name),
@@ -221,7 +234,7 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
};
}),
);
- }, [installedPlugins, updates, hiddenPlugins, disabledPlugins]);
+ }, [installedPlugins, updates, hiddenPlugins, disabledPlugins, sortPlugins, sortedPlugins]);
if (installedPlugins.length === 0) {
return (
@@ -232,6 +245,9 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
}
function onSave(entries: ReorderableEntry<PluginTableData>[]) {
+ if (sortPlugins) {
+ return;
+ }
const newOrder = entries.map((entry) => entry.data!.name);
console.log(newOrder);
setPluginOrder(newOrder);
@@ -265,7 +281,12 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
</DialogButton>
)}
<DialogControlsSection style={{ marginTop: 0 }}>
- <ReorderableList<PluginTableData> entries={pluginEntries} onSave={onSave} interactables={PluginInteractables} />
+ <ReorderableList<PluginTableData>
+ entries={pluginEntries}
+ onSave={onSave}
+ interactables={PluginInteractables}
+ disableReordering={sortPlugins}
+ />
</DialogControlsSection>
</DialogBody>
);
diff --git a/frontend/src/plugin-loader.tsx b/frontend/src/plugin-loader.tsx
index f0a358ef..31f4ec7e 100644
--- a/frontend/src/plugin-loader.tsx
+++ b/frontend/src/plugin-loader.tsx
@@ -360,6 +360,12 @@ class PluginLoader extends Logger {
this.deckyState.setPluginOrder(pluginOrder);
});
+ // Grab and set sort plugins setting
+ getSetting<boolean>('sortPlugins', false).then((sortPlugins) => {
+ this.debug('sortPlugins: ', sortPlugins);
+ this.deckyState.setSortPlugins(sortPlugins);
+ });
+
this.frozenPluginsService.init();
this.hiddenPluginsService.init();
this.notificationService.init();