feat: minimum support for unrefed object cleanup
This commit is contained in:
@@ -9,7 +9,7 @@
|
|||||||
class="absolute inset-0 transition-all duration-300 group-hover:scale-110"
|
class="absolute inset-0 transition-all duration-300 group-hover:scale-110"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="useObject(game.mCoverId)"
|
:src="useObject(game.mCoverObjectId)"
|
||||||
class="w-full h-full object-cover brightness-[90%]"
|
class="w-full h-full object-cover brightness-[90%]"
|
||||||
:class="{ active: active === game.id }"
|
:class="{ active: active === game.id }"
|
||||||
:alt="game.mName"
|
:alt="game.mName"
|
||||||
@@ -42,7 +42,7 @@ const props = defineProps<{
|
|||||||
game:
|
game:
|
||||||
| SerializeObject<{
|
| SerializeObject<{
|
||||||
id: string;
|
id: string;
|
||||||
mCoverId: string;
|
mCoverObjectId: string;
|
||||||
mName: string;
|
mName: string;
|
||||||
mShortDescription: string;
|
mShortDescription: string;
|
||||||
}>
|
}>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
class="flex flex-row items-center w-full p-1 rounded-md transition-all duration-200 hover:bg-zinc-800 hover:scale-105 hover:shadow-lg active:scale-95"
|
class="flex flex-row items-center w-full p-1 rounded-md transition-all duration-200 hover:bg-zinc-800 hover:scale-105 hover:shadow-lg active:scale-95"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="useObject(game.mCoverId)"
|
:src="useObject(game.mCoverObjectId)"
|
||||||
class="h-9 w-9 flex-shrink-0 rounded transition-all duration-300 group-hover:scale-105 hover:rotate-[-2deg] hover:shadow-lg"
|
class="h-9 w-9 flex-shrink-0 rounded transition-all duration-300 group-hover:scale-105 hover:rotate-[-2deg] hover:shadow-lg"
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
class="flex flex-col lg:flex-row lg:justify-between items-start lg:items-center gap-2"
|
class="flex flex-col lg:flex-row lg:justify-between items-start lg:items-center gap-2"
|
||||||
>
|
>
|
||||||
<div class="inline-flex items-center gap-4">
|
<div class="inline-flex items-center gap-4">
|
||||||
<img :src="useObject(game.mIconId)" class="size-20" />
|
<img :src="useObject(game.mIconObjectId)" class="size-20" />
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-5xl font-bold font-display text-zinc-100">
|
<h1 class="text-5xl font-bold font-display text-zinc-100">
|
||||||
{{ game.mName }}
|
{{ game.mName }}
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="game.mImageCarousel.length == 0"
|
v-if="game.mImageCarouselObjectIds.length == 0"
|
||||||
class="text-zinc-400 text-center py-8"
|
class="text-zinc-400 text-center py-8"
|
||||||
>
|
>
|
||||||
No images added to the carousel yet.
|
No images added to the carousel yet.
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
|
|
||||||
<draggable
|
<draggable
|
||||||
v-else
|
v-else
|
||||||
:list="game.mImageCarousel"
|
:list="game.mImageCarouselObjectIds"
|
||||||
class="w-full flex flex-row gap-x-4 overflow-x-auto my-2 py-4"
|
class="w-full flex flex-row gap-x-4 overflow-x-auto my-2 py-4"
|
||||||
@update="() => updateImageCarousel()"
|
@update="() => updateImageCarousel()"
|
||||||
>
|
>
|
||||||
@@ -242,7 +242,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-3 grid grid-cols-2 grid-flow-dense gap-8">
|
<div class="mt-3 grid grid-cols-2 grid-flow-dense gap-8">
|
||||||
<div
|
<div
|
||||||
v-for="(image, imageIdx) in game.mImageLibrary"
|
v-for="(image, imageIdx) in game.mImageLibraryObjectIds"
|
||||||
:key="imageIdx"
|
:key="imageIdx"
|
||||||
class="group relative flex items-center bg-zinc-950/30"
|
class="group relative flex items-center bg-zinc-950/30"
|
||||||
>
|
>
|
||||||
@@ -251,7 +251,7 @@
|
|||||||
class="transition-all lg:opacity-0 lg:group-hover:opacity-100 absolute inset-0 flex flex-col items-center justify-center gap-y-2 bg-zinc-950/50"
|
class="transition-all lg:opacity-0 lg:group-hover:opacity-100 absolute inset-0 flex flex-col items-center justify-center gap-y-2 bg-zinc-950/50"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
v-if="image !== game.mBannerId"
|
v-if="image !== game.mBannerObjectId"
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-x-1.5 rounded-md bg-blue-600 px-1.5 py-0.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
|
class="inline-flex items-center gap-x-1.5 rounded-md bg-blue-600 px-1.5 py-0.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
|
||||||
@click="() => updateBannerImage(image)"
|
@click="() => updateBannerImage(image)"
|
||||||
@@ -259,7 +259,7 @@
|
|||||||
Set as banner
|
Set as banner
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="image !== game.mCoverId"
|
v-if="image !== game.mCoverObjectId"
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-x-1.5 rounded-md bg-blue-600 px-1.5 py-0.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
|
class="inline-flex items-center gap-x-1.5 rounded-md bg-blue-600 px-1.5 py-0.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
|
||||||
@click="() => updateCoverImage(image)"
|
@click="() => updateCoverImage(image)"
|
||||||
@@ -275,14 +275,17 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="image === game.mBannerId || image === game.mCoverId"
|
v-if="
|
||||||
|
image === game.mBannerObjectId ||
|
||||||
|
image === game.mCoverObjectId
|
||||||
|
"
|
||||||
class="absolute bottom-0 left-0 bg-zinc-950/75 text-zinc-100 text-sm font-semibold px-2 py-1 rounded-tr"
|
class="absolute bottom-0 left-0 bg-zinc-950/75 text-zinc-100 text-sm font-semibold px-2 py-1 rounded-tr"
|
||||||
>
|
>
|
||||||
current
|
current
|
||||||
{{
|
{{
|
||||||
[
|
[
|
||||||
image === game.mBannerId ? "banner" : undefined,
|
image === game.mBannerObjectId ? "banner" : undefined,
|
||||||
image === game.mCoverId ? "cover" : undefined,
|
image === game.mCoverObjectId ? "cover" : undefined,
|
||||||
]
|
]
|
||||||
.filter((e) => e)
|
.filter((e) => e)
|
||||||
.join(" & ")
|
.join(" & ")
|
||||||
@@ -400,7 +403,7 @@
|
|||||||
<template #default>
|
<template #default>
|
||||||
<div class="grid grid-cols-2 grid-flow-dense gap-4">
|
<div class="grid grid-cols-2 grid-flow-dense gap-4">
|
||||||
<div
|
<div
|
||||||
v-for="(image, imageIdx) in game.mImageLibrary"
|
v-for="(image, imageIdx) in game.mImageLibraryObjectIds"
|
||||||
:key="imageIdx"
|
:key="imageIdx"
|
||||||
class="group relative flex items-center bg-zinc-950/30"
|
class="group relative flex items-center bg-zinc-950/30"
|
||||||
>
|
>
|
||||||
@@ -418,7 +421,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="game.mImageLibrary.length == 0"
|
v-if="game.mImageLibraryObjectIds.length == 0"
|
||||||
class="text-zinc-400 col-span-2"
|
class="text-zinc-400 col-span-2"
|
||||||
>
|
>
|
||||||
No images to add.
|
No images to add.
|
||||||
@@ -548,7 +551,7 @@ const game = ref(rawGame);
|
|||||||
|
|
||||||
const coreMetadataName = ref(game.value.mName);
|
const coreMetadataName = ref(game.value.mName);
|
||||||
const coreMetadataDescription = ref(game.value.mShortDescription);
|
const coreMetadataDescription = ref(game.value.mShortDescription);
|
||||||
const coreMetadataIconUrl = ref(useObject(game.value.mIconId));
|
const coreMetadataIconUrl = ref(useObject(game.value.mIconObjectId));
|
||||||
const coreMetadataIconFileUpload = ref<FileList | undefined>();
|
const coreMetadataIconFileUpload = ref<FileList | undefined>();
|
||||||
const coreMetadataLoading = ref(false);
|
const coreMetadataLoading = ref(false);
|
||||||
|
|
||||||
@@ -665,8 +668,8 @@ watch(descriptionHTML, (_v) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const validAddCarouselImages = computed(() =>
|
const validAddCarouselImages = computed(() =>
|
||||||
game.value.mImageLibrary.filter(
|
game.value.mImageLibraryObjectIds.filter(
|
||||||
(e) => !game.value.mImageCarousel.includes(e),
|
(e) => !game.value.mImageCarouselObjectIds.includes(e),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -683,15 +686,15 @@ function insertImageAtCursor(id: string) {
|
|||||||
|
|
||||||
async function updateBannerImage(id: string) {
|
async function updateBannerImage(id: string) {
|
||||||
try {
|
try {
|
||||||
if (game.value.mBannerId == id) return;
|
if (game.value.mBannerObjectId == id) return;
|
||||||
const { mBannerId } = await $dropFetch("/api/v1/admin/game", {
|
const { mBannerObjectId } = await $dropFetch("/api/v1/admin/game", {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: {
|
body: {
|
||||||
id: gameId,
|
id: gameId,
|
||||||
mBannerId: id,
|
mBannerId: id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
game.value.mBannerId = mBannerId;
|
game.value.mBannerObjectId = mBannerObjectId;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
createModal(
|
createModal(
|
||||||
ModalType.Notification,
|
ModalType.Notification,
|
||||||
@@ -710,15 +713,15 @@ async function updateBannerImage(id: string) {
|
|||||||
|
|
||||||
async function updateCoverImage(id: string) {
|
async function updateCoverImage(id: string) {
|
||||||
try {
|
try {
|
||||||
if (game.value.mCoverId == id) return;
|
if (game.value.mCoverObjectId == id) return;
|
||||||
const { mCoverId } = await $dropFetch("/api/v1/admin/game", {
|
const { mCoverObjectId } = await $dropFetch("/api/v1/admin/game", {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: {
|
body: {
|
||||||
id: gameId,
|
id: gameId,
|
||||||
mCoverId: id,
|
mCoverId: id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
game.value.mCoverId = mCoverId;
|
game.value.mCoverObjectId = mCoverObjectId;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
createModal(
|
createModal(
|
||||||
ModalType.Notification,
|
ModalType.Notification,
|
||||||
@@ -737,7 +740,7 @@ async function updateCoverImage(id: string) {
|
|||||||
|
|
||||||
async function deleteImage(id: string) {
|
async function deleteImage(id: string) {
|
||||||
try {
|
try {
|
||||||
const { mBannerId, mImageLibrary } = await $dropFetch(
|
const { mBannerObjectId, mImageLibraryObjectIds } = await $dropFetch(
|
||||||
"/api/v1/admin/game/image",
|
"/api/v1/admin/game/image",
|
||||||
{
|
{
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
@@ -747,8 +750,8 @@ async function deleteImage(id: string) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
game.value.mImageLibrary = mImageLibrary;
|
game.value.mImageLibraryObjectIds = mImageLibraryObjectIds;
|
||||||
game.value.mBannerId = mBannerId;
|
game.value.mBannerObjectId = mBannerObjectId;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
createModal(
|
createModal(
|
||||||
ModalType.Notification,
|
ModalType.Notification,
|
||||||
@@ -767,7 +770,7 @@ async function deleteImage(id: string) {
|
|||||||
|
|
||||||
async function uploadAfterImageUpload(result: Game) {
|
async function uploadAfterImageUpload(result: Game) {
|
||||||
if (!game.value) return;
|
if (!game.value) return;
|
||||||
game.value.mImageLibrary = result.mImageLibrary;
|
game.value.mImageLibraryObjectIds = result.mImageLibraryObjectIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteVersion(versionName: string) {
|
async function deleteVersion(versionName: string) {
|
||||||
@@ -827,13 +830,15 @@ async function updateVersionOrder() {
|
|||||||
|
|
||||||
function addImageToCarousel(id: string) {
|
function addImageToCarousel(id: string) {
|
||||||
showAddCarouselModal.value = false;
|
showAddCarouselModal.value = false;
|
||||||
game.value.mImageCarousel.push(id);
|
game.value.mImageCarouselObjectIds.push(id);
|
||||||
updateImageCarousel();
|
updateImageCarousel();
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeImageFromCarousel(id: string) {
|
function removeImageFromCarousel(id: string) {
|
||||||
const imageIndex = game.value.mImageCarousel.findIndex((e) => e == id);
|
const imageIndex = game.value.mImageCarouselObjectIds.findIndex(
|
||||||
game.value.mImageCarousel.splice(imageIndex, 1);
|
(e) => e == id,
|
||||||
|
);
|
||||||
|
game.value.mImageCarouselObjectIds.splice(imageIndex, 1);
|
||||||
updateImageCarousel();
|
updateImageCarousel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -843,7 +848,7 @@ async function updateImageCarousel() {
|
|||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: {
|
body: {
|
||||||
id: gameId,
|
id: gameId,
|
||||||
mImageCarousel: game.value.mImageCarousel,
|
mImageCarousel: game.value.mImageCarouselObjectIds,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
<div class="flex flex-1 flex-row p-4 gap-x-4">
|
<div class="flex flex-1 flex-row p-4 gap-x-4">
|
||||||
<img
|
<img
|
||||||
class="h-16 w-16 flex-shrink-0 rounded-md"
|
class="h-16 w-16 flex-shrink-0 rounded-md"
|
||||||
:src="useObject(game.mIconId)"
|
:src="useObject(game.mIconObjectId)"
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<!-- Banner background with gradient overlays -->
|
<!-- Banner background with gradient overlays -->
|
||||||
<div class="absolute inset-0 z-0 rounded-xl overflow-hidden">
|
<div class="absolute inset-0 z-0 rounded-xl overflow-hidden">
|
||||||
<img
|
<img
|
||||||
:src="useObject(game.mBannerId)"
|
:src="useObject(game.mBannerObjectId)"
|
||||||
class="w-full h-[24rem] object-cover blur-sm scale-105"
|
class="w-full h-[24rem] object-cover blur-sm scale-105"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
@@ -73,13 +73,16 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<VueCarousel :items-to-show="1">
|
<VueCarousel :items-to-show="1">
|
||||||
<VueSlide v-for="image in game.mImageCarousel" :key="image">
|
<VueSlide
|
||||||
|
v-for="image in game.mImageCarouselObjectIds"
|
||||||
|
:key="image"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
class="w-fit h-48 lg:h-96 rounded"
|
class="w-fit h-48 lg:h-96 rounded"
|
||||||
:src="useObject(image)"
|
:src="useObject(image)"
|
||||||
/>
|
/>
|
||||||
</VueSlide>
|
</VueSlide>
|
||||||
<VueSlide v-if="game.mImageCarousel.length == 0">
|
<VueSlide v-if="game.mImageCarouselObjectIds.length == 0">
|
||||||
<div
|
<div
|
||||||
class="h-48 lg:h-96 aspect-[1/2] flex items-center justify-center text-zinc-700 font-bold font-display"
|
class="h-48 lg:h-96 aspect-[1/2] flex items-center justify-center text-zinc-700 font-bold font-display"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -5,7 +5,10 @@
|
|||||||
>
|
>
|
||||||
<!-- banner image -->
|
<!-- banner image -->
|
||||||
<div class="absolute flex top-0 h-fit inset-x-0 h-12 -z-[20] pb-4">
|
<div class="absolute flex top-0 h-fit inset-x-0 h-12 -z-[20] pb-4">
|
||||||
<img :src="useObject(game.mBannerId)" class="blur-sm w-full h-auto" />
|
<img
|
||||||
|
:src="useObject(game.mBannerObjectId)"
|
||||||
|
class="blur-sm w-full h-auto"
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 bg-gradient-to-b from-transparent to-80% to-zinc-950"
|
class="absolute inset-0 bg-gradient-to-b from-transparent to-80% to-zinc-950"
|
||||||
/>
|
/>
|
||||||
@@ -29,7 +32,7 @@
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="transition-all duration-300 hover:scale-105 hover:rotate-[-1deg] w-64 h-auto rounded gameCover"
|
class="transition-all duration-300 hover:scale-105 hover:rotate-[-1deg] w-64 h-auto rounded gameCover"
|
||||||
:src="useObject(game.mCoverId)"
|
:src="useObject(game.mCoverObjectId)"
|
||||||
:alt="game.mName"
|
:alt="game.mName"
|
||||||
/>
|
/>
|
||||||
<div class="flex items-center gap-x-2">
|
<div class="flex items-center gap-x-2">
|
||||||
@@ -115,13 +118,16 @@
|
|||||||
</p>
|
</p>
|
||||||
<div class="mt-6 py-4 rounded">
|
<div class="mt-6 py-4 rounded">
|
||||||
<VueCarousel :items-to-show="1">
|
<VueCarousel :items-to-show="1">
|
||||||
<VueSlide v-for="image in game.mImageCarousel" :key="image">
|
<VueSlide
|
||||||
|
v-for="image in game.mImageCarouselObjectIds"
|
||||||
|
:key="image"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
class="w-fit h-48 lg:h-96 rounded"
|
class="w-fit h-48 lg:h-96 rounded"
|
||||||
:src="useObject(image)"
|
:src="useObject(image)"
|
||||||
/>
|
/>
|
||||||
</VueSlide>
|
</VueSlide>
|
||||||
<VueSlide v-if="game.mImageCarousel.length == 0">
|
<VueSlide v-if="game.mImageCarouselObjectIds.length == 0">
|
||||||
<div
|
<div
|
||||||
class="h-48 lg:h-96 aspect-[1/2] flex items-center justify-center text-zinc-700 font-bold font-display"
|
class="h-48 lg:h-96 aspect-[1/2] flex items-center justify-center text-zinc-700 font-bold font-display"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<div class="w-full h-full relative">
|
<div class="w-full h-full relative">
|
||||||
<div class="absolute inset-0">
|
<div class="absolute inset-0">
|
||||||
<img
|
<img
|
||||||
:src="useObject(game.mBannerId)"
|
:src="useObject(game.mBannerObjectId)"
|
||||||
alt=""
|
alt=""
|
||||||
class="size-full object-cover object-center"
|
class="size-full object-cover object-center"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
-- Rename game table columns
|
||||||
|
ALTER TABLE "Game" RENAME COLUMN "mIconId" TO "mIconObjectId";
|
||||||
|
ALTER TABLE "Game" RENAME COLUMN "mBannerId" TO "mBannerObjectId";
|
||||||
|
ALTER TABLE "Game" RENAME COLUMN "mCoverId" TO "mCoverObjectId";
|
||||||
|
ALTER TABLE "Game" RENAME COLUMN "mImageCarousel" TO "mImageCarouselObjectIds";
|
||||||
|
ALTER TABLE "Game" RENAME COLUMN "mImageLibrary" TO "mImageLibraryObjectIds";
|
||||||
|
|
||||||
|
-- Rename saveslot table columns
|
||||||
|
ALTER TABLE "SaveSlot" RENAME COLUMN "history" TO "historyObjectIds";
|
||||||
@@ -24,11 +24,11 @@ model Game {
|
|||||||
mReviewCount Int
|
mReviewCount Int
|
||||||
mReviewRating Float // 0 to 1
|
mReviewRating Float // 0 to 1
|
||||||
|
|
||||||
mIconId String // linked to objects in s3
|
mIconObjectId String // linked to objects in s3
|
||||||
mBannerId String // linked to objects in s3
|
mBannerObjectId String // linked to objects in s3
|
||||||
mCoverId String
|
mCoverObjectId String
|
||||||
mImageCarousel String[] // linked to below array
|
mImageCarouselObjectIds String[] // linked to below array
|
||||||
mImageLibrary String[] // linked to objects in s3
|
mImageLibraryObjectIds String[] // linked to objects in s3
|
||||||
|
|
||||||
versions GameVersion[]
|
versions GameVersion[]
|
||||||
libraryBasePath String @unique // Base dir for all the game versions
|
libraryBasePath String @unique // Base dir for all the game versions
|
||||||
@@ -80,7 +80,7 @@ model SaveSlot {
|
|||||||
lastUsedClientId String?
|
lastUsedClientId String?
|
||||||
lastUsedClient Client? @relation(fields: [lastUsedClientId], references: [id])
|
lastUsedClient Client? @relation(fields: [lastUsedClientId], references: [id])
|
||||||
|
|
||||||
history String[] // list of objects
|
historyObjectIds String[] // list of objects
|
||||||
historyChecksums String[] // list of hashes
|
historyChecksums String[] // list of hashes
|
||||||
|
|
||||||
@@id([gameId, userId, index], name: "id")
|
@@id([gameId, userId, index], name: "id")
|
||||||
|
|||||||
@@ -21,27 +21,27 @@ export default defineEventHandler(async (h3) => {
|
|||||||
id: gameId,
|
id: gameId,
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
mBannerId: true,
|
mBannerObjectId: true,
|
||||||
mImageLibrary: true,
|
mImageLibraryObjectIds: true,
|
||||||
mCoverId: true,
|
mCoverObjectId: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!game)
|
if (!game)
|
||||||
throw createError({ statusCode: 400, statusMessage: "Invalid game ID" });
|
throw createError({ statusCode: 400, statusMessage: "Invalid game ID" });
|
||||||
|
|
||||||
const imageIndex = game.mImageLibrary.findIndex((e) => e == imageId);
|
const imageIndex = game.mImageLibraryObjectIds.findIndex((e) => e == imageId);
|
||||||
if (imageIndex == -1)
|
if (imageIndex == -1)
|
||||||
throw createError({ statusCode: 400, statusMessage: "Image not found" });
|
throw createError({ statusCode: 400, statusMessage: "Image not found" });
|
||||||
|
|
||||||
game.mImageLibrary.splice(imageIndex, 1);
|
game.mImageLibraryObjectIds.splice(imageIndex, 1);
|
||||||
await objectHandler.deleteAsSystem(imageId);
|
await objectHandler.deleteAsSystem(imageId);
|
||||||
|
|
||||||
if (game.mBannerId === imageId) {
|
if (game.mBannerObjectId === imageId) {
|
||||||
game.mBannerId = game.mImageLibrary[0];
|
game.mBannerObjectId = game.mImageLibraryObjectIds[0];
|
||||||
}
|
}
|
||||||
if (game.mCoverId === imageId) {
|
if (game.mCoverObjectId === imageId) {
|
||||||
game.mCoverId = game.mImageLibrary[0];
|
game.mCoverObjectId = game.mImageLibraryObjectIds[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await prisma.game.update({
|
const result = await prisma.game.update({
|
||||||
@@ -49,14 +49,14 @@ export default defineEventHandler(async (h3) => {
|
|||||||
id: gameId,
|
id: gameId,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
mBannerId: game.mBannerId,
|
mBannerObjectId: game.mBannerObjectId,
|
||||||
mImageLibrary: game.mImageLibrary,
|
mImageLibraryObjectIds: game.mImageLibraryObjectIds,
|
||||||
mCoverId: game.mCoverId,
|
mCoverObjectId: game.mCoverObjectId,
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
mBannerId: true,
|
mBannerObjectId: true,
|
||||||
mImageLibrary: true,
|
mImageLibraryObjectIds: true,
|
||||||
mCoverId: true,
|
mCoverObjectId: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export default defineEventHandler(async (h3) => {
|
|||||||
id: gameId,
|
id: gameId,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
mImageLibrary: {
|
mImageLibraryObjectIds: {
|
||||||
push: id,
|
push: id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export default defineEventHandler(async (h3) => {
|
|||||||
id: gameId,
|
id: gameId,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
mIconId: id,
|
mIconObjectId: id,
|
||||||
mName: name,
|
mName: name,
|
||||||
mShortDescription: description,
|
mShortDescription: description,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ export default defineEventHandler(async (h3) => {
|
|||||||
id: true,
|
id: true,
|
||||||
mName: true,
|
mName: true,
|
||||||
mShortDescription: true,
|
mShortDescription: true,
|
||||||
mCoverId: true,
|
mCoverObjectId: true,
|
||||||
mBannerId: true,
|
mBannerObjectId: true,
|
||||||
mDevelopers: {
|
mDevelopers: {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class LibraryManager {
|
|||||||
metadataSource: true,
|
metadataSource: true,
|
||||||
mDevelopers: true,
|
mDevelopers: true,
|
||||||
mPublishers: true,
|
mPublishers: true,
|
||||||
mIconId: true,
|
mIconObjectId: true,
|
||||||
libraryBasePath: true,
|
libraryBasePath: true,
|
||||||
},
|
},
|
||||||
orderBy: {
|
orderBy: {
|
||||||
|
|||||||
@@ -174,10 +174,10 @@ export class MetadataHandler {
|
|||||||
mReviewRating: metadata.reviewRating,
|
mReviewRating: metadata.reviewRating,
|
||||||
mReleased: metadata.released,
|
mReleased: metadata.released,
|
||||||
|
|
||||||
mIconId: metadata.icon,
|
mIconObjectId: metadata.icon,
|
||||||
mBannerId: metadata.bannerId,
|
mBannerObjectId: metadata.bannerId,
|
||||||
mCoverId: metadata.coverId,
|
mCoverObjectId: metadata.coverId,
|
||||||
mImageLibrary: metadata.images,
|
mImageLibraryObjectIds: metadata.images,
|
||||||
|
|
||||||
libraryBasePath,
|
libraryBasePath,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class SaveManager {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
history: {
|
historyObjectIds: {
|
||||||
push: newSaveObjectId,
|
push: newSaveObjectId,
|
||||||
},
|
},
|
||||||
historyChecksums: {
|
historyChecksums: {
|
||||||
@@ -88,12 +88,12 @@ class SaveManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const historyLimit = await applicationSettings.get("saveSlotHistoryLimit");
|
const historyLimit = await applicationSettings.get("saveSlotHistoryLimit");
|
||||||
if (newSave.history.length > historyLimit) {
|
if (newSave.historyObjectIds.length > historyLimit) {
|
||||||
// Delete previous
|
// Delete previous
|
||||||
const safeFromIndex = newSave.history.length - historyLimit;
|
const safeFromIndex = newSave.historyObjectIds.length - historyLimit;
|
||||||
|
|
||||||
const toDelete = newSave.history.slice(0, safeFromIndex);
|
const toDelete = newSave.historyObjectIds.slice(0, safeFromIndex);
|
||||||
const toKeepObjects = newSave.history.slice(safeFromIndex);
|
const toKeepObjects = newSave.historyObjectIds.slice(safeFromIndex);
|
||||||
const toKeepHashes = newSave.historyChecksums.slice(safeFromIndex);
|
const toKeepHashes = newSave.historyChecksums.slice(safeFromIndex);
|
||||||
|
|
||||||
// Delete objects first, so if we error out, we don't lose track of objects in backend
|
// Delete objects first, so if we error out, we don't lose track of objects in backend
|
||||||
@@ -110,7 +110,7 @@ class SaveManager {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
history: toKeepObjects,
|
historyObjectIds: toKeepObjects,
|
||||||
historyChecksums: toKeepHashes,
|
historyChecksums: toKeepHashes,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user