feat: featured gallery
This commit is contained in:
parent
6634e0648e
commit
2d02f64239
6 changed files with 66 additions and 24 deletions
|
|
@ -13,7 +13,7 @@ function includes<T, TArray extends T>(list: { includes: (arg0: TArray) => boole
|
||||||
|
|
||||||
export function PackageGrid({ items, provider }: { items: Array<SearchResult>; provider: Provider }) {
|
export function PackageGrid({ items, provider }: { items: Array<SearchResult>; provider: Provider }) {
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-2 xl:grid-cols-3 gap-2">
|
<div className="grid grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-2">
|
||||||
{items.map(item => (
|
{items.map(item => (
|
||||||
<PackageItem key={item.project_id} {...item} provider={provider} />
|
<PackageItem key={item.project_id} {...item} provider={provider} />
|
||||||
))}
|
))}
|
||||||
|
|
@ -45,11 +45,11 @@ export function PackageItem({ provider, ...item }: SearchResult & { provider: Pr
|
||||||
<img alt={`Icon for ${item.title}`} className="absolute z-0 max-w-none w-7/6 opacity-50 blur-xl" src={item.icon_url} />
|
<img alt={`Icon for ${item.title}`} className="absolute z-0 max-w-none w-7/6 opacity-50 blur-xl" src={item.icon_url} />
|
||||||
<img
|
<img
|
||||||
alt={`Icon for ${item.title}`}
|
alt={`Icon for ${item.title}`}
|
||||||
className="relative z-1 aspect-ratio-square rounded-md image-render-auto w-2/5"
|
className="relative z-1 aspect-ratio-square rounded-lg image-render-auto h-5/6"
|
||||||
src={item.icon_url}
|
src={item.icon_url}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
<Tooltip text={loaders.map(upperFirst).join(', ')}>
|
<Tooltip className="bg-component-bg-disabled" text={loaders.map(upperFirst).join(', ')}>
|
||||||
<Focusable>
|
<Focusable>
|
||||||
<div className="flex flex-col rounded-full bg-component-bg/70 border-component-border/70 border p-1 absolute top-0 right-0 m-2">
|
<div className="flex flex-col rounded-full bg-component-bg/70 border-component-border/70 border p-1 absolute top-0 right-0 m-2">
|
||||||
{loaders.toSpliced(loaders.length > 3 ? 2 : 3).map(loader => <LoaderIcon className="w-5 m-1" key={loader} loader={loader} />)}
|
{loaders.toSpliced(loaders.length > 3 ? 2 : 3).map(loader => <LoaderIcon className="w-5 m-1" key={loader} loader={loader} />)}
|
||||||
|
|
|
||||||
|
|
@ -27,19 +27,20 @@ function RouteComponent() {
|
||||||
|
|
||||||
function Featured() {
|
function Featured() {
|
||||||
// const context = useBrowserContext();
|
// const context = useBrowserContext();
|
||||||
const [provider, slug] = useMemo<[Provider, string]>(() => ['Modrinth', 'sodium'], []);
|
const [provider, slug] = useMemo<[Provider, string]>(() => ['Modrinth', 'iris'], []);
|
||||||
const featuredPackage = usePackageData(provider, slug, {}, 'getFeatured');
|
const featuredPackage = usePackageData(provider, slug, {}, 'getFeatured');
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const featuredImageIndex = useMemo(() => featuredPackage.data?.gallery.findIndex(image => image.featured), [featuredPackage.data]);
|
||||||
|
const [selectedImage, setSelectedImage] = useState(featuredImageIndex ?? 0);
|
||||||
return (
|
return (
|
||||||
<Show when={featuredPackage.isSuccess}>
|
<Show when={featuredPackage.isSuccess}>
|
||||||
<div className="flex flex-col gap-y-1">
|
<div className="flex flex-col gap-y-1">
|
||||||
<h5 className="ml-2">Featured</h5>
|
<h5 className="ml-2 uppercase opacity-60">Featured</h5>
|
||||||
<div className="w-full flex flex-row overflow-hidden rounded-lg bg-page-elevated">
|
<div className="w-full flex flex-col lg:flex-row overflow-hidden rounded-lg bg-page-elevated">
|
||||||
<div className="w-full p-1">
|
<div className="w-full p-1">
|
||||||
<img alt="thumbnail" className="aspect-ratio-video h-full rounded-md object-cover object-center" src={featuredPackage.data?.icon_url ?? ''} />
|
<img alt="thumbnail" className="aspect-video h-full w-full rounded-md object-cover object-center" src={featuredPackage.data?.gallery[selectedImage].url} />
|
||||||
</div>
|
</div>
|
||||||
<div className="max-w-84 min-w-52 flex flex-col gap-y-1 p-4">
|
<div className="max-w-64 min-w-52 flex flex-col gap-y-1 p-4">
|
||||||
<h2>{featuredPackage.data?.name}</h2>
|
<h2>{featuredPackage.data?.name}</h2>
|
||||||
|
|
||||||
<Show when={false}>
|
<Show when={false}>
|
||||||
|
|
@ -49,7 +50,20 @@ function Featured() {
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<p className="mt-1 flex-1 leading-normal">{featuredPackage.data?.short_desc}</p>
|
<p className="my-1 flex-1 leading-normal">{featuredPackage.data?.short_desc}</p>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-3 gap-2 h-full">
|
||||||
|
{featuredPackage.data?.gallery.map(
|
||||||
|
(image, index) => (
|
||||||
|
<img
|
||||||
|
className="aspect-video object-center object-cover rounded-md"
|
||||||
|
key={image.url}
|
||||||
|
onClick={() => setSelectedImage(index)}
|
||||||
|
src={image.url}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row justify-end">
|
<div className="flex flex-row justify-end">
|
||||||
<Button color="ghost" onClick={() => navigate({ to: '/app/browser/package/$provider/$slug', params: { provider, slug } })}>
|
<Button color="ghost" onClick={() => navigate({ to: '/app/browser/package/$provider/$slug', params: { provider, slug } })}>
|
||||||
|
|
@ -77,7 +91,7 @@ function Lists() {
|
||||||
function List({ provider }: { provider: Provider }) {
|
function List({ provider }: { provider: Provider }) {
|
||||||
const search = useBrowserSearch(provider, {
|
const search = useBrowserSearch(provider, {
|
||||||
filters: null,
|
filters: null,
|
||||||
limit: 18 as unknown as bigint,
|
limit: 24 as unknown as bigint,
|
||||||
offset: null,
|
offset: null,
|
||||||
query: null,
|
query: null,
|
||||||
sort: null,
|
sort: null,
|
||||||
|
|
@ -85,7 +99,7 @@ function List({ provider }: { provider: Provider }) {
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h5 className="uppercase p-3 opacity-60">{provider}</h5>
|
<h5 className="uppercase m-2 opacity-60">{provider}</h5>
|
||||||
<div className={`relative overflow-hidden ${expanded ? '' : 'h-128'}`}>
|
<div className={`relative overflow-hidden ${expanded ? '' : 'h-128'}`}>
|
||||||
{search.isSuccess
|
{search.isSuccess
|
||||||
? <PackageGrid items={search.data.items} provider={provider} />
|
? <PackageGrid items={search.data.items} provider={provider} />
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,14 @@ function RouteComponent() {
|
||||||
export function BrowserLayout(props: any) {
|
export function BrowserLayout(props: any) {
|
||||||
return (
|
return (
|
||||||
<div className="relative h-full flex flex-1 flex-col items-center gap-2">
|
<div className="relative h-full flex flex-1 flex-col items-center gap-2">
|
||||||
<div className="h-full w-full max-w-screen-xl flex flex-1 flex-col items-center gap-y-2">
|
<div className="h-full w-full max-w-screen-2xl flex flex-1 flex-col items-center gap-y-2">
|
||||||
<div className="grid grid-cols-[220px_auto_220px] w-full gap-x-6">
|
<div className="grid grid-cols-[220px_auto_220px] w-full gap-x-6">
|
||||||
<div />
|
<div />
|
||||||
<BrowserToolbar />
|
<BrowserToolbar />
|
||||||
<div />
|
<div />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-[220px_auto_220px] w-full max-w-screen-xl gap-x-6 pb-8">
|
<div className="grid grid-cols-[220px_auto_220px] w-full gap-x-6 pb-8">
|
||||||
<BrowserCategories />
|
<BrowserCategories />
|
||||||
|
|
||||||
<div className="h-full flex flex-col gap-y-4">
|
<div className="h-full flex flex-col gap-y-4">
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ pub struct ManagedPackage {
|
||||||
pub links: PackageLinks,
|
pub links: PackageLinks,
|
||||||
pub status: PackageStatus,
|
pub status: PackageStatus,
|
||||||
pub downloads: usize,
|
pub downloads: usize,
|
||||||
|
pub gallery: Vec<PackageGallery>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[onelauncher_macro::specta]
|
#[onelauncher_macro::specta]
|
||||||
|
|
@ -205,6 +206,16 @@ pub struct PackageDonationUrl {
|
||||||
pub url: String,
|
pub url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[onelauncher_macro::specta]
|
||||||
|
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct PackageGallery {
|
||||||
|
pub url: String,
|
||||||
|
pub thumbnail_url: String,
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub featured: Option<bool>
|
||||||
|
}
|
||||||
|
|
||||||
/// https://api.modrinth.com/v2/tag/donation_platform
|
/// https://api.modrinth.com/v2/tag/donation_platform
|
||||||
#[onelauncher_macro::specta]
|
#[onelauncher_macro::specta]
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use super::ProviderExt;
|
use super::ProviderExt;
|
||||||
use crate::api::packages::data::{
|
use crate::api::packages::data::{
|
||||||
ManagedPackage, ManagedUser, ManagedVersion, ManagedVersionDependency, ManagedVersionFile, PackageAuthor, PackageDependencyType, PackageDonationUrl, PackageLicense, PackageLinks, PackageReleaseType, PackageSide, PackageStatus, SearchQuery, SearchResult, DEFAULT_LIMIT
|
ManagedPackage, ManagedUser, ManagedVersion, ManagedVersionDependency, ManagedVersionFile, PackageAuthor, PackageDependencyType, PackageDonationUrl, PackageGallery, PackageLicense, PackageLinks, PackageReleaseType, PackageSide, PackageStatus, SearchQuery, SearchResult, DEFAULT_LIMIT
|
||||||
};
|
};
|
||||||
use crate::error::LauncherResult;
|
use crate::error::LauncherResult;
|
||||||
use crate::utils::http;
|
use crate::utils::http;
|
||||||
|
|
@ -363,7 +363,7 @@ struct ModrinthPackage {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub license: Option<PackageLicense>,
|
pub license: Option<PackageLicense>,
|
||||||
// #[serde(default)]
|
// #[serde(default)]
|
||||||
// pub gallery: Vec<Gallery>,
|
pub gallery: Vec<ModrinthGallery>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub status: PackageStatus,
|
pub status: PackageStatus,
|
||||||
}
|
}
|
||||||
|
|
@ -405,6 +405,7 @@ impl From<ModrinthPackage> for ManagedPackage {
|
||||||
},
|
},
|
||||||
status: value.status,
|
status: value.status,
|
||||||
downloads: value.downloads,
|
downloads: value.downloads,
|
||||||
|
gallery: value.gallery.into_iter().map(Into::into).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -532,6 +533,29 @@ impl From<ModrinthUser> for ManagedUser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ModrinthGallery {
|
||||||
|
#[serde(rename = "raw_url")]
|
||||||
|
pub url: String,
|
||||||
|
#[serde(rename = "url")]
|
||||||
|
pub thumbnail_url: String,
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub featured: Option<bool>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ModrinthGallery> for PackageGallery {
|
||||||
|
fn from(value: ModrinthGallery) -> Self {
|
||||||
|
Self {
|
||||||
|
url: value.url,
|
||||||
|
thumbnail_url: value.thumbnail_url,
|
||||||
|
title: value.title,
|
||||||
|
description: value.description,
|
||||||
|
featured: value.featured
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
enum FacetOperation {
|
enum FacetOperation {
|
||||||
NotEq,
|
NotEq,
|
||||||
|
|
|
||||||
|
|
@ -110,13 +110,6 @@ body {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 { @apply text-[2rem] font-semibold; line-height: 1.5 !important; }
|
|
||||||
h2 { @apply text-[1.5rem] font-semibold; line-height: 1.25 !important; }
|
|
||||||
h3 { @apply text-[1.25rem] font-medium; }
|
|
||||||
h4 { @apply text-[1rem] font-medium; }
|
|
||||||
h5 { @apply text-[0.875rem] text-fg-secondary font-bold uppercase; }
|
|
||||||
h6 { @apply text-[0.75rem] text-fg-secondary font-medium uppercase; }
|
|
||||||
|
|
||||||
code {
|
code {
|
||||||
@apply rounded-md p-2 text-left;
|
@apply rounded-md p-2 text-left;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue