Store overhaul (#142)

* feat: small library tweaks + company page

* feat: new store view

* fix: ci merge error

* feat: add genres to store page

* feat: sorting

* feat: lock game/version imports while their tasks are running

* feat: feature games

* feat: tag based filtering

* fix: make tags alphabetical

* refactor: move a bunch of i18n to common

* feat: add localizations for everything

* fix: title description on panel

* fix: feature carousel text

* fix: i18n footer strings

* feat: add tag page

* fix: develop merge

* feat: offline games support (don't error out if provider throws)

* feat: tag management

* feat: show library next to game import + small fixes

* feat: most of the company and tag managers

* feat: company text field editing

* fix: small fixes + tsgo experiemental

* feat: upload icon and banner

* feat: store infinite scrolling and bulk import mode

* fix: lint

* fix: add drop-base to prettier ignore
This commit is contained in:
DecDuck
2025-07-30 13:40:49 +10:00
committed by GitHub
parent 1ae051f066
commit 8363de2eed
97 changed files with 3506 additions and 524 deletions

View File

@ -18,6 +18,8 @@ import taskHandler, { wrapTaskContext } from "../tasks";
import { randomUUID } from "crypto";
import { fuzzy } from "fast-fuzzy";
import { logger } from "~/server/internal/logging";
import libraryManager from "../library";
import type { GameTagModel } from "~/prisma/client/models";
export class MissingMetadataProviderConfig extends Error {
private providerName: string;
@ -124,19 +126,22 @@ export class MetadataHandler {
);
}
private parseTags(tags: string[]) {
const results: Array<Prisma.TagCreateOrConnectWithoutGamesInput> = [];
private async parseTags(tags: string[]) {
const results: Array<GameTagModel> = [];
tags.forEach((t) =>
results.push({
where: {
name: t,
},
create: {
name: t,
},
}),
);
for (const tag of tags) {
const rawResults: GameTagModel[] =
await prisma.$queryRaw`SELECT * FROM "GameTag" WHERE SIMILARITY(name, ${tag}) > 0.45;`;
let resultTag = rawResults.at(0);
if (!resultTag) {
resultTag = await prisma.gameTag.create({
data: {
name: tag,
},
});
}
results.push(resultTag);
}
return results;
}
@ -180,6 +185,8 @@ export class MetadataHandler {
});
if (existing) return undefined;
await libraryManager.lockGame(libraryId, libraryPath);
const gameId = randomUUID();
const taskId = `import:${gameId}`;
@ -262,7 +269,7 @@ export class MetadataHandler {
connectOrCreate: metadataHandler.parseRatings(metadata.reviews),
},
tags: {
connectOrCreate: metadataHandler.parseTags(metadata.tags),
connect: await metadataHandler.parseTags(metadata.tags),
},
libraryId,
@ -271,6 +278,10 @@ export class MetadataHandler {
});
logger.info(`Finished game import.`);
progress(100);
},
async finally() {
await libraryManager.unlockGame(libraryId, libraryPath);
},
});