import { useNavigate } from '@solidjs/router'; import { type Accessor, type Context, For, type ParentProps, type Setter, Show, createContext, createEffect, createSignal, onMount, untrack, useContext } from 'solid-js'; import type { Cluster, ManagedPackage, PackageType, ProviderSearchQuery, ProviderSearchResults, Providers } from '@onelauncher/client/bindings'; import useCommand, { tryResult } from './useCommand'; import { useRecentCluster } from './useCluster'; import { bridge } from '~imports'; import BrowserPackage from '~ui/pages/browser/BrowserPackage'; import type { ModalProps } from '~ui/components/overlay/Modal'; import Modal, { createModal } from '~ui/components/overlay/Modal'; import Dropdown from '~ui/components/base/Dropdown'; import Button from '~ui/components/base/Button'; interface BrowserControllerType { cluster: Accessor; setCluster: Setter; displayBrowser: (cluster?: Cluster | undefined) => void; displayPackage: (id: string, provider: Providers) => void; displayCategory: (category: string) => void; displayClusterSelector: () => void; search: () => void; searchQuery: Accessor; setSearchQuery: Setter; packageType: Accessor; setPackageType: Setter; refreshCache: () => void; cache: Accessor; featured: Accessor; }; const BrowserContext = createContext() as Context; export function BrowserProvider(props: ParentProps) { const [mainPageCache, setMainPageCache] = createSignal(); const [featured, setFeatured] = createSignal(); // Used for the current "Browser Mode". It'll only show packages of the selected type const [packageType, setPackageType] = createSignal('mod'); // Active cluster that results should be "focused" on const [cluster, setCluster] = createSignal(); const [searchOptions, setSearchOptions] = createSignal({ query: '', limit: 20, categories: null, game_versions: null, loaders: null, package_types: ['mod'], open_source: null, }); const navigate = useNavigate(); const recentCluster = useRecentCluster(); const modal = createModal(props => ( )); const controller: BrowserControllerType = { cluster, setCluster, displayBrowser(cluster?: Cluster) { setCluster(cluster); }, displayPackage(id: string, provider: Providers) { navigate(BrowserPackage.buildUrl({ id, provider })); }, displayCategory(_category: string) { }, displayClusterSelector() { modal.show(); }, search: () => { navigate('/browser/search'); }, searchQuery: searchOptions, setSearchQuery: setSearchOptions, packageType, setPackageType, async refreshCache() { const opts = untrack(searchOptions); const res = await tryResult(() => bridge.commands.searchProviderPackages('Modrinth', opts)); setMainPageCache(res); // TODO: Better algorithm for selecting a featured package const firstPackage = res.results[0]; if (firstPackage !== undefined) { const featuredPackage = await tryResult(() => bridge.commands.getProviderPackage('Modrinth', firstPackage.project_id)); setFeatured(featuredPackage); } }, cache: mainPageCache, featured, }; onMount(() => { if (mainPageCache() === undefined) controller.refreshCache(); }); createEffect(() => { setCluster(recentCluster()); }); return ( {props.children} ); } export default function useBrowser() { const controller = useContext(BrowserContext); if (!controller) throw new Error('useBrowserController should be called inside its BrowserProvider'); return controller; } function ChooseClusterModal(props: ModalProps) { const [selected, setSelected] = createSignal(0); const [clusters] = useCommand(() => bridge.commands.getClusters()); const controller = useBrowser(); const currentlySelectedCluster = (list: Cluster[]) => { const cluster = controller.cluster(); if (cluster === undefined) return 0; if (list === undefined) return 0; const index = list.findIndex(c => c.uuid === cluster.uuid) || 0; if (index === -1) return 0; return index; }; createEffect(() => { setSelected(currentlySelectedCluster(clusters() || [])); }); function chooseCluster() { const index = untrack(selected); const clusterz = untrack(clusters); if (clusterz !== undefined && index !== undefined) controller.setCluster(clusterz[index]); props.hide(); } return ( Loading...} > {cluster => ( {cluster.meta.name} )} )} buttons={[