mirror of
https://github.com/Drop-OSS/drop.git
synced 2025-11-10 04:22:09 +10:00
feat: ratings ui, import giantbomb ratings
This commit is contained in:
@ -120,12 +120,11 @@ import {
|
||||
ArrowUpRightIcon,
|
||||
} from "@heroicons/vue/20/solid";
|
||||
import { micromark } from "micromark";
|
||||
import type { Game } from "~/prisma/client";
|
||||
|
||||
const route = useRoute();
|
||||
const id = route.params.id.toString();
|
||||
|
||||
const rawGame = await $dropFetch<Game>(`/api/v1/games/${id}`);
|
||||
const { game: rawGame } = await $dropFetch(`/api/v1/games/${id}`);
|
||||
const game = computed(() => {
|
||||
if (!rawGame) {
|
||||
throw createError({ statusCode: 404, message: "Game not found" });
|
||||
|
||||
@ -103,7 +103,9 @@
|
||||
'w-4 h-4',
|
||||
]"
|
||||
/>
|
||||
<span class="text-zinc-600">({{ 0 }} reviews)</span>
|
||||
<span class="text-zinc-600"
|
||||
>({{ rating._sum.mReviewCount ?? 0 }} reviews)</span
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -174,10 +176,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ArrowTopRightOnSquareIcon } from "@heroicons/vue/24/outline";
|
||||
import { StarIcon } from "@heroicons/vue/24/solid";
|
||||
import type { Game, GameVersion } from "~/prisma/client";
|
||||
import { micromark } from "micromark";
|
||||
import { DateTime } from "luxon";
|
||||
import type { SerializeObject } from "nitropack";
|
||||
import type { PlatformClient } from "~/composables/types";
|
||||
|
||||
const route = useRoute();
|
||||
@ -185,9 +185,7 @@ const gameId = route.params.id.toString();
|
||||
|
||||
const user = useUser();
|
||||
|
||||
const game = await $dropFetch<
|
||||
SerializeObject<Game> & { versions: GameVersion[] }
|
||||
>(`/api/v1/games/${gameId}`);
|
||||
const { game, rating } = await $dropFetch(`/api/v1/games/${gameId}`);
|
||||
|
||||
// Preview description (first 30 lines)
|
||||
const showPreview = ref(true);
|
||||
@ -219,10 +217,10 @@ const platforms = game.versions
|
||||
.filter((e, i, u) => u.indexOf(e) === i);
|
||||
|
||||
// const rating = Math.round(game.mReviewRating * 5);
|
||||
const rating = Math.round(0 * 5);
|
||||
const averageRating = Math.round((rating._avg.mReviewRating ?? 0) * 5);
|
||||
const ratingArray = Array(5)
|
||||
.fill(null)
|
||||
.map((_, i) => i + 1 <= rating);
|
||||
.map((_, i) => i + 1 <= averageRating);
|
||||
|
||||
useHead({
|
||||
title: game.mName,
|
||||
|
||||
@ -56,7 +56,7 @@ model GameRating {
|
||||
|
||||
mReviewHref String?
|
||||
|
||||
Game Game @relation(fields: [gameId], references: [id], onDelete: Cascade)
|
||||
game Game @relation(fields: [gameId], references: [id], onDelete: Cascade)
|
||||
gameId String
|
||||
|
||||
@@unique([metadataSource, metadataId], name: "metadataKey")
|
||||
|
||||
@ -22,5 +22,17 @@ export default defineEventHandler(async (h3) => {
|
||||
if (!game)
|
||||
throw createError({ statusCode: 404, statusMessage: "Game not found" });
|
||||
|
||||
return game;
|
||||
const rating = await prisma.gameRating.aggregate({
|
||||
where: {
|
||||
gameId: game.id,
|
||||
},
|
||||
_avg: {
|
||||
mReviewRating: true,
|
||||
},
|
||||
_sum: {
|
||||
mReviewCount: true,
|
||||
},
|
||||
});
|
||||
|
||||
return { game, rating };
|
||||
});
|
||||
|
||||
@ -7,6 +7,7 @@ import type {
|
||||
GameMetadata,
|
||||
_FetchCompanyMetadataParams,
|
||||
CompanyMetadata,
|
||||
GameMetadataRating,
|
||||
} from "./types";
|
||||
import axios, { type AxiosRequestConfig } from "axios";
|
||||
import TurndownService from "turndown";
|
||||
@ -58,6 +59,17 @@ interface GameResult {
|
||||
tags: string; // If it's "All Images", art, otherwise screenshot
|
||||
original: string;
|
||||
}>;
|
||||
|
||||
reviews: Array<{
|
||||
api_detail_url: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface ReviewResult {
|
||||
deck: string;
|
||||
score: number; // Out of 5
|
||||
reviewer: string;
|
||||
site_detail_url: string;
|
||||
}
|
||||
|
||||
interface CompanySearchResult {
|
||||
@ -198,6 +210,21 @@ export class GiantBombProvider implements MetadataProvider {
|
||||
}-${gameData.expected_release_day ?? 1}`,
|
||||
).toJSDate();
|
||||
|
||||
const reviews: GameMetadataRating[] = [];
|
||||
for (const { api_detail_url } of gameData.reviews) {
|
||||
const reviewId = api_detail_url.split("/").at(-2);
|
||||
if (!reviewId) continue;
|
||||
const review = await this.request<ReviewResult>("review", reviewId, {});
|
||||
console.log(review.data);
|
||||
reviews.push({
|
||||
metadataSource: MetadataSource.GiantBomb,
|
||||
metadataId: reviewId,
|
||||
mReviewCount: 1,
|
||||
mReviewRating: review.data.results.score / 5,
|
||||
mReviewHref: review.data.results.site_detail_url,
|
||||
});
|
||||
}
|
||||
|
||||
const metadata: GameMetadata = {
|
||||
id: gameData.guid,
|
||||
name: gameData.name,
|
||||
@ -207,7 +234,7 @@ export class GiantBombProvider implements MetadataProvider {
|
||||
|
||||
tags: [],
|
||||
|
||||
reviews: [],
|
||||
reviews,
|
||||
|
||||
publishers,
|
||||
developers,
|
||||
|
||||
@ -355,8 +355,11 @@ export class IGDBProvider implements MetadataProvider {
|
||||
const response = await this.request<IGDBGameFull>("games", body);
|
||||
|
||||
for (let i = 0; i < response.length; i++) {
|
||||
const currentGame = response[i];
|
||||
if(!currentGame) continue;
|
||||
|
||||
let iconRaw;
|
||||
const cover = response[i].cover;
|
||||
const cover = currentGame.cover;
|
||||
if (cover !== undefined) {
|
||||
iconRaw = await this.getCoverURL(cover);
|
||||
} else {
|
||||
@ -366,7 +369,7 @@ export class IGDBProvider implements MetadataProvider {
|
||||
let banner = "";
|
||||
|
||||
const images = [icon];
|
||||
for (const art of response[i]?.artworks ?? []) {
|
||||
for (const art of currentGame.artworks ?? []) {
|
||||
// if banner not set
|
||||
if (banner.length <= 0) {
|
||||
banner = createObject(await this.getArtworkURL(art));
|
||||
@ -378,7 +381,7 @@ export class IGDBProvider implements MetadataProvider {
|
||||
|
||||
const publishers: Company[] = [];
|
||||
const developers: Company[] = [];
|
||||
for (const involvedCompany of response[i]?.involved_companies ?? []) {
|
||||
for (const involvedCompany of currentGame.involved_companies ?? []) {
|
||||
// get details about the involved company
|
||||
const involved_company_response =
|
||||
await this.request<IGDBInvolvedCompany>(
|
||||
@ -408,13 +411,13 @@ export class IGDBProvider implements MetadataProvider {
|
||||
}
|
||||
}
|
||||
|
||||
const firstReleaseDate = response[i].first_release_date;
|
||||
const firstReleaseDate = currentGame.first_release_date;
|
||||
|
||||
return {
|
||||
id: "" + response[i].id,
|
||||
name: response[i].name,
|
||||
shortDescription: this.trimMessage(response[i].summary, 280),
|
||||
description: response[i].summary,
|
||||
shortDescription: this.trimMessage(currentGame.summary, 280),
|
||||
description: currentGame.summary,
|
||||
released:
|
||||
firstReleaseDate === undefined
|
||||
? new Date()
|
||||
@ -422,18 +425,18 @@ export class IGDBProvider implements MetadataProvider {
|
||||
|
||||
reviews: [
|
||||
{
|
||||
metadataId: "" + response[i].id,
|
||||
metadataId: "" + currentGame.id,
|
||||
metadataSource: MetadataSource.IGDB,
|
||||
mReviewCount: response[i]?.total_rating_count ?? 0,
|
||||
mReviewRating: (response[i]?.total_rating ?? 0) / 100,
|
||||
mReviewHref: response[i].url,
|
||||
mReviewCount: currentGame.total_rating_count ?? 0,
|
||||
mReviewRating: (currentGame.total_rating ?? 0) / 100,
|
||||
mReviewHref: currentGame.url,
|
||||
},
|
||||
],
|
||||
|
||||
publishers: [],
|
||||
developers: [],
|
||||
|
||||
tags: await this.getGenres(response[i].genres),
|
||||
tags: await this.getGenres(currentGame.genres),
|
||||
|
||||
icon,
|
||||
bannerId: banner,
|
||||
|
||||
@ -764,7 +764,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz#8ace5259254426ccef57f3175bc64ed7095ed919"
|
||||
integrity sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==
|
||||
|
||||
"@lobomfz/prismark@^0.0.3":
|
||||
"@lobomfz/prismark@0.0.3":
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@lobomfz/prismark/-/prismark-0.0.3.tgz#8b4eb34963e96cf3fff01432e62de22f184f2f2d"
|
||||
integrity sha512-g2xfR/F+sRBRUhWYlpUkafqZjqsQBetjfzdWvQndRU4wdoavn3zblM3OQwb7vrsrKB6Wmbs+DtLGaD5XBQ2v8A==
|
||||
|
||||
Reference in New Issue
Block a user