fix: browser main page
This commit is contained in:
parent
292e7df7f8
commit
85443ee5a9
4 changed files with 141 additions and 79 deletions
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
35
apps/onelauncher/frontend/src/routes/app/browser/search.tsx
Normal file
35
apps/onelauncher/frontend/src/routes/app/browser/search.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue