import { For, Match, type ParentProps, Show, Switch, createContext, createEffect, createSignal, on, useContext } from 'solid-js'; import { A, type Params, Route, useSearchParams } from '@solidjs/router'; import { CalendarIcon, ChevronDownIcon, ClockRewindIcon, Download01Icon, File02Icon, HeartIcon, LinkExternal01Icon } from '@untitled-theme/icons-solid'; import type { Cluster, ManagedPackage, ManagedUser, ManagedVersion, Providers } from '@onelauncher/client/bindings'; import { getLicenseUrl, getPackageUrl, upperFirst } from '../../../utils'; import { bridge } from '~imports'; import useCommand from '~ui/hooks/useCommand'; import { abbreviateNumber, formatAsRelative } from '~utils'; import Tooltip from '~ui/components/base/Tooltip'; import Markdown from '~ui/components/content/Markdown'; import Button from '~ui/components/base/Button'; import Dropdown from '~ui/components/base/Dropdown'; import useBrowser from '~ui/hooks/useBrowser'; import SteveHead from '~assets/images/steve.png'; import usePromptOpener from '~ui/hooks/usePromptOpener'; import Link from '~ui/components/base/Link'; import usePagination from '~ui/hooks/usePagination'; import Spinner from '~ui/components/Spinner'; import AnimatedRoutes from '~ui/components/AnimatedRoutes'; interface BrowserModParams extends Params { id: string; provider: Providers; } const BrowserPackageContext = createContext(null); const basePath = '/browser/package'; function BrowserPackageProvider(props: ParentProps & { pkg: ManagedPackage }) { return ( // eslint-disable-next-line solid/reactivity -- Should be fine {props.children} ); } function BrowserPackageRoutes() { return ( <> {/* */} ); } function NavLink(props: ParentProps & { href: string }) { const [params] = useSearchParams(); const url = () => { const searchParams = new URLSearchParams(params as Record); return `${basePath}${props.href}?${searchParams.toString()}`; }; return ( ); } function BrowserPackage(props: ParentProps) { const [params] = useSearchParams(); const [pkg] = useCommand(() => bridge.commands.getProviderPackage(params.provider!, params.id!)); const [authors] = useCommand(pkg, () => bridge.commands.getProviderAuthors(pkg()!.provider, pkg()!.author)); const links = [ ['About', '/'], // ['Gallery', '/gallery'], ['Versions', '/versions'], ]; return (
{link => ( {link[0]} )}
{props.children}
)} />
); } BrowserPackage.buildUrl = function (params: BrowserModParams): string { return `${basePath}/?id=${params.id}&provider=${params.provider}`; }; BrowserPackage.Routes = BrowserPackageRoutes; export default BrowserPackage; function BrowserSidebar(props: { package: ManagedPackage; authors: ManagedUser[] }) { const createdAt = () => new Date(props.package.created); const updatedAt = () => new Date(props.package.updated); const promptOpen = usePromptOpener(); return (
{`Icon {`Icon

{props.package.title}

{props.package.package_type} {' '} on {' '} {props.package.provider}

{props.package.description}

{abbreviateNumber(props.package.downloads)}
{abbreviateNumber(props.package.followers)}

Links

{props.package.provider} {' '} Page

Authors

{author => ( <>
promptOpen(author.url)} > {`${author.username}'s
{author.username} Organization {author.role}
)}

Details

License {props.package.license?.name || props.package.license?.id || 'Unknown'}
Created {formatAsRelative(createdAt().getTime(), 'en', 'long')}
Last Updated {formatAsRelative(updatedAt().getTime(), 'en', 'long')}
); } function InstallButton(props: ManagedPackage) { const [selected, setSelected] = createSignal(0); const [clusters] = useCommand(() => bridge.commands.getClusters()); const controller = useBrowser(); const meetsRequirements = (cluster: Cluster) => { const game_version = props.game_versions.includes(cluster.meta.mc_version); const loader = props.loaders.includes(cluster.meta.loader || 'vanilla'); return game_version && loader; }; const filtered = () => clusters()?.filter(meetsRequirements); const getSelectedCluster = () => filtered()?.[selected()]; async function download() { const cluster = getSelectedCluster(); if (cluster === undefined) return; // TODO: Add a progress bar try { await bridge.commands.downloadProviderPackage( props.provider, props.id, cluster.uuid, cluster.meta.mc_version, cluster.meta.loader || null, null, // TODO: Specific package version ); await bridge.commands.syncClusterPackages(cluster.path || ''); } catch (err) { console.error(err); } } createEffect(() => { if (filtered() === undefined) return; const cluster = controller.cluster(); if (cluster !== undefined && meetsRequirements(cluster)) { const index = filtered()!.findIndex(c => c.uuid === cluster.uuid); if (index !== -1) setSelected(index); } }); return (
); } // Sub pages function BrowserPackageBody() { const context = useContext(BrowserPackageContext); return (
); } // function BrowserPackageGallery() { // // const context = useContext(BrowserPackageContext); // return ( //
//
Gallery
// {/* // {image => ( // {image.alt} // )} // */} //
// ); // } function BrowserPackageVersions() { const context = useContext(BrowserPackageContext); const MAX_ITEMS_PER_PAGE = 20; const { page, Navigation } = usePagination({ itemsCount: () => context?.versions.length || 0, itemsPerPage: () => MAX_ITEMS_PER_PAGE, }); const getVersionsForPage = (page: number) => { if (context === null) return []; const newestToOldest = context.versions.toReversed(); const start = (page - 1) * MAX_ITEMS_PER_PAGE; const end = start + MAX_ITEMS_PER_PAGE; return newestToOldest.slice(start, end); }; const [versions, { refetch }] = useCommand(context, async () => { if (context === null) return { error: 'No package context', status: 'error' }; const list = getVersionsForPage(page()); return await bridge.commands.getProviderPackageVersions(context.provider, list); }); let container!: HTMLDivElement; createEffect(on(() => page(), () => { refetch(); container.parentElement?.parentElement?.scrollIntoView({ behavior: 'smooth' }); })); return (

Versions - Page {' '} {page()}

{version => }
Name Game Version Loader Created Downloads
); } function colorForType(type: string) { switch (type) { case 'release': return 'bg-code-trace'; case 'snapshot': return 'bg-code-debug'; case 'beta': return 'bg-code-warn'; case 'alpha': return 'bg-code-error'; default: return 'bg-gray-05'; } } function VersionRow(props: ManagedVersion) { return (
{props.version_type.charAt(0).toUpperCase()}

{props.version_id}

{props.name}

3}> {props.game_versions.slice(-3).toReversed().join(', ')} ... {props.game_versions.join(', ')} {props.loaders.map(upperFirst).join(', ')} {formatAsRelative(new Date(props.published).getTime(), 'en', 'long')} {props.downloads.toLocaleString()}