From ad61d313d48c896caf4b73775b1026c4b6b3182d Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Fri, 22 Aug 2025 16:15:45 -0400 Subject: [PATCH 01/15] Add AI guideline to contributing.md --- CONTRIBUTING.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6d27e047f..d1b9c492c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,6 +8,29 @@ Thank you for considering contributing to RomM! This document outlines some guid Please note that this project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md). By participating in this project, you are expected to uphold this code. +## AI Assistance Notice + +> [!IMPORTANT] +> +> If you are using **any kind of AI assistance** to contribute to RomM, it must be disclosed in the pull request. + +If you are using any kind of AI assistance while contributing to RomM **this must be disclosed in the pull request**, along with the extent to which AI assistance was used (e.g. docs only vs. code generation). If PR responses are being generated by an AI, disclose that as well. As a small exception, trivial tab-completion doesn't need to be disclosed. + +An example disclosure: + +> This PR was written primarily by Claude Code. + +Or a more detailed disclosure: + +> I consulted ChatGPT to understand the codebase but the solution +> was fully authored manually by myself. + +Failure to disclose this is rude to the human operators on the other end of the pull request, but it also makes it difficult to determine how much scrutiny to apply to the contribution. + +In a perfect world, AI assistance would produce equal or higher quality work than any human. That isn't the world we live in today, and in most cases it's generating slop. + +Please be respectful to maintainers and disclose AI assistance. + ## Contributing to the Docs If you would like to contribute to the project's [documentation](https://docs.romm.app), open a pull request against [the docs repo](https://github.com/rommapp/docs). We welcome any contributions that help improve the documentation (new pages, updates, or corrections). From 7c8fc6936a7ea93266086cdc64e84715dfc999a4 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Fri, 22 Aug 2025 22:26:07 -0400 Subject: [PATCH 02/15] Delay requests for global collections --- .../AppBar/common/FilterDrawer/Base.vue | 4 +- .../components/common/NewVersionDialog.vue | 11 ++++- frontend/src/layouts/Main.vue | 26 +++++++----- frontend/src/stores/roms.ts | 29 +++++++++++++ .../Gallery/Collection/BaseCollection.vue | 4 +- frontend/src/views/Gallery/Platform.vue | 4 +- frontend/src/views/Home.vue | 41 +++++-------------- 7 files changed, 70 insertions(+), 49 deletions(-) diff --git a/frontend/src/components/Gallery/AppBar/common/FilterDrawer/Base.vue b/frontend/src/components/Gallery/AppBar/common/FilterDrawer/Base.vue index bbdf5cbb7..1c11a6ed7 100644 --- a/frontend/src/components/Gallery/AppBar/common/FilterDrawer/Base.vue +++ b/frontend/src/components/Gallery/AppBar/common/FilterDrawer/Base.vue @@ -16,7 +16,7 @@ import storePlatforms from "@/stores/platforms"; import type { Events } from "@/types/emitter"; import type { Emitter } from "mitt"; import { storeToRefs } from "pinia"; -import { inject, nextTick, onMounted, watch } from "vue"; +import { inject, nextTick, onBeforeMount, watch } from "vue"; import { useDisplay } from "vuetify"; import { useI18n } from "vue-i18n"; import { useRouter } from "vue-router"; @@ -210,7 +210,7 @@ function setFilters() { ]); } -onMounted(async () => { +onBeforeMount(async () => { const { search: urlSearch, filterMatched: urlFilteredMatch, diff --git a/frontend/src/components/common/NewVersionDialog.vue b/frontend/src/components/common/NewVersionDialog.vue index 09869ac9c..e6c7dc71f 100644 --- a/frontend/src/components/common/NewVersionDialog.vue +++ b/frontend/src/components/common/NewVersionDialog.vue @@ -2,7 +2,6 @@ import storeHeartbeat from "@/stores/heartbeat"; import semver from "semver"; import { onMounted, ref } from "vue"; -import { useDisplay } from "vuetify"; const heartbeat = storeHeartbeat(); const { VERSION } = heartbeat.value.SYSTEM; @@ -13,13 +12,15 @@ function dismissVersionBanner() { localStorage.setItem("dismissedVersion", GITHUB_VERSION.value); latestVersionDismissed.value = true; } -onMounted(async () => { + +async function fetchLatestVersion() { try { const response = await fetch( "https://api.github.com/repos/rommapp/romm/releases/latest", ); const json = await response.json(); GITHUB_VERSION.value = json.tag_name; + const publishedAt = new Date(json.published_at); latestVersionDismissed.value = // Hide if the version is not valid @@ -31,6 +32,12 @@ onMounted(async () => { } catch (error) { console.error("Failed to fetch latest version from Github", error); } + + document.removeEventListener("network-quiesced", fetchLatestVersion); +} + +onMounted(async () => { + document.addEventListener("network-quiesced", fetchLatestVersion); }); diff --git a/frontend/src/layouts/Main.vue b/frontend/src/layouts/Main.vue index b3840fd7b..bfdb7874a 100644 --- a/frontend/src/layouts/Main.vue +++ b/frontend/src/layouts/Main.vue @@ -23,7 +23,7 @@ import storeNavigation from "@/stores/navigation"; import storePlatforms from "@/stores/platforms"; import type { Events } from "@/types/emitter"; import type { Emitter } from "mitt"; -import { inject, onBeforeMount, ref } from "vue"; +import { inject, onBeforeMount, onMounted, ref } from "vue"; import { isNull } from "lodash"; const navigationStore = storeNavigation(); @@ -49,18 +49,24 @@ const virtualCollectionTypeRef = ref( : storedVirtualCollectionType, ); -onBeforeMount(async () => { - await Promise.all([ - platformsStore.fetchPlatforms(), - collectionsStore.fetchCollections(), - collectionsStore.fetchSmartCollections(), - showVirtualCollections - ? collectionsStore.fetchVirtualCollections(virtualCollectionTypeRef.value) - : Promise.resolve(), - ]); +function fetchData() { + platformsStore.fetchPlatforms(); + collectionsStore.fetchCollections(); + collectionsStore.fetchSmartCollections(); + showVirtualCollections + ? collectionsStore.fetchVirtualCollections(virtualCollectionTypeRef.value) + : Promise.resolve(); navigationStore.reset(); + document.removeEventListener("network-quiesced", fetchData); +} + +onBeforeMount(() => { + document.addEventListener("network-quiesced", fetchData); +}); + +onMounted(() => { // Hack to prevent main page transition on first load const main = document.getElementById("main"); if (main) main.classList.remove("no-transition"); diff --git a/frontend/src/stores/roms.ts b/frontend/src/stores/roms.ts index 06c13cae5..52240933c 100644 --- a/frontend/src/stores/roms.ts +++ b/frontend/src/stores/roms.ts @@ -201,6 +201,35 @@ export default defineStore("roms", { }); }); }, + fetchRecentRoms(): Promise { + return new Promise((resolve, reject) => { + romApi + .getRecentRoms() + .then(({ data: { items } }) => { + this.setRecentRoms(items); + resolve(items); + }) + .catch((error) => { + reject(error); + }); + }); + }, + fetchContinuePlayingRoms(): Promise { + return new Promise((resolve, reject) => { + romApi + .getRecentPlayedRoms() + .then(({ data: { items } }) => { + const filteredItems = items.filter( + (rom) => rom.rom_user.last_played, + ); + this.setContinuePlayingRoms(filteredItems); + resolve(items); + }) + .catch((error) => { + reject(error); + }); + }); + }, add(roms: SimpleRom[]) { this.allRoms = this.allRoms.concat(roms); }, diff --git a/frontend/src/views/Gallery/Collection/BaseCollection.vue b/frontend/src/views/Gallery/Collection/BaseCollection.vue index fd69c4a79..20a2f5a19 100644 --- a/frontend/src/views/Gallery/Collection/BaseCollection.vue +++ b/frontend/src/views/Gallery/Collection/BaseCollection.vue @@ -16,7 +16,7 @@ import { views } from "@/utils"; import { isNull, throttle } from "lodash"; import type { Emitter } from "mitt"; import { storeToRefs } from "pinia"; -import { inject, onBeforeUnmount, onMounted, ref, watch } from "vue"; +import { inject, onBeforeMount, onBeforeUnmount, ref, watch } from "vue"; import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router"; const props = defineProps<{ @@ -163,7 +163,7 @@ function resetGallery() { noCollectionError.value = false; } -onMounted(async () => { +onBeforeMount(async () => { const routeCollectionId = route.params.collection; currentPlatform.value = null; diff --git a/frontend/src/views/Gallery/Platform.vue b/frontend/src/views/Gallery/Platform.vue index ce851f305..6dee76e35 100644 --- a/frontend/src/views/Gallery/Platform.vue +++ b/frontend/src/views/Gallery/Platform.vue @@ -16,7 +16,7 @@ import { views } from "@/utils"; import type { Emitter } from "mitt"; import { isNull, throttle } from "lodash"; import { storeToRefs } from "pinia"; -import { inject, onBeforeUnmount, onMounted, ref, watch } from "vue"; +import { inject, onBeforeMount, onBeforeUnmount, ref, watch } from "vue"; import { onBeforeRouteUpdate, useRoute } from "vue-router"; const route = useRoute(); @@ -161,7 +161,7 @@ function resetGallery() { noPlatformError.value = false; } -onMounted(async () => { +onBeforeMount(async () => { const routePlatformId = Number(route.params.platform); currentCollection.value = null; diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue index ad7abbabe..385d1b475 100644 --- a/frontend/src/views/Home.vue +++ b/frontend/src/views/Home.vue @@ -1,5 +1,5 @@ From 14cf963a2ded7168016abd695e50f749b8c94e75 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Fri, 22 Aug 2025 22:55:11 -0400 Subject: [PATCH 03/15] some funny business --- frontend/src/layouts/Main.vue | 42 +++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/frontend/src/layouts/Main.vue b/frontend/src/layouts/Main.vue index bfdb7874a..7ebc31d10 100644 --- a/frontend/src/layouts/Main.vue +++ b/frontend/src/layouts/Main.vue @@ -25,10 +25,15 @@ import type { Events } from "@/types/emitter"; import type { Emitter } from "mitt"; import { inject, onBeforeMount, onMounted, ref } from "vue"; import { isNull } from "lodash"; +import { useRoute } from "vue-router"; +import { ROUTES } from "@/plugins/router"; +const route = useRoute(); const navigationStore = storeNavigation(); const platformsStore = storePlatforms(); const collectionsStore = storeCollections(); +const fetchedType = ref(null); + const emitter = inject>("emitter"); emitter?.on("refreshDrawer", async () => { platformsStore.fetchPlatforms(); @@ -50,20 +55,43 @@ const virtualCollectionTypeRef = ref( ); function fetchData() { - platformsStore.fetchPlatforms(); - collectionsStore.fetchCollections(); - collectionsStore.fetchSmartCollections(); - showVirtualCollections - ? collectionsStore.fetchVirtualCollections(virtualCollectionTypeRef.value) - : Promise.resolve(); + if (fetchedType.value !== "platform") { + platformsStore.fetchPlatforms(); + } + if (fetchedType.value !== "collection") { + collectionsStore.fetchCollections(); + } + if (fetchedType.value !== "smart") { + collectionsStore.fetchSmartCollections(); + } + if (fetchedType.value !== "virtual") { + collectionsStore.fetchVirtualCollections(virtualCollectionTypeRef.value); + } navigationStore.reset(); document.removeEventListener("network-quiesced", fetchData); } -onBeforeMount(() => { +onBeforeMount(async () => { document.addEventListener("network-quiesced", fetchData); + + if (route.name == ROUTES.COLLECTION) { + collectionsStore.fetchCollections(); + fetchedType.value = "collection"; + } else if ( + showVirtualCollections && + route.name == ROUTES.VIRTUAL_COLLECTION + ) { + collectionsStore.fetchVirtualCollections(virtualCollectionTypeRef.value); + fetchedType.value = "virtual"; + } else if (route.name == ROUTES.SMART_COLLECTION) { + collectionsStore.fetchSmartCollections(); + fetchedType.value = "smart"; + } else { + platformsStore.fetchPlatforms(); + fetchedType.value = "platform"; + } }); onMounted(() => { From 4fbd8a2da0412e3069695e634c84f98f2c42a5c8 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 23 Aug 2025 07:55:50 -0400 Subject: [PATCH 04/15] fix hack --- frontend/src/layouts/Main.vue | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/frontend/src/layouts/Main.vue b/frontend/src/layouts/Main.vue index 7ebc31d10..7bf1e71ec 100644 --- a/frontend/src/layouts/Main.vue +++ b/frontend/src/layouts/Main.vue @@ -23,7 +23,7 @@ import storeNavigation from "@/stores/navigation"; import storePlatforms from "@/stores/platforms"; import type { Events } from "@/types/emitter"; import type { Emitter } from "mitt"; -import { inject, onBeforeMount, onMounted, ref } from "vue"; +import { inject, onBeforeMount, ref } from "vue"; import { isNull } from "lodash"; import { useRoute } from "vue-router"; import { ROUTES } from "@/plugins/router"; @@ -55,6 +55,8 @@ const virtualCollectionTypeRef = ref( ); function fetchData() { + document.removeEventListener("network-quiesced", fetchData); + if (fetchedType.value !== "platform") { platformsStore.fetchPlatforms(); } @@ -70,7 +72,9 @@ function fetchData() { navigationStore.reset(); - document.removeEventListener("network-quiesced", fetchData); + // Hack to prevent main page transition on first load + const main = document.getElementById("main"); + if (main) main.classList.remove("no-transition"); } onBeforeMount(async () => { @@ -93,12 +97,6 @@ onBeforeMount(async () => { fetchedType.value = "platform"; } }); - -onMounted(() => { - // Hack to prevent main page transition on first load - const main = document.getElementById("main"); - if (main) main.classList.remove("no-transition"); -}); + From bc6ad0f20f6c48653714416aa5694dd774be1b7e Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 23 Aug 2025 13:46:39 -0400 Subject: [PATCH 12/15] cleanup --- frontend/index.html | 7 ------- frontend/src/RomM.vue | 3 ++- frontend/src/layouts/Main.vue | 2 ++ frontend/src/views/Gallery/Collection/BaseCollection.vue | 2 +- frontend/src/views/Gallery/Platform.vue | 2 +- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 31636be2b..f3539d56d 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -25,13 +25,6 @@