mirror of
https://github.com/rommapp/romm.git
synced 2026-06-28 06:46:00 +00:00
248 lines
7.4 KiB
Vue
248 lines
7.4 KiB
Vue
<script setup lang="ts">
|
|
import type { Emitter } from "mitt";
|
|
import { computed, inject, ref } from "vue";
|
|
import { useI18n } from "vue-i18n";
|
|
import { useRouter } from "vue-router";
|
|
import { useDisplay } from "vuetify";
|
|
import CollectionCard from "@/components/common/Collection/Card.vue";
|
|
import RDialog from "@/components/common/RDialog.vue";
|
|
import { ROUTES } from "@/plugins/router";
|
|
import collectionApi, {
|
|
type UpdatedCollection,
|
|
} from "@/services/api/collection";
|
|
import storeCollections from "@/stores/collections";
|
|
import storeHeartbeat from "@/stores/heartbeat";
|
|
import type { Events } from "@/types/emitter";
|
|
import { getMissingCoverImage } from "@/utils/covers";
|
|
|
|
const { t } = useI18n();
|
|
const { mdAndUp } = useDisplay();
|
|
const router = useRouter();
|
|
const show = ref(false);
|
|
const heartbeat = storeHeartbeat();
|
|
const collection = ref<UpdatedCollection>({ name: "" } as UpdatedCollection);
|
|
const collectionsStore = storeCollections();
|
|
const imagePreviewUrl = ref<string | undefined>("");
|
|
const removeCover = ref(false);
|
|
const emitter = inject<Emitter<Events>>("emitter");
|
|
emitter?.on("showCreateCollectionDialog", () => {
|
|
show.value = true;
|
|
removeCover.value = false;
|
|
});
|
|
emitter?.on("updateUrlCover", (coverUrl) => {
|
|
setArtwork(coverUrl);
|
|
});
|
|
|
|
const missingCoverImage = computed(() =>
|
|
getMissingCoverImage(collection.value.name),
|
|
);
|
|
|
|
function triggerFileInput() {
|
|
const fileInput = document.getElementById("file-input");
|
|
fileInput?.click();
|
|
}
|
|
|
|
function previewImage(event: Event) {
|
|
const input = event.target as HTMLInputElement;
|
|
if (!input.files) return;
|
|
|
|
const reader = new FileReader();
|
|
reader.onload = () => {
|
|
setArtwork(reader.result?.toString() || "");
|
|
};
|
|
if (input.files[0]) {
|
|
reader.readAsDataURL(input.files[0]);
|
|
}
|
|
}
|
|
|
|
function setArtwork(coverUrl: string) {
|
|
if (!coverUrl || !collection.value) return;
|
|
collection.value.url_cover = coverUrl;
|
|
imagePreviewUrl.value = coverUrl;
|
|
removeCover.value = false;
|
|
}
|
|
|
|
async function removeArtwork() {
|
|
imagePreviewUrl.value = missingCoverImage.value;
|
|
removeCover.value = true;
|
|
}
|
|
|
|
async function createCollection() {
|
|
if (!collection.value) return;
|
|
|
|
emitter?.emit("showLoadingDialog", { loading: true, scrim: true });
|
|
|
|
try {
|
|
const { data } = await collectionApi.createCollection({
|
|
collection: collection.value,
|
|
});
|
|
|
|
emitter?.emit("snackbarShow", {
|
|
msg: `Collection ${data.name} created successfully!`,
|
|
icon: "mdi-check-bold",
|
|
color: "green",
|
|
timeout: 2000,
|
|
});
|
|
collectionsStore.addCollection(data);
|
|
if (data.name.toLowerCase() == "favourites") {
|
|
collectionsStore.setFavoriteCollection(data);
|
|
}
|
|
emitter?.emit("showLoadingDialog", { loading: false, scrim: false });
|
|
router.push({ name: ROUTES.COLLECTION, params: { collection: data.id } });
|
|
closeDialog();
|
|
} catch (error: any) {
|
|
console.log(error);
|
|
emitter?.emit("snackbarShow", {
|
|
msg: error.response.data.detail,
|
|
icon: "mdi-close-circle",
|
|
color: "red",
|
|
});
|
|
}
|
|
}
|
|
|
|
function closeDialog() {
|
|
show.value = false;
|
|
collection.value = {} as UpdatedCollection;
|
|
imagePreviewUrl.value = "";
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<r-dialog
|
|
@close="closeDialog"
|
|
v-model="show"
|
|
icon="mdi-bookmark-box-multiple"
|
|
:width="mdAndUp ? '45vw' : '95vw'"
|
|
>
|
|
<template #header>
|
|
<v-card-title>{{ t("collection.create-collection") }}</v-card-title>
|
|
</template>
|
|
<template #content>
|
|
<v-row class="align-center pa-2" no-gutters>
|
|
<v-col cols="12" lg="7" xl="9">
|
|
<v-row class="pa-2" no-gutters>
|
|
<v-col>
|
|
<v-text-field
|
|
v-model="collection.name"
|
|
:label="t('collection.name')"
|
|
variant="outlined"
|
|
required
|
|
hide-details
|
|
@keyup.enter="createCollection"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
<v-row class="pa-2" no-gutters>
|
|
<v-col>
|
|
<v-text-field
|
|
v-model="collection.description"
|
|
class="mt-1"
|
|
:label="t('collection.description')"
|
|
variant="outlined"
|
|
required
|
|
hide-details
|
|
@keyup.enter="createCollection"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
<v-row class="pa-2" no-gutters>
|
|
<v-col>
|
|
<v-switch
|
|
v-model="collection.is_public"
|
|
:label="t('collection.public-desc')"
|
|
color="primary"
|
|
hide-details
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
</v-col>
|
|
<v-col>
|
|
<v-row class="pa-2 justify-center" no-gutters>
|
|
<v-col class="cover">
|
|
<collection-card
|
|
:key="collection.updated_at"
|
|
:show-title="false"
|
|
:with-link="false"
|
|
:collection="collection"
|
|
:coverSrc="imagePreviewUrl"
|
|
title-on-hover
|
|
>
|
|
<template #append-inner>
|
|
<v-btn-group divided density="compact">
|
|
<v-btn
|
|
:disabled="
|
|
!heartbeat.value.METADATA_SOURCES
|
|
?.STEAMGRIDDB_API_ENABLED
|
|
"
|
|
size="small"
|
|
class="translucent"
|
|
@click="
|
|
emitter?.emit('showSearchCoverDialog', {
|
|
term: collection.name as string,
|
|
aspectRatio: null,
|
|
})
|
|
"
|
|
>
|
|
<v-icon size="large">mdi-image-search-outline</v-icon>
|
|
</v-btn>
|
|
<v-btn
|
|
size="small"
|
|
class="translucent"
|
|
@click="triggerFileInput"
|
|
>
|
|
<v-icon size="large">mdi-pencil</v-icon>
|
|
<v-file-input
|
|
id="file-input"
|
|
v-model="collection.artwork"
|
|
accept="image/*"
|
|
hide-details
|
|
class="file-input"
|
|
@change="previewImage"
|
|
/>
|
|
</v-btn>
|
|
<v-btn
|
|
size="small"
|
|
class="translucent"
|
|
@click="removeArtwork"
|
|
>
|
|
<v-icon size="large" class="text-romm-red"
|
|
>mdi-delete</v-icon
|
|
>
|
|
</v-btn>
|
|
</v-btn-group>
|
|
</template>
|
|
</collection-card>
|
|
</v-col>
|
|
</v-row>
|
|
</v-col>
|
|
</v-row>
|
|
</template>
|
|
<template #append>
|
|
<v-divider />
|
|
<v-row class="justify-center pa-2" no-gutters>
|
|
<v-btn-group divided density="compact">
|
|
<v-btn class="bg-toplayer" @click="closeDialog">
|
|
{{ t("common.cancel") }}
|
|
</v-btn>
|
|
<v-btn
|
|
class="bg-toplayer text-romm-green"
|
|
:disabled="!collection.name"
|
|
:variant="!collection.name ? 'plain' : 'flat'"
|
|
@click="createCollection"
|
|
>
|
|
{{ t("common.create") }}
|
|
</v-btn>
|
|
</v-btn-group>
|
|
</v-row>
|
|
</template>
|
|
</r-dialog>
|
|
</template>
|
|
<style scoped>
|
|
.cover {
|
|
min-width: 240px;
|
|
min-height: 330px;
|
|
max-width: 240px;
|
|
max-height: 330px;
|
|
}
|
|
</style>
|