diff options
| author | EMERALD <hudson.samuels@gmail.com> | 2023-01-19 20:00:42 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-01-19 18:00:42 -0800 |
| commit | 3ebaac6752cb2b13ee5bfb6274cd7ae60b0d6bcb (patch) | |
| tree | 70b18b6f9ec2b2e23d2eef374e106a8870e794e8 /frontend/src/components/store/Store.tsx | |
| parent | cbbd56486070eab9a08253b2778fcd64877acd68 (diff) | |
| download | decky-loader-3ebaac6752cb2b13ee5bfb6274cd7ae60b0d6bcb.tar.gz decky-loader-3ebaac6752cb2b13ee5bfb6274cd7ae60b0d6bcb.zip | |
Store and plugin installation visual improvements (#343)v2.5.0-pre1
* Redesign store, add comments for filtering
* Improve installation/uninstallation modals
* Fix store comment to be easier to fix
* Add source code info to about page
Diffstat (limited to 'frontend/src/components/store/Store.tsx')
| -rw-r--r-- | frontend/src/components/store/Store.tsx | 224 |
1 files changed, 206 insertions, 18 deletions
diff --git a/frontend/src/components/store/Store.tsx b/frontend/src/components/store/Store.tsx index 2ffd8efb..7a9c0e33 100644 --- a/frontend/src/components/store/Store.tsx +++ b/frontend/src/components/store/Store.tsx @@ -1,6 +1,16 @@ -import { SteamSpinner } from 'decky-frontend-lib'; -import { FC, useEffect, useState } from 'react'; +import { + Dropdown, + DropdownOption, + Focusable, + PanelSectionRow, + SteamSpinner, + Tabs, + TextField, + findModule, +} from 'decky-frontend-lib'; +import { FC, useEffect, useMemo, useState } from 'react'; +import logo from '../../../assets/plugin_store.png'; import Logger from '../../logger'; import { StorePlugin, getPluginList } from '../../store'; import PluginCard from './PluginCard'; @@ -8,7 +18,12 @@ import PluginCard from './PluginCard'; const logger = new Logger('FilePicker'); const StorePage: FC<{}> = () => { + const [currentTabRoute, setCurrentTabRoute] = useState<string>('browse'); const [data, setData] = useState<StorePlugin[] | null>(null); + const { TabCount } = findModule((m) => { + if (m?.TabCount && m?.TabTitle) return true; + return false; + }); useEffect(() => { (async () => { @@ -19,19 +34,12 @@ const StorePage: FC<{}> = () => { }, []); return ( - <div - style={{ - marginTop: '40px', - height: 'calc( 100% - 40px )', - overflowY: 'scroll', - }} - > + <> <div style={{ - display: 'flex', - flexWrap: 'nowrap', - flexDirection: 'column', - height: '100%', + marginTop: '40px', + height: 'calc( 100% - 40px )', + background: '#0005', }} > {!data ? ( @@ -39,13 +47,193 @@ const StorePage: FC<{}> = () => { <SteamSpinner /> </div> ) : ( - <div> - {data.map((plugin: StorePlugin) => ( - <PluginCard plugin={plugin} /> - ))} - </div> + <Tabs + activeTab={currentTabRoute} + onShowTab={(tabId: string) => { + setCurrentTabRoute(tabId); + }} + tabs={[ + { + title: 'Browse', + content: <BrowseTab children={{ data: data }} />, + id: 'browse', + renderTabAddon: () => <span className={TabCount}>{data.length}</span>, + }, + { + title: 'About', + content: <AboutTab />, + id: 'about', + }, + ]} + /> )} </div> + </> + ); +}; + +const BrowseTab: FC<{ children: { data: StorePlugin[] } }> = (data) => { + const sortOptions = useMemo( + (): DropdownOption[] => [ + { data: 1, label: 'Alphabetical (A to Z)' }, + { data: 2, label: 'Alphabetical (Z to A)' }, + ], + [], + ); + + // const filterOptions = useMemo((): DropdownOption[] => [{ data: 1, label: 'All' }], []); + + const [selectedSort, setSort] = useState<number>(sortOptions[0].data); + // const [selectedFilter, setFilter] = useState<number>(filterOptions[0].data); + const [searchFieldValue, setSearchValue] = useState<string>(''); + + return ( + <> + <style>{` + .deckyStoreCardInstallContainer > .Panel { + padding: 0; + } + `}</style> + {/* This should be used once filtering is added + + <PanelSectionRow> + <Focusable style={{ display: 'flex', maxWidth: '100%' }}> + <div + style={{ + display: 'flex', + flexDirection: 'column', + width: '47.5%', + }} + > + <span className="DialogLabel">Sort</span> + <Dropdown + menuLabel="Sort" + rgOptions={sortOptions} + strDefaultLabel="Last Updated (Newest)" + selectedOption={selectedSort} + onChange={(e) => setSort(e.data)} + /> + </div> + <div + style={{ + display: 'flex', + flexDirection: 'column', + width: '47.5%', + marginLeft: 'auto', + }} + > + <span className="DialogLabel">Filter</span> + <Dropdown + menuLabel="Filter" + rgOptions={filterOptions} + strDefaultLabel="All" + selectedOption={selectedFilter} + onChange={(e) => setFilter(e.data)} + /> + </div> + </Focusable> + </PanelSectionRow> + <div style={{ justifyContent: 'center', display: 'flex' }}> + <Focusable style={{ display: 'flex', alignItems: 'center', width: '96%' }}> + <div style={{ width: '100%' }}> + <TextField label="Search" value={searchFieldValue} onChange={(e) => setSearchValue(e.target.value)} /> + </div> + </Focusable> + </div> + */} + <PanelSectionRow> + <Focusable style={{ display: 'flex', maxWidth: '100%' }}> + <div + style={{ + display: 'flex', + flexDirection: 'column', + minWidth: '100%', + maxWidth: '100%', + }} + > + <span className="DialogLabel">Sort</span> + <Dropdown + menuLabel="Sort" + rgOptions={sortOptions} + strDefaultLabel="Last Updated (Newest)" + selectedOption={selectedSort} + onChange={(e) => setSort(e.data)} + /> + </div> + </Focusable> + </PanelSectionRow> + <div style={{ justifyContent: 'center', display: 'flex' }}> + <Focusable style={{ display: 'flex', alignItems: 'center', width: '96%' }}> + <div style={{ width: '100%' }}> + <TextField label="Search" value={searchFieldValue} onChange={(e) => setSearchValue(e.target.value)} /> + </div> + </Focusable> + </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} /> + ))} + </div> + </> + ); +}; + +const AboutTab: FC<{}> = () => { + return ( + <div + style={{ + display: 'flex', + flexDirection: 'column', + }} + > + <style>{` + .deckyStoreAboutHeader { + font-size: 24px; + font-weight: 600; + margin-top: 20px; + } + `}</style> + <img + src={logo} + style={{ + width: '256px', + height: 'auto', + alignSelf: 'center', + }} + /> + <span className="deckyStoreAboutHeader">Testing</span> + <span> + Please consider testing new plugins to help the Decky Loader team!{' '} + <a + href="https://deckbrew.xyz/testing" + target="_blank" + style={{ + textDecoration: 'none', + }} + > + deckbrew.xyz/testing + </a> + </span> + <span className="deckyStoreAboutHeader">Contributing</span> + <span> + If you would like to contribute to the Decky Plugin Store, check the SteamDeckHomebrew/decky-plugin-template + repository on GitHub. Information on development and distribution is available in the README. + </span> + <span className="deckyStoreAboutHeader">Source Code</span> + <span>All plugin source code is available on SteamDeckHomebrew/decky-plugin-database repository on GitHub.</span> </div> ); }; |
