fix: browser main page

This commit is contained in:
Szedann 2025-07-17 20:28:29 +02:00
parent 292e7df7f8
commit 85443ee5a9
4 changed files with 141 additions and 79 deletions

View file

@ -0,0 +1,72 @@
import type { Provider, SearchResult } from '@/bindings.gen';
import { abbreviateNumber } from '@/utils';
import { Show } from '@onelauncher/common/components';
import { Download01Icon } from '@untitled-theme/icons-react';
export function PackageGrid({ items, provider }: { items: Array<SearchResult>; provider: Provider }) {
return (
<div className="grid grid-cols-2 xl:grid-cols-3 gap-2">
{items.map(item => (
<PackageItem key={item.project_id} {...item} provider={provider} />
))}
</div>
);
}
export function PackageItem({ provider, ...item }: SearchResult & { provider: Provider }) {
function redirect() {
}
return (
<div
className="h-full min-w-50 flex overflow-hidden rounded-lg bg-component-bg hover:bg-component-bg-hover flex-col max-h-74 min-h-74"
onClick={redirect}
tabIndex={0}
>
<div
className="relative flex items-center justify-center overflow-hidden w-full h-28"
>
<Show
fallback={(
<div
className="aspect-ratio-square rounded-md bg-border/05 w-2/5"
/>
)}
when={item.icon_url}
>
<img alt={`Icon for ${item.title}`} className="absolute z-0 max-w-none w-7/6 opacity-50 filter-blur-xl" src={item.icon_url} />
<img
alt={`Icon for ${item.title}`}
className="relative z-1 aspect-ratio-square rounded-md image-render-auto w-2/5"
src={item.icon_url}
/>
</Show>
</div>
<div className="flex flex-1 flex-col gap-2 p-3">
<div className="flex flex-col gap-2">
<h4 className="text-fg-primary font-medium line-height-normal">{item.title}</h4>
<p className="text-xs text-fg-secondary">
By
{' '}
<span className="text-fg-primary">{item.author}</span>
{' '}
on
{' '}
<span className="text-fg-primary">{provider}</span>
</p>
</div>
<p className="max-h-22 flex-1 overflow-hidden text-sm text-fg-secondary line-height-snug">{item.description}</p>
<div className="flex flex-row gap-4 text-xs">
<Show when={provider !== 'SkyClient'}>
<div className="flex flex-row items-center gap-2">
<Download01Icon className="h-4 w-4" />
{abbreviateNumber(item.downloads)}
</div>
</Show>
</div>
</div>
</div>
);
}

View file

@ -1,15 +1,17 @@
import type { Provider, SearchResult } from '@/bindings.gen';
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 { abbreviateNumber } from '@/utils';
import { PROVIDERS } from '@/utils';
import { Button, Show } from '@onelauncher/common/components';
import { createFileRoute, useSearch } from '@tanstack/react-router';
import { ChevronRightIcon, Download01Icon, HeartIcon } from '@untitled-theme/icons-react';
import { useEffect } from 'react';
import { createFileRoute } from '@tanstack/react-router';
import { ChevronRightIcon } from '@untitled-theme/icons-react';
import { useState } from 'react';
import { BrowserLayout } from './route';
export const Route = createFileRoute('/app/browser/')({
component: RouteComponent,
component: RouteComponent
});
function RouteComponent() {
@ -17,7 +19,7 @@ function RouteComponent() {
<BrowserLayout>
<div className="flex flex-col gap-8">
<Featured />
<Search />
<Lists />
</div>
</BrowserLayout>
);
@ -58,85 +60,38 @@ function Featured() {
);
}
function Search() {
const context = useBrowserContext();
const search = useBrowserSearch(context.provider, context.query);
useEffect(() => {
search.refetch();
}, [context.provider, context.query]);
function Lists() {
return (
<div className="grid grid-cols-2 xl:grid-cols-3 gap-2">
<Show when={search.isSuccess}>
{search.data?.items.map(item => (
<PackageItem key={item.project_id} {...item} provider={context.provider} />
))}
</Show>
<div>
{PROVIDERS.map(provider => (
<List key={provider} provider={provider} />
))}
</div>
);
}
function PackageItem(props: SearchResult & { provider: Provider }) {
function redirect() {
}
function List({ provider }: { provider: Provider }) {
const search = useBrowserSearch(provider, {
filters: null,
limit: 18 as unknown as bigint,
offset: null,
query: null,
sort: null,
});
const [expanded, setExpanded] = useState(false);
return (
<div
className="h-full min-w-50 flex overflow-hidden rounded-lg bg-component-bg hover:bg-component-bg-hover flex-col max-h-74 min-h-74"
onClick={redirect}
tabIndex={0}
>
<div
className="relative flex items-center justify-center overflow-hidden w-full h-28"
>
<Show
fallback={(
<div
className="aspect-ratio-square rounded-md bg-border/05 w-2/5"
/>
)}
when={props.icon_url}
>
<img alt={`Icon for ${props.title}`} className="absolute z-0 max-w-none w-7/6 opacity-50 filter-blur-xl" src={props.icon_url} />
<img
alt={`Icon for ${props.title}`}
className="relative z-1 aspect-ratio-square rounded-md image-render-auto w-2/5"
src={props.icon_url}
/>
<div>
<h3 className="uppercase p-3 opacity-60">{provider}</h3>
<div className={`relative overflow-hidden ${expanded ? '' : 'h-128'}`}>
{search.isSuccess
? <PackageGrid items={search.data.items} provider={provider} />
: <h3>Loading...</h3>}
<Show when={!expanded}>
<div className="absolute w-full bottom-0 flex justify-center p-10 left-0 right-0 z-10 bg-gradient-to-b from-transparent to-page to-20%">
<Button color="secondary" onClick={() => setExpanded(true)}>Show More</Button>
</div>
</Show>
</div>
<div className="flex flex-1 flex-col gap-2 p-3">
<div className="flex flex-col gap-2">
<h4 className="text-fg-primary font-medium line-height-normal">{props.title}</h4>
<p className="text-xs text-fg-secondary">
By
{' '}
<span className="text-fg-primary">{props.author}</span>
{' '}
on
{' '}
<span className="text-fg-primary">{props.provider}</span>
</p>
</div>
<p className="max-h-22 flex-1 overflow-hidden text-sm text-fg-secondary line-height-snug">{props.description}</p>
<div className="flex flex-row gap-4 text-xs">
<Show when={props.provider !== 'SkyClient'}>
<div className="flex flex-row items-center gap-2">
<Download01Icon className="h-4 w-4" />
{abbreviateNumber(props.downloads)}
</div>
{/* <Show when={props.follows > 0}>
<div className="flex flex-row items-center gap-2">
<HeartIcon className="h-4 w-4" />
{abbreviateNumber(props.follows)}
</div>
</Show> */}
</Show>
</div>
</div>
</div>
);
}

View file

@ -0,0 +1,35 @@
import { PackageGrid } from '@/components/content/PackageItem';
import { useBrowserContext, useBrowserSearch } from '@/hooks/useBrowser';
import { Show } from '@onelauncher/common/components';
import { createFileRoute } from '@tanstack/react-router';
import { useEffect } from 'react';
import { BrowserLayout } from './route';
export const Route = createFileRoute('/app/browser/search')({
component: RouteComponent,
});
function RouteComponent() {
return (
<BrowserLayout>
<div className="flex flex-col gap-8">
<Search />
</div>
</BrowserLayout>
);
}
function Search() {
const context = useBrowserContext();
const search = useBrowserSearch(context.provider, context.query, {
});
useEffect(() => {
search.refetch();
}, [context.provider, context.query]);
return (
<Show when={search.isSuccess}>
<PackageGrid items={search.data!.items} provider={context.provider} />
</Show>
);
}

View file

@ -6,7 +6,7 @@ export interface CategoryItem {
}
export const browserCategories = {
byPackageType(packageType: 'mod' | string, provider: string): Array<CategoryItem> {
byPackageType(packageType: 'mod' | string & {}, provider: string): Array<CategoryItem> {
switch (packageType) {
case 'mod':
return this.mod(provider);