diff --git a/mclib/src/query/IModQueryService.ts b/mclib/src/query/IModQueryService.ts index d9dbb87..8981734 100644 --- a/mclib/src/query/IModQueryService.ts +++ b/mclib/src/query/IModQueryService.ts @@ -1,4 +1,4 @@ -import { MCVersion, ModMetadata, ModReleases, ModRepoMetadata, ModRepositoryName } from "../types"; +import { MCVersion, ModMetadata, ModReleases, ModRepositoryName } from "../types"; export interface IModQueryService { getMinecraftVersions(): Promise; @@ -6,7 +6,7 @@ export interface IModQueryService { query: string, specifiedRepos: ModRepositoryName[], maxResults: number, - ): Promise>; + ): Promise; getModReleasesFromMetadata(modMeta: ModMetadata): Promise; getModByDataHash(modData: Uint8Array): Promise; } diff --git a/mclib/src/query/LocalModQueryService.ts b/mclib/src/query/LocalModQueryService.ts index f42c255..7061e4e 100644 --- a/mclib/src/query/LocalModQueryService.ts +++ b/mclib/src/query/LocalModQueryService.ts @@ -1,5 +1,6 @@ -import { type MCVersion, type ModRepositoryName, type ModRepoMetadata, type IRepository, type ModMetadata, type ModReleases, ModMetadataUtil, IModQueryService } from ".."; +import { type MCVersion, type ModRepositoryName, type IRepository, type ModMetadata, type ModReleases, ModMetadataUtil, IModQueryService } from ".."; import { logger } from "../logger"; +import { isSameModAndRepo } from "../utils"; export class LocalModQueryService implements IModQueryService { @@ -21,9 +22,9 @@ export class LocalModQueryService implements IModQueryService { async searchMods( query: string, specifiedRepos: ModRepositoryName[], - maxResults: number = 10, - ): Promise> { - const allResults: Array<[ModRepositoryName, ModRepoMetadata]> = []; + maxResults: number, + ): Promise { + const allResults: Array = []; for (const repo of this.repositories) { try { @@ -32,9 +33,20 @@ export class LocalModQueryService implements IModQueryService { continue; // Skip repositories not in the specified list } - const results = await repo.searchMods(query, maxResults); - for (const mod of results) { - allResults.push([repoName, mod]); + // Loop over search results of this repository + for (const newModRepo of await repo.searchMods(query, maxResults)) { + + // Check if this mod already exists in the aggregated results + let wasMerged = false; + for (const existingMod of allResults) { + if (isSameModAndRepo(existingMod, newModRepo)) { + // Update existing mod entry with additional repository metadata + existingMod.push(newModRepo); + wasMerged = true; + break; + } + } + if (!wasMerged) allResults.push([newModRepo]); // Add as new entry } } catch (_) { // Ignore errors for individual repositories @@ -42,11 +54,13 @@ export class LocalModQueryService implements IModQueryService { } // Sort by download count descending - allResults.sort((a, b) => b[1].downloadCount - a[1].downloadCount); + allResults.sort((a, b) => b[0].downloadCount - a[0].downloadCount); + // Limit results if maxResults is specified if (maxResults !== undefined) { return allResults.slice(0, maxResults); } + return allResults; } diff --git a/mclib/src/query/RemoteModQueryService.ts b/mclib/src/query/RemoteModQueryService.ts index afd79d3..cd9a3d5 100644 --- a/mclib/src/query/RemoteModQueryService.ts +++ b/mclib/src/query/RemoteModQueryService.ts @@ -33,7 +33,7 @@ export class RemoteModQueryService implements IModQueryService { query: string, specifiedRepos: ModRepositoryName[], maxResults: number - ): Promise> { + ): Promise { return this.callEndpoint("searchMods", { query, specifiedRepos, maxResults }); } diff --git a/mclib/src/utils.ts b/mclib/src/utils.ts index caf3f3d..4659f8c 100644 --- a/mclib/src/utils.ts +++ b/mclib/src/utils.ts @@ -1,5 +1,19 @@ +import { ModMetadata, ModRepoMetadata } from "./types"; + export function validateParam(s: string) { if (!/^[a-zA-Z0-9]{0,64}$/.test(s)) { throw new Error('Invalid parameter: must be 0-64 alphanumeric characters'); } } + +export function isSameModAndRepo(modA: ModMetadata, modB: ModRepoMetadata): boolean { + for (const v of modA) { + if (isSameModRepo(v, modB)) return true; + } + return false; +} + + // Returns true if id OR slug matches id OR slug of the other mod +export function isSameModRepo(modA: ModRepoMetadata, modB: ModRepoMetadata): boolean { + return [modA.id, modA.slug].some(idA => [modB.id, modB.slug].includes(idA)); +} diff --git a/web/messages/en.json b/web/messages/en.json index b5121ba..de0b848 100644 --- a/web/messages/en.json +++ b/web/messages/en.json @@ -9,10 +9,8 @@ "search_mod_by_name": "Search for a mod by its name", "search_mods_button": "Search", "search_results": { - "loading_mod_search": "Please wait, we're searching your mod", "downloads_count": "downloads", - "open_mod_repo_link": "See on {repo_name}", - "add_mod": "Add", + "repo_from": "From", "remove_mod": "Remove" }, "drag_and_drop": { diff --git a/web/messages/fr.json b/web/messages/fr.json index f4b9589..5c3311e 100644 --- a/web/messages/fr.json +++ b/web/messages/fr.json @@ -9,10 +9,8 @@ "search_mod_by_name": "Recherchez un mod par son nom", "search_mods_button": "Rechercher", "search_results": { - "loading_mod_search": "Veuillez patienter, nous recherchons votre mod", "downloads_count": "téléchargements", - "open_mod_repo_link": "Voir sur {repo_name}", - "add_mod": "Ajouter", + "repo_from": "Depuis", "remove_mod": "Enlever" }, "drag_and_drop": { diff --git a/web/src/components/ModSearch.svelte b/web/src/components/ModSearch.svelte index 21015ae..864d6e0 100644 --- a/web/src/components/ModSearch.svelte +++ b/web/src/components/ModSearch.svelte @@ -1,7 +1,7 @@ - - - - {#each search_results.slice(0, 10) as [repository, mod] (mod.id)} - - - - - - - {/each} - -
{ - add_mod_to_list(mod); +
    + {#each search_results.slice(0, 10) as modMeta (modMeta[0].id)} + {@const firstRepoMeta = modMeta[0]} +
  • +
    { + hoveredRow = null; + }} + > +
    +
    { + add_mod_to_list(firstRepoMeta); + }} + onkeydown={(ke) => { + if (ke.code == 'Enter') add_mod_to_list(firstRepoMeta); + }} + > +
    + {firstRepoMeta.name} pic +

    + {firstRepoMeta.name} +

    +
    +

    + {humanize_number(modMeta.reduce((sum, current) => sum + current.downloadCount, 0)) + + ' ' + + m['add_mods.search_results.downloads_count']()} +

    +
    + +
    +
{ - add_mod_to_list(mod); + onmouseleave={() => { + hoveredRow = null; }} > - {mod.name} - { - add_mod_to_list(mod); - }} - > - {humanize_number(mod.downloadCount) + - ' ' + - m['add_mods.search_results.downloads_count']()} -
+ {#each modMeta as repo (repo.id)} + {repo.repository.toUpperCase()} + {/each} + + + + {/each} +