summaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
authorParty Wumpus <48649272+PartyWumpus@users.noreply.github.com>2024-02-07 17:38:08 +0000
committerAAGaming <aagaming@riseup.net>2024-02-14 16:43:30 -0500
commitec41c6121956afc4eb33c9f21905cdce4c07a097 (patch)
tree6310daec76d70fbbd78fb765b36adf2b0fde1ce5 /frontend
parent21c7742f9a5ad499a95c6b54644d5494d5536967 (diff)
downloaddecky-loader-ec41c6121956afc4eb33c9f21905cdce4c07a097.tar.gz
decky-loader-ec41c6121956afc4eb33c9f21905cdce4c07a097.zip
Refactor plugin store and add sorting by downloads and release date (#547)
* untested first commit * fix types & names * comment out built in sorting for now * rerun search when sort changes * fix ts complaints * use prettier * stop switch-case fall through * move spinner * use locale instead of hardcoded string * fix typo * add sorting by downloads & try using the data field in the dropdown for data * fix typing error * fix asc/desc in dropdown * fix asc/desc again. asc = smaller one go first aaaaa * I don't think i know what ascending means maybe * use props instead of children, like a normal component
Diffstat (limited to 'frontend')
-rw-r--r--frontend/src/components/store/Store.tsx137
-rw-r--r--frontend/src/store.tsx26
2 files changed, 95 insertions, 68 deletions
diff --git a/frontend/src/components/store/Store.tsx b/frontend/src/components/store/Store.tsx
index 1afbb22b..e3d1b0f1 100644
--- a/frontend/src/components/store/Store.tsx
+++ b/frontend/src/components/store/Store.tsx
@@ -8,20 +8,19 @@ import {
TextField,
findModule,
} from 'decky-frontend-lib';
-import { FC, useEffect, useMemo, useState } from 'react';
+import { Dispatch, FC, SetStateAction, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import logo from '../../../assets/plugin_store.png';
import Logger from '../../logger';
-import { Store, StorePlugin, getPluginList, getStore } from '../../store';
+import { SortDirections, SortOptions, Store, StorePlugin, getPluginList, getStore } from '../../store';
import PluginCard from './PluginCard';
const logger = new Logger('Store');
const StorePage: FC<{}> = () => {
const [currentTabRoute, setCurrentTabRoute] = useState<string>('browse');
- const [data, setData] = useState<StorePlugin[] | null>(null);
- const [isTesting, setIsTesting] = useState<boolean>(false);
+ const [pluginCount, setPluginCount] = useState<number | null>(null);
const { TabCount } = findModule((m) => {
if (m?.TabCount && m?.TabTitle) return true;
return false;
@@ -29,17 +28,6 @@ const StorePage: FC<{}> = () => {
const { t } = useTranslation();
- useEffect(() => {
- (async () => {
- const res = await getPluginList();
- logger.log('got data!', res);
- setData(res);
- const storeRes = await getStore();
- logger.log(`store is ${storeRes}, isTesting is ${storeRes === Store.Testing}`);
- setIsTesting(storeRes === Store.Testing);
- })();
- }, []);
-
return (
<>
<div
@@ -49,52 +37,71 @@ const StorePage: FC<{}> = () => {
background: '#0005',
}}
>
- {!data ? (
- <div style={{ height: '100%' }}>
- <SteamSpinner />
- </div>
- ) : (
- <Tabs
- activeTab={currentTabRoute}
- onShowTab={(tabId: string) => {
- setCurrentTabRoute(tabId);
- }}
- tabs={[
- {
- title: t('Store.store_tabs.title'),
- content: <BrowseTab children={{ data: data, isTesting: isTesting }} />,
- id: 'browse',
- renderTabAddon: () => <span className={TabCount}>{data.length}</span>,
- },
- {
- title: t('Store.store_tabs.about'),
- content: <AboutTab />,
- id: 'about',
- },
- ]}
- />
- )}
+ <Tabs
+ activeTab={currentTabRoute}
+ onShowTab={(tabId: string) => {
+ setCurrentTabRoute(tabId);
+ }}
+ tabs={[
+ {
+ title: t('Store.store_tabs.title'),
+ content: <BrowseTab setPluginCount={setPluginCount} />,
+ id: 'browse',
+ renderTabAddon: () => <span className={TabCount}>{pluginCount}</span>,
+ },
+ {
+ title: t('Store.store_tabs.about'),
+ content: <AboutTab />,
+ id: 'about',
+ },
+ ]}
+ />
</div>
</>
);
};
-const BrowseTab: FC<{ children: { data: StorePlugin[]; isTesting: boolean } }> = (data) => {
+const BrowseTab: FC<{ setPluginCount: Dispatch<SetStateAction<number | null>> }> = ({ setPluginCount }) => {
const { t } = useTranslation();
- const sortOptions = useMemo(
+ const dropdownSortOptions = useMemo(
(): DropdownOption[] => [
- { data: 1, label: t('Store.store_tabs.alph_desc') },
- { data: 2, label: t('Store.store_tabs.alph_asce') },
+ // ascending and descending order are the wrong way around for the alphabetical sort
+ // this is because it was initially done incorrectly for i18n and 'fixing' it would
+ // make all the translations incorrect
+ { data: [SortOptions.name, SortDirections.ascending], label: t('Store.store_tabs.alph_desc') },
+ { data: [SortOptions.name, SortDirections.descending], label: t('Store.store_tabs.alph_asce') },
+ { data: [SortOptions.date, SortDirections.ascending], label: t('Store.store_tabs.date_asce') },
+ { data: [SortOptions.date, SortDirections.descending], label: t('Store.store_tabs.date_desc') },
+ { data: [SortOptions.downloads, SortDirections.descending], label: t('Store.store_tabs.downloads_desc') },
+ { data: [SortOptions.downloads, SortDirections.ascending], label: t('Store.store_tabs.downloads_asce') },
],
[],
);
// const filterOptions = useMemo((): DropdownOption[] => [{ data: 1, label: 'All' }], []);
-
- const [selectedSort, setSort] = useState<number>(sortOptions[0].data);
+ const [selectedSort, setSort] = useState<[SortOptions, SortDirections]>(dropdownSortOptions[0].data);
// const [selectedFilter, setFilter] = useState<number>(filterOptions[0].data);
const [searchFieldValue, setSearchValue] = useState<string>('');
+ const [pluginList, setPluginList] = useState<StorePlugin[] | null>(null);
+ const [isTesting, setIsTesting] = useState<boolean>(false);
+
+ useEffect(() => {
+ (async () => {
+ const res = await getPluginList(selectedSort[0], selectedSort[1]);
+ logger.log('got data!', res);
+ setPluginList(res);
+ setPluginCount(res.length);
+ })();
+ }, [selectedSort]);
+
+ useEffect(() => {
+ (async () => {
+ const storeRes = await getStore();
+ logger.log(`store is ${storeRes}, isTesting is ${storeRes === Store.Testing}`);
+ setIsTesting(storeRes === Store.Testing);
+ })();
+ }, []);
return (
<>
@@ -117,7 +124,7 @@ const BrowseTab: FC<{ children: { data: StorePlugin[]; isTesting: boolean } }> =
<span className="DialogLabel">{t("Store.store_sort.label")}</span>
<Dropdown
menuLabel={t("Store.store_sort.label") as string}
- rgOptions={sortOptions}
+ rgOptions={dropdownSortOptions}
strDefaultLabel={t("Store.store_sort.label_def") as string}
selectedOption={selectedSort}
onChange={(e) => setSort(e.data)}
@@ -163,7 +170,7 @@ const BrowseTab: FC<{ children: { data: StorePlugin[]; isTesting: boolean } }> =
<span className="DialogLabel">{t('Store.store_sort.label')}</span>
<Dropdown
menuLabel={t('Store.store_sort.label') as string}
- rgOptions={sortOptions}
+ rgOptions={dropdownSortOptions}
strDefaultLabel={t('Store.store_sort.label_def') as string}
selectedOption={selectedSort}
onChange={(e) => setSort(e.data)}
@@ -182,7 +189,7 @@ const BrowseTab: FC<{ children: { data: StorePlugin[]; isTesting: boolean } }> =
</div>
</Focusable>
</div>
- {data.children.isTesting && (
+ {isTesting && (
<div
style={{
alignItems: 'center',
@@ -213,22 +220,22 @@ const BrowseTab: FC<{ children: { data: StorePlugin[]; isTesting: boolean } }> =
</div>
)}
<div>
- {data.children.data
- .filter((plugin: StorePlugin) => {
- return (
- plugin.name.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
- plugin.description.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
- plugin.author.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
- plugin.tags.some((tag: string) => tag.toLowerCase().includes(searchFieldValue.toLowerCase()))
- );
- })
- .sort((a, b) => {
- if (selectedSort % 2 === 1) return a.name.localeCompare(b.name);
- else return b.name.localeCompare(a.name);
- })
- .map((plugin: StorePlugin) => (
- <PluginCard plugin={plugin} />
- ))}
+ {!pluginList ? (
+ <div style={{ height: '100%' }}>
+ <SteamSpinner />
+ </div>
+ ) : (
+ pluginList
+ .filter((plugin: StorePlugin) => {
+ return (
+ plugin.name.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
+ plugin.description.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
+ plugin.author.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
+ plugin.tags.some((tag: string) => tag.toLowerCase().includes(searchFieldValue.toLowerCase()))
+ );
+ })
+ .map((plugin: StorePlugin) => <PluginCard plugin={plugin} />)
+ )}
</div>
</>
);
diff --git a/frontend/src/store.tsx b/frontend/src/store.tsx
index 3fcfdb2f..8ab8f50a 100644
--- a/frontend/src/store.tsx
+++ b/frontend/src/store.tsx
@@ -7,6 +7,17 @@ export enum Store {
Custom,
}
+export enum SortOptions {
+ name = 'name',
+ date = 'date',
+ downloads = 'downloads',
+}
+
+export enum SortDirections {
+ ascending = 'asc',
+ descending = 'desc',
+}
+
export interface StorePluginVersion {
name: string;
hash: string;
@@ -36,10 +47,19 @@ export async function getStore(): Promise<Store> {
return await getSetting<Store>('store', Store.Default);
}
-export async function getPluginList(): Promise<StorePlugin[]> {
- let version = await DeckyPluginLoader.updateVersion();
+export async function getPluginList(
+ sort_by: SortOptions | null = null,
+ sort_direction: SortDirections | null = null,
+): Promise<StorePlugin[]> {
+ let version = await window.DeckyPluginLoader.updateVersion();
let store = await getSetting<Store | null>('store', null);
let customURL = await getSetting<string>('store-url', 'https://plugins.deckbrew.xyz/plugins');
+
+ let query: URLSearchParams | string = new URLSearchParams();
+ sort_by && query.set('sort_by', sort_by);
+ sort_direction && query.set('sort_direction', sort_direction);
+ query = '?' + String(query);
+
let storeURL;
if (store === null) {
console.log('Could not get store, using Default.');
@@ -82,7 +102,7 @@ export async function getPluginList(): Promise<StorePlugin[]> {
storeURL = 'https://plugins.deckbrew.xyz/plugins';
break;
}
- return fetch(storeURL, {
+ return fetch(storeURL + query, {
method: 'GET',
headers: {
'X-Decky-Version': version.current,