diff --git a/apps/onelauncher/frontend/src/hooks/useBrowser.tsx b/apps/onelauncher/frontend/src/hooks/useBrowser.tsx index 51be9ff1..20ce789b 100644 --- a/apps/onelauncher/frontend/src/hooks/useBrowser.tsx +++ b/apps/onelauncher/frontend/src/hooks/useBrowser.tsx @@ -1,8 +1,9 @@ -import type { ClusterModel, Provider, SearchQuery } from '@/bindings.gen'; +import type { ClusterModel, Paginated, Provider, SearchQuery, SearchResult } from '@/bindings.gen'; +import type { UndefinedInitialDataOptions } from '@tanstack/react-query'; import { bindings } from '@/main'; import { PROVIDERS } from '@/utils'; import { useCommand } from '@onelauncher/common'; -import { createContext, useContext, useMemo, useState } from 'react'; +import { createContext, useContext, useEffect, useMemo, useState } from 'react'; export interface BrowserControllerType { cluster: ClusterModel | undefined; @@ -52,6 +53,7 @@ export function BrowserProvider(props: any) { ); } -export function useBrowserSearch(provider: Provider, query: SearchQuery) { - return useCommand('searchPackages', () => bindings.core.searchPackages(provider, query)); +export function useBrowserSearch(provider: Provider, query: SearchQuery, options?: Omit>, 'queryKey' | 'queryFn'> | undefined) { + const validFilters = useMemo(() => Object.values(query.filters ?? {}).filter(a => a).length > 0, [query.filters]); + return useCommand('searchPackages', () => bindings.core.searchPackages(provider, validFilters ? query : { ...query, filters: null }), options); } diff --git a/apps/onelauncher/frontend/src/routes/app/browser/index.tsx b/apps/onelauncher/frontend/src/routes/app/browser/index.tsx index 43cc0b81..dd8bedc8 100644 --- a/apps/onelauncher/frontend/src/routes/app/browser/index.tsx +++ b/apps/onelauncher/frontend/src/routes/app/browser/index.tsx @@ -1,8 +1,11 @@ +import type { Provider, SearchResult } from '@/bindings.gen'; import OneConfigLogo from '@/assets/logos/oneconfig.svg'; import { useBrowserContext, useBrowserSearch } from '@/hooks/useBrowser'; +import { abbreviateNumber } from '@/utils'; import { Button, Show } from '@onelauncher/common/components'; import { createFileRoute, useSearch } from '@tanstack/react-router'; -import { ChevronRightIcon } from '@untitled-theme/icons-react'; +import { ChevronRightIcon, Download01Icon, HeartIcon } from '@untitled-theme/icons-react'; +import { useEffect } from 'react'; import { BrowserLayout } from './route'; export const Route = createFileRoute('/app/browser/')({ @@ -14,6 +17,7 @@ function RouteComponent() {
+
); @@ -21,9 +25,8 @@ function RouteComponent() { function Featured() { const context = useBrowserContext(); - const search = useBrowserSearch(context.provider, context.query); return ( - + 0)}>
Featured
@@ -50,13 +53,90 @@ function Featured() {
- {search.status} - {search.isSuccess && ( - <> - {search.data.items.map(item =>

{item.name}

)} - - )}
); } + +function Search() { + const context = useBrowserContext(); + const search = useBrowserSearch(context.provider, context.query); + useEffect(() => { + search.refetch(); + }, [context.provider, context.query]); + + return ( +
+ + {search.data?.items.map(item => ( + + ))} + +
+ ); +} + +function PackageItem(props: SearchResult & { provider: Provider }) { + function redirect() { + } + + return ( +
+
+ + )} + when={props.icon_url} + > + {`Icon + {`Icon + +
+
+
+

{props.title}

+

+ By + {' '} + {props.author} + {' '} + on + {' '} + {props.provider} +

+
+ +

{props.description}

+ +
+ +
+ + {abbreviateNumber(props.downloads)} +
+ + {/* 0}> +
+ + {abbreviateNumber(props.follows)} +
+
*/} +
+
+
+
+ ); +} diff --git a/apps/onelauncher/frontend/src/routes/app/browser/route.tsx b/apps/onelauncher/frontend/src/routes/app/browser/route.tsx index b15a71d6..b17d938b 100644 --- a/apps/onelauncher/frontend/src/routes/app/browser/route.tsx +++ b/apps/onelauncher/frontend/src/routes/app/browser/route.tsx @@ -7,6 +7,7 @@ import { browserCategories } from '@/utils/browser'; import { Dropdown, Show, TextField } from '@onelauncher/common/components'; import { createFileRoute, Outlet } from '@tanstack/react-router'; import { SearchMdIcon } from '@untitled-theme/icons-react'; +import { useEffect } from 'react'; export const Route = createFileRoute('/app/browser')({ component: RouteComponent, @@ -51,11 +52,16 @@ export function BrowserLayout(props: any) { function BrowserSidebar() { const context = useBrowserContext(); const clusters = useClusters(); + useEffect(() => { + const game_versions = context.cluster?.mc_version ? [context.cluster.mc_version] : null; + const loaders = context.cluster?.mc_loader ? [context.cluster.mc_loader] : null; + context.setQuery({ ...context.query, filters: { ...defaultFilters, ...context.query.filters, game_versions, loaders } }); + }, [context.cluster]); return (
-
Active Cluster
+
Active Cluster
{ const cluster = clusters?.find(cluster => cluster.id.toString() === id); @@ -71,7 +77,7 @@ function BrowserSidebar() {
-
Provider
+
Provider
context.setProvider(id as typeof context.provider)} selectedKey={context.provider}> {PROVIDERS.map(provider => ( @@ -88,22 +94,22 @@ function BrowserSidebar() { ); } -const defaultFilters:Filters = { +const defaultFilters: Filters = { categories: null, game_versions: null, loaders: null, - package_types: null -} + package_types: null, +}; function BrowserCategories() { const context = useBrowserContext(); const categories = browserCategories.byPackageType('mod', context.provider); - function switchCategory(category:string){ - if(context.query.filters?.categories?.includes(category)) - context.setQuery({...context.query, filters: {...defaultFilters, ...context.query.filters, categories: (context.query.filters?.categories ?? []).filter(cat=>cat!==category)}}) - else - context.setQuery({...context.query, filters: {...defaultFilters, ...context.query.filters, categories: [...(context.query.filters?.categories ?? []), category]}}) + function switchCategory(category: string) { + const newCategories = context.query.filters?.categories?.includes(category) + ? context.query.filters.categories.filter(cat => cat !== category) + : [...(context.query.filters?.categories ?? []), category]; + context.setQuery({ ...context.query, filters: { ...defaultFilters, ...context.query.filters, categories: newCategories.length > 0 ? newCategories : null } }); } return ( @@ -112,13 +118,13 @@ function BrowserCategories() {
-
Categories
+
Categories
{categories.map(category => (

switchCategory(category.id)} + onClick={() => switchCategory(category.id)} > {category.display}

@@ -131,6 +137,7 @@ function BrowserCategories() { } function BrowserToolbar() { + const context = useBrowserContext(); return (
@@ -138,6 +145,12 @@ function BrowserToolbar() {
} + onKeyDown={(e) => { + if (e.key !== 'Enter') + return; + e.preventDefault(); + context.setQuery({ ...context.query, query: e.currentTarget.value }); + }} placeholder="Search for content" />
diff --git a/packages/core/src/api/packages/provider/modrinth.rs b/packages/core/src/api/packages/provider/modrinth.rs index 2712b28c..e1bcb77f 100644 --- a/packages/core/src/api/packages/provider/modrinth.rs +++ b/packages/core/src/api/packages/provider/modrinth.rs @@ -70,11 +70,11 @@ impl ProviderExt for ModrinthProviderImpl { if let Some(loaders) = &filters.loaders { for loader in loaders { - builder.and(Facet("loaders".to_string(), FacetOperation::Eq, loader.to_string())); + builder.and(Facet("categories".to_string(), FacetOperation::Eq, loader.to_string())); } } - params.append_pair("facets", &builder.build()); + params.append_pair("facets", &format!("[{}]", &builder.build())); } if let Some(sort) = &query.sort {