display recent played on homepage

This commit is contained in:
Georges-Antoine Assi
2025-10-21 11:03:19 -04:00
parent 109f781e94
commit beae5c682d
4 changed files with 102 additions and 64 deletions

View File

@@ -2,7 +2,7 @@
import { computed, onMounted, useTemplateRef, watch } from "vue";
import Skeleton from "@/components/common/Game/Card/Skeleton.vue";
import {
recentElementRegistry,
continuePlayingElementRegistry,
gamesListElementRegistry,
} from "@/console/composables/useElementRegistry";
import storeCollections from "@/stores/collections";
@@ -19,8 +19,8 @@ const props = defineProps<{
index: number;
selected?: boolean;
loaded?: boolean;
isRecent?: boolean;
registry?: "recent" | "gamesList";
continuePlaying?: boolean;
registry?: "continuePlaying" | "gamesList";
}>();
const heartbeatStore = storeHeartbeat();
@@ -84,7 +84,10 @@ onMounted(() => {
if (props.registry === "gamesList") {
gamesListElementRegistry.registerElement(props.index, gameCardRef.value);
} else {
recentElementRegistry.registerElement(props.index, gameCardRef.value);
continuePlayingElementRegistry.registerElement(
props.index,
gameCardRef.value,
);
}
});
</script>
@@ -96,7 +99,7 @@ onMounted(() => {
:class="{
'-translate-y-[2px] scale-[1.03] shadow-[0_8px_28px_rgba(0,0,0,0.35),_0_0_0_2px_var(--console-game-card-focus-border),_0_0_16px_var(--console-game-card-focus-border)]':
selected,
'w-[250px] shrink-0': isRecent,
'w-[250px] shrink-0': continuePlaying,
}"
@click="emit('click')"
@focus="emit('focus')"

View File

@@ -25,7 +25,7 @@ export function useElementRegistry() {
// Create a shared registry instance for each section
export const systemElementRegistry = useElementRegistry();
export const recentElementRegistry = useElementRegistry();
export const continuePlayingElementRegistry = useElementRegistry();
export const collectionElementRegistry = useElementRegistry();
export const smartCollectionElementRegistry = useElementRegistry();
export const virtualCollectionElementRegistry = useElementRegistry();

View File

@@ -7,6 +7,7 @@ import {
nextTick,
watch,
useTemplateRef,
onBeforeMount,
} from "vue";
import { useRouter } from "vue-router";
import RIsotipo from "@/components/common/RIsotipo.vue";
@@ -19,7 +20,7 @@ import SystemCard from "@/console/components/SystemCard.vue";
import useBackgroundArt from "@/console/composables/useBackgroundArt";
import {
systemElementRegistry,
recentElementRegistry,
continuePlayingElementRegistry,
collectionElementRegistry,
smartCollectionElementRegistry,
virtualCollectionElementRegistry,
@@ -42,12 +43,12 @@ const collectionsStore = storeCollections();
const { allCollections, smartCollections, virtualCollections } =
storeToRefs(collectionsStore);
const romsStore = storeRoms();
const { recentRoms } = storeToRefs(romsStore);
const { continuePlayingRoms } = storeToRefs(romsStore);
const consoleStore = storeConsole();
const {
navigationMode,
platformIndex,
recentIndex,
continuePlayingIndex,
collectionsIndex,
smartCollectionsIndex,
virtualCollectionsIndex,
@@ -66,7 +67,9 @@ const scrollContainerRef = useTemplateRef<HTMLDivElement>(
"scroll-container-ref",
);
const platformsRef = useTemplateRef<HTMLDivElement>("platforms-ref");
const recentRef = useTemplateRef<HTMLDivElement>("recent-ref");
const continuePlayingRef = useTemplateRef<HTMLDivElement>(
"continue-playing-ref",
);
const collectionsRef = useTemplateRef<HTMLDivElement>("collections-ref");
const smartCollectionsRef = useTemplateRef<HTMLDivElement>(
"smart-collections-ref",
@@ -74,7 +77,9 @@ const smartCollectionsRef = useTemplateRef<HTMLDivElement>(
const virtualCollectionsRef = useTemplateRef<HTMLDivElement>(
"virtual-collections-ref",
);
const recentSectionRef = useTemplateRef<HTMLElement>("recent-section-ref");
const continuePlayingSectionRef = useTemplateRef<HTMLElement>(
"continue-playing-section-ref",
);
const collectionsSectionRef = useTemplateRef<HTMLElement>(
"collections-section-ref",
);
@@ -86,7 +91,8 @@ const virtualCollectionsSectionRef = useTemplateRef<HTMLElement>(
);
const systemElementAt = (i: number) => systemElementRegistry.getElement(i);
const recentElementAt = (i: number) => recentElementRegistry.getElement(i);
const continuePlayingElementAt = (i: number) =>
continuePlayingElementRegistry.getElement(i);
const collectionElementAt = (i: number) =>
collectionElementRegistry.getElement(i);
const smartCollectionElementAt = (i: number) =>
@@ -100,10 +106,13 @@ const { moveLeft: moveSystemLeft, moveRight: moveSystemRight } = useSpatialNav(
() => allPlatforms.value.length || 1,
() => allPlatforms.value.length,
);
const { moveLeft: moveRecentLeft, moveRight: moveRecentRight } = useSpatialNav(
recentIndex,
() => recentRoms.value.length || 1,
() => recentRoms.value.length,
const {
moveLeft: moveContinuePlayingLeft,
moveRight: moveContinuePlayingRight,
} = useSpatialNav(
continuePlayingIndex,
() => continuePlayingRoms.value.length || 1,
() => continuePlayingRoms.value.length,
);
const { moveLeft: moveCollectionLeft, moveRight: moveCollectionRight } =
useSpatialNav(
@@ -134,7 +143,7 @@ useRovingDom(platformIndex, systemElementAt, {
behavior: "smooth",
scroll: false, // handle scrolling manually
});
useRovingDom(recentIndex, recentElementAt, {
useRovingDom(continuePlayingIndex, continuePlayingElementAt, {
inline: "center",
block: "nearest",
behavior: "smooth",
@@ -169,11 +178,11 @@ watch(platformIndex, (newIdx) => {
}
});
watch(recentIndex, (newIdx) => {
watch(continuePlayingIndex, (newIdx) => {
if (!isVerticalScrolling) {
const el = recentElementAt(newIdx);
if (el && recentRef.value) {
centerInCarousel(recentRef.value, el, "smooth");
const el = continuePlayingElementAt(newIdx);
if (el && continuePlayingRef.value) {
centerInCarousel(continuePlayingRef.value, el, "smooth");
}
}
});
@@ -231,27 +240,34 @@ const navigationFunctions = {
return true;
},
},
recent: {
continuePlaying: {
prev: () => {
const before = recentIndex.value;
moveRecentLeft();
if (recentIndex.value === before) {
recentIndex.value = Math.max(0, recentRoms.value.length - 1);
const before = continuePlayingIndex.value;
moveContinuePlayingLeft();
if (continuePlayingIndex.value === before) {
continuePlayingIndex.value = Math.max(
0,
continuePlayingRoms.value.length - 1,
);
}
},
next: () => {
const before = recentIndex.value;
moveRecentRight();
if (recentIndex.value === before) {
recentIndex.value = 0;
const before = continuePlayingIndex.value;
moveContinuePlayingRight();
if (continuePlayingIndex.value === before) {
continuePlayingIndex.value = 0;
}
},
confirm: () => {
if (!recentRoms.value[recentIndex.value]) return false;
if (!continuePlayingRoms.value[continuePlayingIndex.value]) return false;
router.push({
name: ROUTES.CONSOLE_ROM,
params: { rom: recentRoms.value[recentIndex.value].id },
query: { id: recentRoms.value[recentIndex.value].platform_id },
params: {
rom: continuePlayingRoms.value[continuePlayingIndex.value].id,
},
query: {
id: continuePlayingRoms.value[continuePlayingIndex.value].platform_id,
},
});
return true;
},
@@ -367,8 +383,11 @@ function scrollToCurrentRow() {
case "systems":
scrollContainerRef.value?.scrollTo({ top: 0, behavior });
break;
case "recent":
recentSectionRef.value?.scrollIntoView({ behavior, block: "start" });
case "continuePlaying":
continuePlayingSectionRef.value?.scrollIntoView({
behavior,
block: "start",
});
break;
case "collections":
collectionsSectionRef.value?.scrollIntoView({
@@ -506,14 +525,14 @@ function handleAction(action: InputAction): boolean {
navigationMode.value = "controls";
return true;
}
if (currentMode === "recent") {
if (currentMode === "continuePlaying") {
navigationMode.value = "systems";
scrollToCurrentRow();
return true;
}
if (currentMode === "collections") {
navigationMode.value =
recentRoms.value.length > 0 ? "recent" : "systems";
continuePlayingRoms.value.length > 0 ? "continuePlaying" : "systems";
scrollToCurrentRow();
return true;
}
@@ -521,8 +540,8 @@ function handleAction(action: InputAction): boolean {
navigationMode.value =
allCollections.value.length > 0
? "collections"
: recentRoms.value.length > 0
? "recent"
: continuePlayingRoms.value.length > 0
? "continuePlaying"
: "systems";
scrollToCurrentRow();
return true;
@@ -533,8 +552,8 @@ function handleAction(action: InputAction): boolean {
? "smartCollections"
: allCollections.value.length > 0
? "collections"
: recentRoms.value.length > 0
? "recent"
: continuePlayingRoms.value.length > 0
? "continuePlaying"
: "systems";
scrollToCurrentRow();
return true;
@@ -544,8 +563,8 @@ function handleAction(action: InputAction): boolean {
case "moveDown":
if (currentMode === "systems") {
navigationMode.value =
recentRoms.value.length > 0
? "recent"
continuePlayingRoms.value.length > 0
? "continuePlaying"
: allCollections.value.length > 0
? "collections"
: smartCollections.value.length > 0
@@ -556,7 +575,7 @@ function handleAction(action: InputAction): boolean {
scrollToCurrentRow();
return true;
}
if (currentMode === "recent") {
if (currentMode === "continuePlaying") {
navigationMode.value =
allCollections.value.length > 0
? "collections"
@@ -606,8 +625,13 @@ function handleAction(action: InputAction): boolean {
return true;
case "toggleFavorite":
if (currentMode === "recent" && recentRoms.value[recentIndex.value]) {
toggleFavoriteComposable(recentRoms.value[recentIndex.value]);
if (
currentMode === "continuePlaying" &&
continuePlayingRoms.value[continuePlayingIndex.value]
) {
toggleFavoriteComposable(
continuePlayingRoms.value[continuePlayingIndex.value],
);
return true;
}
return false;
@@ -617,10 +641,15 @@ function handleAction(action: InputAction): boolean {
}
}
onBeforeMount(async () => {
await romsStore.fetchContinuePlayingRoms();
});
onMounted(async () => {
// Restore indices within bounds
if (platformIndex.value >= allPlatforms.value.length) platformIndex.value = 0;
if (recentIndex.value >= recentRoms.value.length) recentIndex.value = 0;
if (continuePlayingIndex.value >= continuePlayingRoms.value.length)
continuePlayingIndex.value = 0;
if (collectionsIndex.value >= allCollections.value.length)
collectionsIndex.value = 0;
if (smartCollectionsIndex.value >= smartCollections.value.length)
@@ -633,7 +662,10 @@ onMounted(async () => {
// Center carousels
centerInCarousel(platformsRef.value, systemElementAt(platformIndex.value));
centerInCarousel(recentRef.value, recentElementAt(recentIndex.value));
centerInCarousel(
continuePlayingRef.value,
continuePlayingElementAt(continuePlayingIndex.value),
);
centerInCarousel(
collectionsRef.value,
collectionElementAt(collectionsIndex.value),
@@ -655,7 +687,7 @@ let off: (() => void) | null = null;
onUnmounted(() => {
consoleStore.setHomeState({
platformIndex: platformIndex.value,
recentIndex: recentIndex.value,
continuePlayingIndex: continuePlayingIndex.value,
collectionsIndex: collectionsIndex.value,
smartCollectionsIndex: smartCollectionsIndex.value,
virtualCollectionsIndex: virtualCollectionsIndex.value,
@@ -752,8 +784,8 @@ onUnmounted(() => {
</section>
<section
v-if="recentRoms.length > 0"
ref="recent-section-ref"
v-if="continuePlayingRoms.length > 0"
ref="continue-playing-section-ref"
class="pb-8"
>
<h2
@@ -770,7 +802,7 @@ onUnmounted(() => {
border: `1px solid var(--console-home-carousel-button-border)`,
color: 'var(--console-home-carousel-button-text)',
}"
@click="navigationFunctions.recent.prev"
@click="navigationFunctions.continuePlaying.prev"
>
</button>
@@ -781,26 +813,29 @@ onUnmounted(() => {
border: `1px solid var(--console-home-carousel-button-border)`,
color: 'var(--console-home-carousel-button-text)',
}"
@click="navigationFunctions.recent.next"
@click="navigationFunctions.continuePlaying.next"
>
</button>
<div
ref="recent-ref"
ref="continue-playing-ref"
class="w-full h-full overflow-x-auto overflow-y-hidden no-scrollbar [scrollbar-width:none] [-ms-overflow-style:none]"
@wheel.prevent
>
<div class="flex items-center gap-4 h-full px-12 min-w-max">
<GameCard
v-for="(g, i) in recentRoms"
v-for="(g, i) in continuePlayingRoms"
:key="`${g.platform_id}-${g.id}`"
:rom="g"
:index="i"
:is-recent="true"
:selected="navigationMode === 'recent' && i === recentIndex"
:continue-playing="true"
:selected="
navigationMode === 'continuePlaying' &&
i === continuePlayingIndex
"
:loaded="true"
@click="goGame(g)"
@focus="recentIndex = i"
@focus="continuePlayingIndex = i"
@select="handleItemSelected"
@deselect="handleItemDeselected"
/>
@@ -1042,7 +1077,7 @@ onUnmounted(() => {
<NavigationHint
:show-back="false"
:show-toggle-favorite="navigationMode === 'recent'"
:show-toggle-favorite="navigationMode === 'continuePlaying'"
/>
</div>
<SettingsModal v-model="showSettings" />

View File

@@ -3,7 +3,7 @@ import { ROUTES } from "@/plugins/router";
export type NavigationMode =
| "systems"
| "recent"
| "continuePlaying"
| "collections"
| "smartCollections"
| "virtualCollections"
@@ -12,7 +12,7 @@ export type NavigationMode =
export default defineStore("console", {
state: () => ({
platformIndex: 0,
recentIndex: 0,
continuePlayingIndex: 0,
collectionsIndex: 0,
smartCollectionsIndex: 0,
virtualCollectionsIndex: 0,
@@ -45,7 +45,7 @@ export default defineStore("console", {
actions: {
setHomeState(payload: {
platformIndex?: number;
recentIndex?: number;
continuePlayingIndex?: number;
collectionsIndex?: number;
smartCollectionsIndex?: number;
virtualCollectionsIndex?: number;
@@ -54,8 +54,8 @@ export default defineStore("console", {
}) {
if (payload.platformIndex !== undefined)
this.platformIndex = payload.platformIndex;
if (payload.recentIndex !== undefined)
this.recentIndex = payload.recentIndex;
if (payload.continuePlayingIndex !== undefined)
this.continuePlayingIndex = payload.continuePlayingIndex;
if (payload.collectionsIndex !== undefined)
this.collectionsIndex = payload.collectionsIndex;
if (payload.smartCollectionsIndex !== undefined)