From 17033187034e80c18988f358e25c49ae217a1632 Mon Sep 17 00:00:00 2001 From: Szedann <65607498+Szedann@users.noreply.github.com> Date: Sun, 20 Jul 2025 16:51:07 +0200 Subject: [PATCH] feat: featured section WIP --- .../frontend/src/hooks/useBrowser.tsx | 20 +- .../frontend/src/routes/app/browser/index.tsx | 24 ++- .../app/browser/package.$provider.$slug.tsx | 188 +++++++++--------- 3 files changed, 116 insertions(+), 116 deletions(-) diff --git a/apps/onelauncher/frontend/src/hooks/useBrowser.tsx b/apps/onelauncher/frontend/src/hooks/useBrowser.tsx index 522af87b..1e2ab881 100644 --- a/apps/onelauncher/frontend/src/hooks/useBrowser.tsx +++ b/apps/onelauncher/frontend/src/hooks/useBrowser.tsx @@ -3,8 +3,8 @@ import type { UndefinedInitialDataOptions } from '@tanstack/react-query'; import { bindings } from '@/main'; import { PROVIDERS } from '@/utils'; import { useCommand } from '@onelauncher/common'; -import { createContext, useContext, useEffect, useMemo, useState } from 'react'; import { useNavigate } from '@tanstack/react-router'; +import { createContext, useContext, useEffect, useMemo, useState } from 'react'; export interface BrowserControllerType { cluster: ClusterModel | undefined; @@ -29,7 +29,7 @@ export function BrowserProvider(props: any) { const { children } = props; const [cluster, setCluster] = useState(undefined); const [provider, setProvider] = useState(PROVIDERS[0]); - const navigate = useNavigate() + const navigate = useNavigate(); const [query, setQuery] = useState({ filters: null, query: null, @@ -38,10 +38,10 @@ export function BrowserProvider(props: any) { sort: null, }); - useEffect(()=>{ - if(query.filters?.categories || query.query) - navigate({to: "/app/browser/search"}) - },[query]) + useEffect(() => { + if (query.filters?.categories || query.query) + navigate({ to: '/app/browser/search' }); + }, [query]); const context = useMemo(() => ({ cluster, @@ -65,10 +65,10 @@ export function useBrowserSearch(provider: Provider, query: SearchQuery, options return useCommand('searchPackages', () => bindings.core.searchPackages(provider, validFilters ? query : { ...query, filters: null }), options); } -export function usePackageData(provider: Provider, slug: string, options?: Omit, "queryKey" | "queryFn"> | undefined){ - return useCommand("getPackage", () => bindings.core.getPackage(provider, slug), options) +export function usePackageData(provider: Provider, slug: string, options?: Omit, 'queryKey' | 'queryFn'> | undefined) { + return useCommand('getPackage', () => bindings.core.getPackage(provider, slug), options); } -export function usePackageVersions(provider: Provider, slug: string, {mc_versions, loaders, offset, limit, ...options}:{mc_versions?:Array|null, loaders?:Array|null, offset?:number, limit: number} & Omit>, "queryKey" | "queryFn">){ - return useCommand("getPackageVersions", ()=>bindings.core.getPackageVersions(provider, slug, mc_versions??null, loaders??null, (offset??0) as unknown as bigint, limit as unknown as bigint), options) +export function usePackageVersions(provider: Provider, slug: string, { mc_versions, loaders, offset, limit, ...options }: { mc_versions?: Array | null; loaders?: Array | null; offset?: number; limit: number } & Omit>, 'queryKey' | 'queryFn'>) { + return useCommand('getPackageVersions', () => bindings.core.getPackageVersions(provider, slug, mc_versions ?? null, loaders ?? null, (offset ?? 0) as unknown as bigint, limit as unknown as bigint), options); } diff --git a/apps/onelauncher/frontend/src/routes/app/browser/index.tsx b/apps/onelauncher/frontend/src/routes/app/browser/index.tsx index 9ca0ce77..dad33499 100644 --- a/apps/onelauncher/frontend/src/routes/app/browser/index.tsx +++ b/apps/onelauncher/frontend/src/routes/app/browser/index.tsx @@ -1,16 +1,16 @@ import type { Provider } from '@/bindings.gen'; import OneConfigLogo from '@/assets/logos/oneconfig.svg'; import { PackageGrid } from '@/components/content/PackageItem'; -import { useBrowserContext, useBrowserSearch } from '@/hooks/useBrowser'; +import { useBrowserSearch, usePackageData } from '@/hooks/useBrowser'; import { PROVIDERS } from '@/utils'; import { Button, Show } from '@onelauncher/common/components'; -import { createFileRoute } from '@tanstack/react-router'; +import { createFileRoute, useNavigate } from '@tanstack/react-router'; import { ChevronRightIcon } from '@untitled-theme/icons-react'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import { BrowserLayout } from './route'; export const Route = createFileRoute('/app/browser/')({ - component: RouteComponent + component: RouteComponent, }); @@ -26,17 +26,21 @@ function RouteComponent() { } function Featured() { - const context = useBrowserContext(); + // const context = useBrowserContext(); + const [provider, slug] = useMemo<[Provider, string]>(() => ['Modrinth', 'sodium'], []); + const featuredPackage = usePackageData(provider, slug); + const navigate = useNavigate(); + return ( - 0)}> +
Featured
- thumbnail + thumbnail
-

Sodium

+

{featuredPackage.data?.name}

@@ -45,10 +49,10 @@ function Featured() {
-

Peak mod

+

{featuredPackage.data?.short_desc}

- diff --git a/apps/onelauncher/frontend/src/routes/app/browser/package.$provider.$slug.tsx b/apps/onelauncher/frontend/src/routes/app/browser/package.$provider.$slug.tsx index e3d3f84b..8d10bcd8 100644 --- a/apps/onelauncher/frontend/src/routes/app/browser/package.$provider.$slug.tsx +++ b/apps/onelauncher/frontend/src/routes/app/browser/package.$provider.$slug.tsx @@ -1,45 +1,45 @@ -import type { ClusterModel, ManagedPackage, ManagedUser, ManagedVersion, Paginated, Provider } from '@/bindings.gen' -import { useBrowserContext, usePackageData, usePackageVersions } from '@/hooks/useBrowser' -import { useClusters } from '@/hooks/useCluster' -import { bindings } from '@/main' -import { abbreviateNumber, formatAsRelative, PROVIDERS } from '@/utils' -import { useCommand } from '@onelauncher/common' -import { Show, Tooltip, Button, Dropdown } from '@onelauncher/common/components' -import { createFileRoute, Link } from '@tanstack/react-router' -import { openUrl } from '@tauri-apps/plugin-opener' -import { Download01Icon, LinkExternal01Icon, File02Icon, CalendarIcon, ClockRewindIcon, ChevronDownIcon, ChevronUpIcon } from '@untitled-theme/icons-react' -import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react' -import { Collection, ListBox, ListBoxItem, Popover, Select } from 'react-aria-components' - +import type { ClusterModel, ManagedPackage, ManagedUser, ManagedVersion, Paginated, Provider } from '@/bindings.gen'; +import { useBrowserContext, usePackageData, usePackageVersions } from '@/hooks/useBrowser'; +import { useClusters } from '@/hooks/useCluster'; +import { bindings } from '@/main'; +import { abbreviateNumber, formatAsRelative, PROVIDERS } from '@/utils'; +import { useCommand } from '@onelauncher/common'; +import { Button, Show, Tooltip } from '@onelauncher/common/components'; +import { createFileRoute, Link } from '@tanstack/react-router'; +import { openUrl } from '@tauri-apps/plugin-opener'; +import { CalendarIcon, ChevronDownIcon, ChevronUpIcon, ClockRewindIcon, Download01Icon, File02Icon, LinkExternal01Icon } from '@untitled-theme/icons-react'; +import { createContext, useContext, useMemo, useRef, useState } from 'react'; +import { Collection, ListBox, ListBoxItem, Popover, Select } from 'react-aria-components'; export const Route = createFileRoute('/app/browser/package/$provider/$slug')({ component: RouteComponent, -}) +}); -function includes(list: { includes: (arg0: C) => boolean }, element: T): element is C { - return list.includes(element as unknown as C) +function includes(list: { includes: (arg0: TArray) => boolean }, element: T): element is TArray { + return list.includes(element as unknown as TArray); } -type PackageContextType = { - pkg: ManagedPackage | undefined - versions: Paginated | undefined +interface PackageContextType { + pkg: ManagedPackage | undefined; + versions: Paginated | undefined; } const PackageContext = createContext({ pkg: undefined, - versions: undefined -}) + versions: undefined, +}); function RouteComponent() { - const { provider, slug } = Route.useParams() - if (!includes(PROVIDERS, provider)) throw new Error("Invalid provider"); - const packageData = usePackageData(provider, slug) - const browserContext = useBrowserContext() + const { provider, slug } = Route.useParams(); + if (!includes(PROVIDERS, provider)) + throw new Error('Invalid provider'); + const packageData = usePackageData(provider, slug); + const browserContext = useBrowserContext(); const { data: versions } = usePackageVersions(provider, slug, { mc_versions: browserContext.cluster ? [browserContext.cluster.mc_version] : null, loaders: browserContext.cluster ? [browserContext.cluster.mc_loader] : null, - limit: 50 - }) + limit: 50, + }); return ( @@ -49,9 +49,8 @@ function RouteComponent() {
- About - Versions - + About + Versions
@@ -62,17 +61,16 @@ function RouteComponent() {
- ) + ); } - function BrowserSidebar({ package: pkg }: { package: ManagedPackage }) { - const { provider } = Route.useParams() + const { provider } = Route.useParams(); const createdAt = useMemo(() => pkg.created ? new Date(pkg.created) : null, []); const updatedAt = useMemo(() => pkg.updated ? new Date(pkg.updated) : null, []); - const authors = useCommand("getUsersFromAuthor", () => bindings.core.getUsersFromAuthor(provider as Provider, pkg.author)) + const authors = useCommand('getUsersFromAuthor', () => bindings.core.getUsersFromAuthor(provider as Provider, pkg.author)); return (
@@ -106,7 +104,7 @@ function BrowserSidebar({ package: pkg }: { package: ManagedPackage }) {
- + {/*

Links

@@ -121,7 +119,7 @@ function BrowserSidebar({ package: pkg }: { package: ManagedPackage }) {

Authors

{ authors.isSuccess - ? authors.data.map(author => ) + ? authors.data.map(author => ) :

Loading...

} @@ -133,7 +131,7 @@ function BrowserSidebar({ package: pkg }: { package: ManagedPackage }) {
License - + {pkg.license?.name || pkg.license?.id || 'Unknown'}
@@ -168,60 +166,50 @@ function BrowserSidebar({ package: pkg }: { package: ManagedPackage }) { ); } - -function colorForType(type: string) { - switch (type.toLowerCase()) { - 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-border/05'; - } -} - -function InstallButton({ ...pkg }: ManagedPackage) { - const triggerRef = useRef(null) - const { provider, slug } = Route.useParams() - const [open, setOpen] = useState(false) - const clusters = useClusters() - const browserContext = useBrowserContext() - const { versions } = useContext(PackageContext) +function InstallButton() { + const triggerRef = useRef(null); + const { provider } = Route.useParams(); + const [open, setOpen] = useState(false); + const clusters = useClusters(); + const browserContext = useBrowserContext(); + const { versions } = useContext(PackageContext); const version = useMemo(() => { - if (!versions || !browserContext.cluster) return undefined; + if (!versions || !browserContext.cluster) + return undefined; return versions.items.findLast(version => version.mc_versions.includes(browserContext.cluster!.mc_version) - && version.loaders.includes(browserContext.cluster!.mc_loader) - ) - }, [browserContext.cluster, versions]) + && version.loaders.includes(browserContext.cluster!.mc_loader)); + }, [browserContext.cluster, versions]); function download() { - if (!version || !browserContext.cluster || !includes(PROVIDERS, provider)) return false; - downloadPackage(browserContext.cluster, provider, version) + if (!version || !browserContext.cluster || !includes(PROVIDERS, provider)) + return false; + downloadPackage(browserContext.cluster, provider, version); } - return (
@@ -229,42 +217,50 @@ function InstallButton({ ...pkg }: ManagedPackage) { - { + const cluster = clusters?.find(cluster => cluster.id as unknown as number === e); + if (cluster) + browserContext.setCluster(cluster); + }} + > +
- + - {item => {item.name}} + {item => ( + + {item.name} + + )}
- - ) + ); } - function Author({ author }: { author: ManagedUser }) { - - //TODO: onclick and fallback avatar url + // TODO: onclick and fallback avatar url return ( { if (author.url) openUrl(author.url) }} + onClick={() => { + if (author.url) + openUrl(author.url); + }} > - {`${author.username}'s + {`${author.username}'s
{author.username} @@ -279,9 +275,9 @@ function Author({ author }: { author: ManagedUser }) { - ) + ); } function downloadPackage(cluster: ClusterModel, provider: Provider, version: ManagedVersion, skipCompatibility = false) { - return bindings.core.downloadPackage(provider, version.project_id, version.version_id, cluster.id, skipCompatibility) + return bindings.core.downloadPackage(provider, version.project_id, version.version_id, cluster.id, skipCompatibility); }