feat: Added working search filters
This commit is contained in:
parent
5012f2c3a6
commit
3025502d82
14 changed files with 172 additions and 131 deletions
|
|
@ -55,6 +55,7 @@ pub async fn get_provider_package_version(
|
|||
pub struct ProviderSearchQuery {
|
||||
query: Option<String>,
|
||||
limit: Option<u8>,
|
||||
offset: Option<u32>,
|
||||
game_versions: Option<Vec<String>>,
|
||||
categories: Option<Vec<String>>,
|
||||
loaders: Option<Vec<Loader>>,
|
||||
|
|
@ -69,6 +70,7 @@ pub async fn search_provider_packages(provider: Providers, query: ProviderSearch
|
|||
.search(
|
||||
query.query,
|
||||
query.limit,
|
||||
query.offset,
|
||||
query.game_versions,
|
||||
query.categories,
|
||||
query.loaders,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { ProviderSearchResults, Providers, SearchResult } from '@onelauncher/client/bindings';
|
||||
import type { Providers, SearchResult } from '@onelauncher/client/bindings';
|
||||
import { ChevronDownIcon, Download01Icon, HeartIcon } from '@untitled-theme/icons-solid';
|
||||
import { For, Match, Show, Switch, createSignal } from 'solid-js';
|
||||
import Button from '../base/Button';
|
||||
|
|
@ -6,11 +6,13 @@ import useBrowser from '~ui/hooks/useBrowser';
|
|||
import useSettings from '~ui/hooks/useSettings';
|
||||
import { abbreviateNumber } from '~utils';
|
||||
|
||||
type SearchResultsContainerProps = ProviderSearchResults & {
|
||||
interface SearchResultsContainerProps {
|
||||
provider: Providers;
|
||||
results: SearchResult[];
|
||||
header: string;
|
||||
category: string;
|
||||
collapsable?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
function SearchResultsContainer(props: SearchResultsContainerProps) {
|
||||
const { settings } = useSettings();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useNavigate } from '@solidjs/router';
|
||||
import { type Accessor, type Context, For, type ParentProps, type Setter, Show, createContext, createEffect, createSignal, onMount, untrack, useContext } from 'solid-js';
|
||||
import { type Accessor, type Context, For, type ParentProps, type Setter, Show, createContext, createEffect, createSignal, on, onMount, untrack, useContext } from 'solid-js';
|
||||
import type { Cluster, ManagedPackage, PackageType, ProviderSearchQuery, ProviderSearchResults, Providers } from '@onelauncher/client/bindings';
|
||||
import useCommand, { tryResult } from './useCommand';
|
||||
import { useRecentCluster } from './useCluster';
|
||||
|
|
@ -46,6 +46,7 @@ export function BrowserProvider(props: ParentProps) {
|
|||
const [searchOptions, setSearchOptions] = createSignal<ProviderSearchQuery>({
|
||||
query: '',
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
categories: null,
|
||||
game_versions: null,
|
||||
loaders: null,
|
||||
|
|
@ -119,6 +120,21 @@ export function BrowserProvider(props: ParentProps) {
|
|||
setCluster(recentCluster());
|
||||
});
|
||||
|
||||
createEffect(on(cluster, (cluster) => {
|
||||
if (cluster !== undefined)
|
||||
setSearchOptions((prev) => {
|
||||
const opts: ProviderSearchQuery = {
|
||||
...prev,
|
||||
game_versions: [cluster.meta.mc_version],
|
||||
};
|
||||
|
||||
if (cluster.meta.loader !== undefined)
|
||||
opts.loaders = [cluster.meta.loader];
|
||||
|
||||
return opts;
|
||||
});
|
||||
}));
|
||||
|
||||
return (
|
||||
<BrowserContext.Provider value={controller}>
|
||||
{props.children}
|
||||
|
|
|
|||
|
|
@ -2,26 +2,34 @@ import { ChevronLeftIcon, ChevronRightIcon } from '@untitled-theme/icons-solid';
|
|||
import { For, Show, createSignal } from 'solid-js';
|
||||
import Button from '~ui/components/base/Button';
|
||||
|
||||
interface PaginationOptions {
|
||||
export interface PaginationOptions {
|
||||
itemsCount: () => number;
|
||||
itemsPerPage: () => number;
|
||||
initialPage?: () => number;
|
||||
};
|
||||
|
||||
function usePagination(options: PaginationOptions) {
|
||||
const [page, setPage] = createSignal(Math.max(1, options.initialPage?.() || 1));
|
||||
function usePagination(initialOptions: PaginationOptions) {
|
||||
const [options, setOptions] = createSignal(initialOptions);
|
||||
|
||||
// eslint-disable-next-line solid/reactivity -- -
|
||||
const [page, setPage] = createSignal(Math.max(1, options().initialPage?.() || 1));
|
||||
|
||||
const next = () => setPage(page => page + 1);
|
||||
const prev = () => setPage(page => page - 1);
|
||||
|
||||
const totalPages = () => Math.ceil(options.itemsCount() / options.itemsPerPage());
|
||||
const totalPages = () => Math.ceil(options().itemsCount() / options().itemsPerPage());
|
||||
|
||||
const hasNext = () => page() < totalPages();
|
||||
const hasPrev = () => page() > 1;
|
||||
|
||||
const offset = () => (page() - 1) * options.itemsPerPage();
|
||||
const offset = () => (page() - 1) * options().itemsPerPage();
|
||||
|
||||
// Exported
|
||||
function reset(options: PaginationOptions) {
|
||||
setOptions(options);
|
||||
setPage(1);
|
||||
}
|
||||
|
||||
// Navigation
|
||||
function getVisiblePageNumbers(page: number) {
|
||||
const pages: number[] = [];
|
||||
|
||||
|
|
@ -97,6 +105,7 @@ function usePagination(options: PaginationOptions) {
|
|||
hasPrev,
|
||||
totalPages,
|
||||
offset,
|
||||
reset,
|
||||
Navigation,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
import { useSearchParams } from '@solidjs/router';
|
||||
import ScrollableContainer from '~ui/components/ScrollableContainer';
|
||||
|
||||
function BrowserCategory() {
|
||||
const [params] = useSearchParams<{
|
||||
category: string;
|
||||
}>();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>
|
||||
Category =
|
||||
{' '}
|
||||
{params.category}
|
||||
</h1>
|
||||
<ScrollableContainer>
|
||||
<span>test</span>
|
||||
</ScrollableContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default BrowserCategory;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { ChevronRightIcon } from '@untitled-theme/icons-solid';
|
||||
import { Show } from 'solid-js';
|
||||
import { Show, onMount } from 'solid-js';
|
||||
import type { ManagedPackage } from '@onelauncher/client/bindings';
|
||||
import { BrowserContent } from './BrowserRoot';
|
||||
import useBrowser from '~ui/hooks/useBrowser';
|
||||
|
|
@ -10,6 +10,13 @@ import SearchResultsContainer from '~ui/components/content/SearchResults';
|
|||
function BrowserMain() {
|
||||
const browser = useBrowser();
|
||||
|
||||
onMount(() => {
|
||||
browser.setSearchQuery(prev => ({
|
||||
...prev,
|
||||
categories: [],
|
||||
}));
|
||||
});
|
||||
|
||||
return (
|
||||
<BrowserContent>
|
||||
<div class="flex flex-col gap-8">
|
||||
|
|
|
|||
|
|
@ -145,6 +145,9 @@ function BrowserSidebar(props: { package: ManagedPackage; authors: ManagedUser[]
|
|||
{' '}
|
||||
<span class="text-fg-primary">{props.package.provider}</span>
|
||||
</p>
|
||||
<Show when={props.package.is_archived === true}>
|
||||
<p class="w-fit rounded-full bg-code-warn/10 px-2 py-1 text-xs text-code-warn">Archived</p>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<p class="max-h-22 flex-1 overflow-hidden text-sm text-fg-secondary line-height-snug">{props.package.description}</p>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { Route } from '@solidjs/router';
|
||||
import { Route, useLocation } from '@solidjs/router';
|
||||
import { For, type JSX, type ParentProps } from 'solid-js';
|
||||
import { SearchMdIcon, Settings01Icon } from '@untitled-theme/icons-solid';
|
||||
import type { PackageType } from '@onelauncher/client/bindings';
|
||||
import BrowserMain from './BrowserMain';
|
||||
import BrowserCategory from './BrowserCategory';
|
||||
import BrowserPackage from './BrowserPackage';
|
||||
import BrowserSearch from './BrowserSearch';
|
||||
import Button from '~ui/components/base/Button';
|
||||
|
|
@ -15,7 +13,6 @@ function BrowserRoutes() {
|
|||
return (
|
||||
<>
|
||||
<Route path="/" component={BrowserMain} />
|
||||
<Route path="/category" component={BrowserCategory} />
|
||||
<Route path="/package" component={BrowserPackage} children={<BrowserPackage.Routes />} />
|
||||
<Route path="/search" component={BrowserSearch} />
|
||||
</>
|
||||
|
|
@ -60,27 +57,49 @@ function BrowserToolbar() {
|
|||
);
|
||||
}
|
||||
|
||||
export interface BrowserSidebarCategory {
|
||||
name: string;
|
||||
sub: [string, string][];
|
||||
};
|
||||
function BrowserCategories() {
|
||||
const browser = useBrowser();
|
||||
const location = useLocation();
|
||||
|
||||
const getEnabledCategories = () => browser.searchQuery().categories || [];
|
||||
|
||||
const isEnabled = (category: string) => {
|
||||
return getEnabledCategories().includes(category);
|
||||
};
|
||||
|
||||
const toggleCategory = (category: string) => {
|
||||
const enabledCategories = getEnabledCategories();
|
||||
const index = enabledCategories.indexOf(category);
|
||||
if (index > -1)
|
||||
enabledCategories.splice(index, 1);
|
||||
else
|
||||
enabledCategories.push(category);
|
||||
|
||||
browser.setSearchQuery(prev => ({
|
||||
...prev,
|
||||
categories: enabledCategories,
|
||||
}));
|
||||
|
||||
if (!location.pathname.includes('/search'))
|
||||
browser.search();
|
||||
};
|
||||
|
||||
function BrowserCategories(props: { categories: BrowserSidebarCategory[] }) {
|
||||
return (
|
||||
<div class="top-0 grid grid-cols-[1fr_auto] h-fit min-w-50 gap-y-6">
|
||||
<div />
|
||||
<div class="flex flex-col gap-y-6">
|
||||
<For each={props.categories.concat(browserCategories.provider())}>
|
||||
<For each={browserCategories.byPackageType(browser.packageType())}>
|
||||
{category => (
|
||||
<div class="flex flex-col gap-y-2">
|
||||
<h6 class="my-1">{category.name}</h6>
|
||||
<For each={category.sub}>
|
||||
{sub => (
|
||||
<a
|
||||
href={sub[1]}
|
||||
class="text-md text-fg-primary capitalize hover:text-fg-secondary"
|
||||
children={sub[0]}
|
||||
/>
|
||||
<p
|
||||
class={`text-md capitalize text-fg-primary hover:text-fg-primary-hover ${isEnabled(sub) ? 'text-opacity-100! hover:text-opacity-90!' : 'text-opacity-60! hover:text-opacity-70!'}`}
|
||||
onClick={() => toggleCategory(sub)}
|
||||
>
|
||||
{sub}
|
||||
</p>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
|
|
@ -97,31 +116,21 @@ function BrowserSidebar() {
|
|||
return (
|
||||
<div class="flex flex-col gap-y-4">
|
||||
<div class="flex flex-col gap-y-1">
|
||||
<h6 class="my-1">Active Cluster</h6>
|
||||
<Button
|
||||
buttonStyle="secondary"
|
||||
children={controller.cluster()?.meta.name || 'None'}
|
||||
iconLeft={<Settings01Icon />}
|
||||
onClick={controller.displayClusterSelector}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-y-1">
|
||||
<h6 class="my-1">Search Filters</h6>
|
||||
<div class="flex flex-col gap-y-1">
|
||||
<h6 class="my-1">Active Cluster</h6>
|
||||
<Button
|
||||
buttonStyle="secondary"
|
||||
children={controller.cluster()?.meta.name || 'None'}
|
||||
iconLeft={<Settings01Icon />}
|
||||
onClick={controller.displayClusterSelector}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export type BrowserContentProps = ParentProps & {
|
||||
categories?: BrowserSidebarCategory[];
|
||||
};
|
||||
|
||||
const categories = (packageType: PackageType) => browserCategories.byPackageType(packageType);
|
||||
|
||||
export function BrowserContent(props: BrowserContentProps) {
|
||||
const browser = useBrowser();
|
||||
|
||||
export function BrowserContent(props: ParentProps) {
|
||||
return (
|
||||
<div class="relative h-full flex flex-1 flex-col items-center gap-2">
|
||||
<div class="h-full w-full max-w-screen-xl flex flex-1 flex-col items-center gap-y-2">
|
||||
|
|
@ -132,7 +141,7 @@ export function BrowserContent(props: BrowserContentProps) {
|
|||
</div>
|
||||
|
||||
<div class="grid grid-cols-[220px_auto_220px] w-full max-w-screen-xl gap-x-6 pb-8">
|
||||
<BrowserCategories categories={categories(browser.packageType()).concat(props.categories || [])} />
|
||||
<BrowserCategories />
|
||||
|
||||
<div class="h-full flex flex-col gap-y-4">
|
||||
<div class="h-full flex-1">
|
||||
|
|
|
|||
|
|
@ -5,28 +5,53 @@ import SearchResultsContainer from '~ui/components/content/SearchResults';
|
|||
import Spinner from '~ui/components/Spinner';
|
||||
import useBrowser from '~ui/hooks/useBrowser';
|
||||
import useCommand from '~ui/hooks/useCommand';
|
||||
import usePagination, { type PaginationOptions } from '~ui/hooks/usePagination';
|
||||
|
||||
function BrowserSearch() {
|
||||
const browser = useBrowser();
|
||||
|
||||
const [results, { refetch }] = useCommand(() => bridge.commands.searchProviderPackages('Modrinth', browser.searchQuery()));
|
||||
|
||||
createEffect(on(() => browser.searchQuery().query, () => {
|
||||
const paginationOptions = (): PaginationOptions => ({
|
||||
itemsCount: () => results()?.total || 0,
|
||||
itemsPerPage: () => browser.searchQuery().limit || 20,
|
||||
});
|
||||
|
||||
const pagination = usePagination(paginationOptions());
|
||||
|
||||
createEffect(on(browser.searchQuery, () => {
|
||||
refetch();
|
||||
}));
|
||||
|
||||
createEffect(on(() => results()?.total, (curr, prev) => {
|
||||
if (curr !== prev)
|
||||
pagination.reset(paginationOptions());
|
||||
}));
|
||||
|
||||
createEffect(on(pagination.page, () => {
|
||||
browser.setSearchQuery(prev => ({
|
||||
...prev,
|
||||
offset: pagination.offset(),
|
||||
}));
|
||||
}));
|
||||
|
||||
return (
|
||||
<BrowserContent>
|
||||
<Spinner.Suspense>
|
||||
<Show when={results() !== undefined}>
|
||||
<SearchResultsContainer
|
||||
category="test"
|
||||
header="Results"
|
||||
provider="Modrinth"
|
||||
results={results()!.results}
|
||||
/>
|
||||
</Show>
|
||||
</Spinner.Suspense>
|
||||
<pagination.Navigation />
|
||||
|
||||
<div class="my-2">
|
||||
<Spinner.Suspense>
|
||||
<Show when={results() !== undefined}>
|
||||
<SearchResultsContainer
|
||||
category="test"
|
||||
header="Results"
|
||||
provider="Modrinth"
|
||||
results={results()!.results}
|
||||
/>
|
||||
</Show>
|
||||
</Spinner.Suspense>
|
||||
</div>
|
||||
|
||||
<pagination.Navigation />
|
||||
</BrowserContent>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import type { BrowserListView, PackageType } from '@onelauncher/client/bindings';
|
||||
import type { BrowserSidebarCategory } from '~ui/pages/browser/BrowserRoot';
|
||||
import { LOADERS, PROVIDERS } from '~utils';
|
||||
|
||||
export const BROWSER_VIEWS: BrowserListView[] = ['grid', 'list', 'preview'] as const;
|
||||
|
||||
export interface BrowserSidebarCategory {
|
||||
name: string;
|
||||
sub: string[];
|
||||
};
|
||||
|
||||
export const browserCategories = {
|
||||
byPackageType(packageType: PackageType): BrowserSidebarCategory[] {
|
||||
switch (packageType) {
|
||||
|
|
@ -16,6 +19,8 @@ export const browserCategories = {
|
|||
case 'shaderpack':
|
||||
return this.shaderpack();
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
|
||||
mod(): BrowserSidebarCategory[] {
|
||||
|
|
@ -23,15 +28,14 @@ export const browserCategories = {
|
|||
{
|
||||
name: 'Categories',
|
||||
sub: [
|
||||
['Adventure', '/adventure'],
|
||||
['Magic', '/magic'],
|
||||
['Technology', '/technology'],
|
||||
['Utility', '/utility'],
|
||||
['Worldgen', '/worldgen'],
|
||||
['Miscellaneous', '/miscellaneous'],
|
||||
'adventure',
|
||||
'magic',
|
||||
'technology',
|
||||
'utility',
|
||||
'worldgen',
|
||||
'miscellaneous',
|
||||
],
|
||||
},
|
||||
...this.loader(false),
|
||||
];
|
||||
},
|
||||
|
||||
|
|
@ -46,31 +50,4 @@ export const browserCategories = {
|
|||
shaderpack(): BrowserSidebarCategory[] {
|
||||
return [];
|
||||
},
|
||||
|
||||
provider(): BrowserSidebarCategory[] {
|
||||
return [
|
||||
{
|
||||
name: 'Providers',
|
||||
sub: PROVIDERS.map(provider => [provider, `/${provider.toLowerCase()}`]),
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
loader(vanilla: boolean = false): BrowserSidebarCategory[] {
|
||||
const loaders: [string, string][] = [];
|
||||
|
||||
for (const loader of LOADERS) {
|
||||
if (vanilla === false && loader === 'vanilla')
|
||||
continue;
|
||||
|
||||
loaders.push([loader, `/${loader.toLowerCase()}`]);
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
name: 'Loaders',
|
||||
sub: loaders,
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ async fn main() -> Result<()> {
|
|||
let results = provider.search(
|
||||
Some("chatting".to_string()),
|
||||
Some(5),
|
||||
Some(0),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ impl Providers {
|
|||
&self,
|
||||
query: Option<String>,
|
||||
limit: Option<u8>,
|
||||
offset: Option<u32>,
|
||||
game_versions: Option<Vec<String>>,
|
||||
categories: Option<Vec<String>>,
|
||||
loaders: Option<Vec<Loader>>,
|
||||
|
|
@ -54,6 +55,7 @@ impl Providers {
|
|||
Providers::Modrinth => modrinth::search(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
Some(|mut builder: FacetBuilder| {
|
||||
if let Some(game_versions) = game_versions {
|
||||
for version in game_versions {
|
||||
|
|
@ -63,7 +65,7 @@ impl Providers {
|
|||
|
||||
if let Some(categories) = categories {
|
||||
for category in categories {
|
||||
builder.or(Facet(
|
||||
builder.and(Facet(
|
||||
"categories".to_string(),
|
||||
FacetOperation::Eq,
|
||||
category,
|
||||
|
|
@ -73,7 +75,7 @@ impl Providers {
|
|||
|
||||
if let Some(package_types) = package_types {
|
||||
for package_type in package_types {
|
||||
builder.or(Facet(
|
||||
builder.and(Facet(
|
||||
"project_types".to_string(),
|
||||
FacetOperation::Eq,
|
||||
package_type.get_name().to_string(),
|
||||
|
|
|
|||
|
|
@ -36,8 +36,7 @@ pub struct ModrinthPackage {
|
|||
// pub donation_urls: Vec<DonationUrl>,
|
||||
pub project_type: PackageType,
|
||||
pub downloads: u32,
|
||||
#[serde(default)]
|
||||
pub icon_url: String,
|
||||
pub icon_url: Option<String>,
|
||||
#[serde(alias = "project_id")]
|
||||
pub id: String,
|
||||
pub team: String,
|
||||
|
|
@ -57,6 +56,8 @@ pub struct ModrinthPackage {
|
|||
pub license: Option<License>,
|
||||
// #[serde(default)]
|
||||
// pub gallery: Vec<Gallery>,
|
||||
#[serde(default)]
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
impl From<ModrinthPackage> for ManagedPackage {
|
||||
|
|
@ -71,7 +72,7 @@ impl From<ModrinthPackage> for ManagedPackage {
|
|||
versions: value.versions,
|
||||
game_versions: value.game_versions,
|
||||
loaders: value.loaders,
|
||||
icon_url: Some(value.icon_url),
|
||||
icon_url: value.icon_url,
|
||||
created: value.published,
|
||||
updated: value.updated,
|
||||
client: value.client_side,
|
||||
|
|
@ -87,6 +88,7 @@ impl From<ModrinthPackage> for ManagedPackage {
|
|||
team: value.team,
|
||||
organization: value.organization,
|
||||
},
|
||||
is_archived: value.status == "archived",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -114,6 +116,7 @@ pub struct Gallery {
|
|||
#[derive(Deserialize)]
|
||||
struct SearchResults {
|
||||
hits: Vec<SearchResult>,
|
||||
total_hits: u32,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
|
|
@ -241,20 +244,23 @@ impl FacetBuilder {
|
|||
}
|
||||
|
||||
pub fn build(&self) -> String {
|
||||
let mut builder = String::new();
|
||||
let mut builder: Vec<String> = vec![];
|
||||
|
||||
for facet in &self.facets {
|
||||
builder.push('[');
|
||||
let mut stringified = String::new();
|
||||
stringified.push('[');
|
||||
for (i, f) in facet.iter().enumerate() {
|
||||
if i != 0 {
|
||||
builder.push(',');
|
||||
stringified.push(',');
|
||||
}
|
||||
builder.push_str(format!("\"{}\"", f.to_string()).as_str());
|
||||
stringified.push_str(format!("\"{}\"", f.to_string()).as_str());
|
||||
}
|
||||
builder.push(']');
|
||||
stringified.push(']');
|
||||
|
||||
builder.push(stringified);
|
||||
}
|
||||
|
||||
builder
|
||||
builder.join(",")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -306,6 +312,7 @@ pub async fn list() -> Result<Vec<ModrinthPackage>> {
|
|||
pub async fn search<F>(
|
||||
query: Option<String>,
|
||||
limit: Option<u8>,
|
||||
offset: Option<u32>,
|
||||
facets: Option<F>,
|
||||
) -> Result<ProviderSearchResults>
|
||||
where
|
||||
|
|
@ -316,9 +323,10 @@ where
|
|||
let results: SearchResults = serde_json::from_slice(
|
||||
&fetch(
|
||||
format_url!(
|
||||
"/search?query={}&limit={}{}",
|
||||
"/search?query={}&limit={}&offset={}{}",
|
||||
query.unwrap_or_default(),
|
||||
limit.unwrap_or(10),
|
||||
offset.unwrap_or(0),
|
||||
if facets.is_empty() {
|
||||
"".to_string()
|
||||
} else {
|
||||
|
|
@ -335,6 +343,7 @@ where
|
|||
Ok(ProviderSearchResults {
|
||||
provider: Providers::Modrinth,
|
||||
results: results.hits,
|
||||
total: results.total_hits,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ pub struct SearchResult {
|
|||
pub struct ProviderSearchResults {
|
||||
pub provider: Providers,
|
||||
pub results: Vec<SearchResult>,
|
||||
pub total: u32,
|
||||
}
|
||||
|
||||
/// A struct that represents a Package.
|
||||
|
|
@ -587,8 +588,9 @@ pub struct ManagedPackage {
|
|||
pub categories: Vec<String>,
|
||||
pub optional_categories: Option<Vec<String>>,
|
||||
pub license: Option<License>,
|
||||
|
||||
pub author: Author,
|
||||
|
||||
pub is_archived: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "specta", derive(specta::Type))]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue