diff options
| author | Party Wumpus <48649272+PartyWumpus@users.noreply.github.com> | 2024-02-07 17:38:08 +0000 |
|---|---|---|
| committer | AAGaming <aagaming@riseup.net> | 2024-02-14 16:43:30 -0500 |
| commit | ec41c6121956afc4eb33c9f21905cdce4c07a097 (patch) | |
| tree | 6310daec76d70fbbd78fb765b36adf2b0fde1ce5 /frontend | |
| parent | 21c7742f9a5ad499a95c6b54644d5494d5536967 (diff) | |
| download | decky-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.tsx | 137 | ||||
| -rw-r--r-- | frontend/src/store.tsx | 26 |
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, |
