mirror of
https://github.com/rommapp/romm.git
synced 2026-02-18 00:27:41 +01:00
268 lines
8.4 KiB
Vue
268 lines
8.4 KiB
Vue
<script setup lang="ts">
|
|
import Excluded from "@/components/Settings/LibraryManagement/Config/Excluded.vue";
|
|
import PlatformBinding from "@/components/Settings/LibraryManagement/Config/PlatformBinding.vue";
|
|
import PlatformVersions from "@/components/Settings/LibraryManagement/Config/PlatformVersions.vue";
|
|
import GameTable from "@/components/common/Game/Table.vue";
|
|
import PlatformIcon from "@/components/common/Platform/Icon.vue";
|
|
import MissingFromFSIcon from "@/components/common/MissingFromFSIcon.vue";
|
|
import LoadMoreBtn from "@/components/Gallery/LoadMoreBtn.vue";
|
|
import FabOverlay from "@/components/Gallery/FabOverlay.vue";
|
|
import storeRoms, { MAX_FETCH_LIMIT } from "@/stores/roms";
|
|
import storeGalleryFilter from "@/stores/galleryFilter";
|
|
import type { Emitter } from "mitt";
|
|
import storeGalleryView from "@/stores/galleryView";
|
|
import storePlatforms from "@/stores/platforms";
|
|
import type { Events } from "@/types/emitter";
|
|
import { storeToRefs } from "pinia";
|
|
import { ref, onMounted, inject, onBeforeUnmount, computed } from "vue";
|
|
import { useI18n } from "vue-i18n";
|
|
import { debounce } from "lodash";
|
|
|
|
const { t } = useI18n();
|
|
const tab = ref<"config" | "missing">("config");
|
|
const romsStore = storeRoms();
|
|
const { allRoms, fetchingRoms, fetchTotalRoms, filteredRoms } =
|
|
storeToRefs(romsStore);
|
|
const galleryViewStore = storeGalleryView();
|
|
const { scrolledToTop } = storeToRefs(galleryViewStore);
|
|
const galleryFilterStore = storeGalleryFilter();
|
|
const { selectedPlatform } = storeToRefs(galleryFilterStore);
|
|
const platformsStore = storePlatforms();
|
|
const missingGamesLoading = ref(false);
|
|
const emitter = inject<Emitter<Events>>("emitter");
|
|
let timeout: ReturnType<typeof setTimeout> = setTimeout(() => {}, 400);
|
|
const allPlatforms = computed(() =>
|
|
[
|
|
...new Map(
|
|
allRoms.value
|
|
.map((rom) => platformsStore.get(rom.platform_id))
|
|
.filter((platform) => !!platform)
|
|
.map((platform) => [platform!.id, platform]),
|
|
).values(),
|
|
].sort((a, b) => a!.name.localeCompare(b!.name)),
|
|
);
|
|
|
|
const onFilterChange = debounce(
|
|
() => {
|
|
romsStore.resetPagination();
|
|
galleryFilterStore.setFilterMissing(true);
|
|
romsStore.fetchRoms(galleryFilterStore, false);
|
|
|
|
const url = new URL(window.location.href);
|
|
// Update URL with filters
|
|
Object.entries({
|
|
platform: selectedPlatform.value
|
|
? String(selectedPlatform.value.id)
|
|
: null,
|
|
}).forEach(([key, value]) => {
|
|
if (value) {
|
|
url.searchParams.set(key, value);
|
|
} else {
|
|
url.searchParams.delete(key);
|
|
}
|
|
});
|
|
galleryFilterStore.setFilterMissing(false);
|
|
},
|
|
500,
|
|
// If leading and trailing options are true, this is invoked on the trailing edge of
|
|
// the timeout only if the the function is invoked more than once during the wait
|
|
{ leading: true, trailing: true },
|
|
);
|
|
|
|
async function fetchRoms() {
|
|
if (fetchingRoms.value) return;
|
|
|
|
emitter?.emit("showLoadingDialog", {
|
|
loading: true,
|
|
scrim: false,
|
|
});
|
|
|
|
galleryFilterStore.setFilterMissing(true);
|
|
romsStore
|
|
.fetchRoms(galleryFilterStore, false)
|
|
.then(() => {
|
|
emitter?.emit("showLoadingDialog", {
|
|
loading: false,
|
|
scrim: false,
|
|
});
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error fetching missing games:", error);
|
|
emitter?.emit("snackbarShow", {
|
|
msg: `Couldn't fetch missing ROMs: ${error}`,
|
|
icon: "mdi-close-circle",
|
|
color: "red",
|
|
timeout: 4000,
|
|
});
|
|
})
|
|
.finally(() => {
|
|
galleryFilterStore.setFilterMissing(false);
|
|
if (romsStore.fetchOffset === romsStore.fetchLimit) {
|
|
missingGamesLoading.value = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
function cleanupAll() {
|
|
romsStore.setLimit(MAX_FETCH_LIMIT);
|
|
galleryFilterStore.setFilterMissing(true);
|
|
romsStore
|
|
.fetchRoms(galleryFilterStore, false)
|
|
.then(() => {
|
|
emitter?.emit("showLoadingDialog", {
|
|
loading: false,
|
|
scrim: false,
|
|
});
|
|
emitter?.emit("showDeleteRomDialog", romsStore.filteredRoms);
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error fetching missing games:", error);
|
|
emitter?.emit("snackbarShow", {
|
|
msg: `Couldn't fetch missing ROMs: ${error}`,
|
|
icon: "mdi-close-circle",
|
|
color: "red",
|
|
timeout: 4000,
|
|
});
|
|
})
|
|
.finally(() => {
|
|
galleryFilterStore.setFilterMissing(false);
|
|
});
|
|
}
|
|
|
|
function resetMissingRoms() {
|
|
romsStore.reset();
|
|
galleryFilterStore.resetFilters();
|
|
}
|
|
|
|
function onScroll() {
|
|
clearTimeout(timeout);
|
|
|
|
window.setTimeout(async () => {
|
|
scrolledToTop.value = window.scrollY === 0;
|
|
if (
|
|
window.innerHeight + window.scrollY >= document.body.offsetHeight - 60 &&
|
|
fetchTotalRoms.value > filteredRoms.value.length
|
|
) {
|
|
await fetchRoms();
|
|
}
|
|
}, 100);
|
|
}
|
|
|
|
onMounted(() => {
|
|
resetMissingRoms();
|
|
fetchRoms();
|
|
window.addEventListener("scroll", onScroll);
|
|
});
|
|
|
|
onBeforeUnmount(() => {
|
|
resetMissingRoms();
|
|
window.removeEventListener("scroll", onScroll);
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<v-row no-gutters class="pa-2">
|
|
<v-col cols="12">
|
|
<v-tabs
|
|
v-model="tab"
|
|
align-tabs="start"
|
|
slider-color="secondary"
|
|
selected-class="bg-toplayer"
|
|
>
|
|
<v-tab prepend-icon="mdi-cog" class="rounded" value="config"
|
|
>Config</v-tab
|
|
>
|
|
<v-tab
|
|
prepend-icon="mdi-folder-question"
|
|
class="rounded"
|
|
value="missing"
|
|
>Missing games</v-tab
|
|
>
|
|
</v-tabs>
|
|
</v-col>
|
|
<v-col>
|
|
<v-tabs-window v-model="tab">
|
|
<v-tabs-window-item value="config">
|
|
<platform-binding class="mt-2" />
|
|
<platform-versions class="mt-4" />
|
|
<excluded class="mt-4" />
|
|
</v-tabs-window-item>
|
|
<v-tabs-window-item value="missing">
|
|
<v-row class="mt-2 mr-2 align-center" no-gutters>
|
|
<v-col>
|
|
<v-select
|
|
class="mx-2"
|
|
v-model="selectedPlatform"
|
|
hide-details
|
|
prepend-inner-icon="mdi-controller"
|
|
clearable
|
|
:label="t('common.platform')"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
:items="allPlatforms"
|
|
@update:model-value="onFilterChange"
|
|
>
|
|
<template #item="{ props, item }">
|
|
<v-list-item
|
|
v-bind="props"
|
|
class="py-4"
|
|
:title="item.raw.name ?? ''"
|
|
:subtitle="item.raw.fs_slug"
|
|
>
|
|
<template #prepend>
|
|
<platform-icon
|
|
:key="item.raw.slug"
|
|
:size="35"
|
|
:slug="item.raw.slug"
|
|
:name="item.raw.name"
|
|
:fs-slug="item.raw.fs_slug"
|
|
/>
|
|
</template>
|
|
<template #append>
|
|
<missing-from-f-s-icon
|
|
v-if="item.raw.missing_from_fs"
|
|
text="Missing platform from filesystem"
|
|
chip
|
|
chip-label
|
|
chipDensity="compact"
|
|
class="ml-2"
|
|
/>
|
|
<v-chip class="ml-2" size="x-small" label>
|
|
{{ item.raw.rom_count }}
|
|
</v-chip>
|
|
</template>
|
|
</v-list-item>
|
|
</template>
|
|
<template #chip="{ item }">
|
|
<platform-icon
|
|
:key="item.raw.slug"
|
|
:slug="item.raw.slug"
|
|
:name="item.raw.name"
|
|
:fs-slug="item.raw.fs_slug"
|
|
:size="20"
|
|
class="mx-2"
|
|
/>
|
|
{{ item.raw.name }}
|
|
</template>
|
|
</v-select>
|
|
</v-col>
|
|
<v-col cols="auto">
|
|
<v-btn
|
|
prepend-icon="mdi-delete"
|
|
size="large"
|
|
class="text-romm-red bg-toplayer"
|
|
variant="flat"
|
|
@click="cleanupAll"
|
|
>Clean up all</v-btn
|
|
>
|
|
</v-col>
|
|
</v-row>
|
|
<game-table class="mx-2 mt-2" show-platform-icon />
|
|
<load-more-btn :fetchRoms="fetchRoms" />
|
|
<fab-overlay />
|
|
</v-tabs-window-item>
|
|
</v-tabs-window>
|
|
</v-col>
|
|
</v-row>
|
|
</template>
|