mirror of
https://github.com/rommapp/romm.git
synced 2026-06-28 06:46:00 +00:00
Merge pull request #2610 from rommapp/scan-page-performance
Improve scan page performance on large lists
This commit is contained in:
@@ -169,3 +169,12 @@ const { t } = useI18n();
|
||||
</v-list-item>
|
||||
</v-expansion-panel-text>
|
||||
</template>
|
||||
<style scoped>
|
||||
.v-chip {
|
||||
contain: layout style paint;
|
||||
}
|
||||
|
||||
.v-avatar {
|
||||
contain: layout style paint;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,7 +16,10 @@ const fallbackCoverImage = computed(() =>
|
||||
|
||||
<template>
|
||||
<v-avatar :width="size" rounded="0">
|
||||
<v-img :src="props.rom.path_cover_small || fallbackCoverImage">
|
||||
<v-img
|
||||
:src="props.rom.path_cover_small || fallbackCoverImage"
|
||||
:alt="props.rom.name || props.rom.fs_name"
|
||||
>
|
||||
<template #error>
|
||||
<v-img :src="fallbackCoverImage" />
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { debounce } from "lodash";
|
||||
import type { Emitter } from "mitt";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { inject, onBeforeUnmount } from "vue";
|
||||
import { inject, onBeforeUnmount, ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { ScanStats } from "@/__generated__";
|
||||
import socket from "@/services/socket";
|
||||
@@ -35,6 +36,54 @@ const romsStore = storeRoms();
|
||||
// Connect to socket on load to catch running scans
|
||||
if (!socket.connected) socket.connect();
|
||||
|
||||
// Batch ROM updates to prevent excessive re-renders
|
||||
const romUpdateQueue = ref<SimpleRom[]>([]);
|
||||
const processRomUpdates = debounce(() => {
|
||||
if (romUpdateQueue.value.length === 0) return;
|
||||
|
||||
const updates = [...romUpdateQueue.value];
|
||||
romUpdateQueue.value = [];
|
||||
updates.forEach((rom) => {
|
||||
// Remove the ROM from the recent list and add it back to the top
|
||||
romsStore.removeFromRecent(rom);
|
||||
romsStore.addToRecent(rom);
|
||||
|
||||
if (romsStore.currentPlatform?.id === rom.platform_id) {
|
||||
const existingRom = romsStore.filteredRoms.find((r) => r.id === rom.id);
|
||||
existingRom ? romsStore.update(rom) : romsStore.add([rom]);
|
||||
}
|
||||
|
||||
let scannedPlatform = scanningPlatforms.value.find(
|
||||
(p) => p.slug === rom.platform_slug,
|
||||
);
|
||||
|
||||
// Add the platform if the socket dropped and it's missing
|
||||
if (!scannedPlatform) {
|
||||
scanningPlatforms.value.push({
|
||||
display_name: rom.platform_display_name,
|
||||
slug: rom.platform_slug,
|
||||
id: rom.platform_id,
|
||||
fs_slug: rom.platform_fs_slug,
|
||||
is_identified: true,
|
||||
roms: [],
|
||||
});
|
||||
scannedPlatform = scanningPlatforms.value.at(-1)!;
|
||||
}
|
||||
|
||||
// Check if ROM already exists in the store
|
||||
const existingRomInPlatform = scannedPlatform?.roms.find(
|
||||
(r) => r.id === rom.id,
|
||||
);
|
||||
if (existingRomInPlatform) {
|
||||
scannedPlatform.roms = scannedPlatform.roms.map((r) =>
|
||||
r.id === rom.id ? rom : r,
|
||||
);
|
||||
} else {
|
||||
scannedPlatform?.roms.push(rom);
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
|
||||
socket.on(
|
||||
"scan:scanning_platform",
|
||||
({
|
||||
@@ -68,45 +117,9 @@ socket.on(
|
||||
socket.on("scan:scanning_rom", (rom: SimpleRom) => {
|
||||
scanningStore.setScanning(true);
|
||||
|
||||
// Remove the ROM from the recent list and add it back to the top
|
||||
romsStore.removeFromRecent(rom);
|
||||
romsStore.addToRecent(rom);
|
||||
|
||||
if (romsStore.currentPlatform?.id === rom.platform_id) {
|
||||
const existingRom = romsStore.filteredRoms.find((r) => r.id === rom.id);
|
||||
if (existingRom) {
|
||||
romsStore.update(rom);
|
||||
} else {
|
||||
romsStore.add([rom]);
|
||||
}
|
||||
}
|
||||
|
||||
let scannedPlatform = scanningPlatforms.value.find(
|
||||
(p) => p.slug === rom.platform_slug,
|
||||
);
|
||||
|
||||
// Add the platform if the socket dropped and it's missing
|
||||
if (!scannedPlatform) {
|
||||
scanningPlatforms.value.push({
|
||||
display_name: rom.platform_display_name,
|
||||
slug: rom.platform_slug,
|
||||
id: rom.platform_id,
|
||||
fs_slug: rom.platform_fs_slug,
|
||||
is_identified: true,
|
||||
roms: [],
|
||||
});
|
||||
scannedPlatform = scanningPlatforms.value[0];
|
||||
}
|
||||
|
||||
// Check if ROM already exists in the store
|
||||
const existingRom = scannedPlatform?.roms.find((r) => r.id === rom.id);
|
||||
if (existingRom) {
|
||||
scannedPlatform.roms = scannedPlatform.roms.map((r) =>
|
||||
r.id === rom.id ? rom : r,
|
||||
);
|
||||
} else {
|
||||
scannedPlatform?.roms.push(rom);
|
||||
}
|
||||
// Queue ROM for batch processing instead of immediate update
|
||||
romUpdateQueue.value.push(rom);
|
||||
processRomUpdates();
|
||||
});
|
||||
|
||||
socket.on("scan:done", () => {
|
||||
@@ -142,6 +155,7 @@ onBeforeUnmount(() => {
|
||||
socket.off("scan:scanning_rom");
|
||||
socket.off("scan:done");
|
||||
socket.off("scan:done_ko");
|
||||
processRomUpdates.cancel();
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { throttle } from "lodash";
|
||||
import { onUnmounted, watchEffect, nextTick, type ShallowRef } from "vue";
|
||||
|
||||
export const useAutoScroll = (
|
||||
@@ -9,7 +10,7 @@ export const useAutoScroll = (
|
||||
let isUserScrolled = false;
|
||||
let observer: MutationObserver | null = null;
|
||||
|
||||
const scrollToBottom = () => {
|
||||
const scrollToBottom = throttle(() => {
|
||||
const containerEl = scrollContainer.value?.$el;
|
||||
if (!containerEl) return;
|
||||
|
||||
@@ -17,7 +18,7 @@ export const useAutoScroll = (
|
||||
top: containerEl.scrollHeight,
|
||||
behavior: config.smooth ? "smooth" : "instant",
|
||||
});
|
||||
};
|
||||
}, 50);
|
||||
|
||||
const init = () => {
|
||||
const containerEl = scrollContainer.value?.$el;
|
||||
@@ -31,18 +32,32 @@ export const useAutoScroll = (
|
||||
containerEl.scrollHeight;
|
||||
});
|
||||
|
||||
// Auto-scroll on content changes
|
||||
observer = new MutationObserver((e: MutationRecord[]) => {
|
||||
// Auto-scroll on content changes with throttled observer
|
||||
observer = new MutationObserver((mutations: MutationRecord[]) => {
|
||||
if (!config.always && isUserScrolled) return;
|
||||
if (e[e.length - 1].addedNodes.length === 0) return;
|
||||
scrollToBottom();
|
||||
|
||||
// Only process if there are actual node additions
|
||||
const hasNewNodes = mutations.some(
|
||||
(mutation) =>
|
||||
mutation.type === "childList" && mutation.addedNodes.length > 0,
|
||||
);
|
||||
|
||||
if (hasNewNodes) {
|
||||
scrollToBottom();
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(observedEl, { childList: true, subtree: config.deep });
|
||||
observer.observe(observedEl, {
|
||||
childList: true,
|
||||
subtree: config.deep,
|
||||
attributes: false,
|
||||
characterData: false,
|
||||
});
|
||||
scrollToBottom();
|
||||
};
|
||||
|
||||
const cleanup = () => {
|
||||
scrollToBottom.cancel();
|
||||
observer?.disconnect();
|
||||
observer = null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user