mirror of
https://github.com/rommapp/romm.git
synced 2026-02-18 23:42:07 +01:00
Merge pull request #2297 from rommapp/gallery-loading-states
Use skeleton loaders as loading states for games
This commit is contained in:
@@ -1,10 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { views } from "@/utils";
|
||||
import { storeToRefs } from "pinia";
|
||||
import Skeleton from "@/components/common/Game/Card/Skeleton.vue";
|
||||
import storeGalleryView from "@/stores/galleryView";
|
||||
import { RECENT_ROMS_LIMIT } from "@/services/api/rom";
|
||||
import storeRoms from "@/stores/roms";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
platformId?: number;
|
||||
romCount?: number;
|
||||
}>(),
|
||||
{
|
||||
platformId: undefined,
|
||||
romCount: RECENT_ROMS_LIMIT,
|
||||
},
|
||||
);
|
||||
|
||||
const galleryViewStore = storeGalleryView();
|
||||
const romsStore = storeRoms();
|
||||
const { currentView } = storeToRefs(galleryViewStore);
|
||||
const { fetchLimit } = storeToRefs(romsStore);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -12,7 +28,7 @@ const { currentView } = storeToRefs(galleryViewStore);
|
||||
<v-col>
|
||||
<v-row v-if="currentView != 2" no-gutters class="mx-1 mt-3 mr-14">
|
||||
<v-col
|
||||
v-for="_ in 60"
|
||||
v-for="_ in Math.min(props.romCount, fetchLimit)"
|
||||
class="pa-1 align-self-end"
|
||||
:cols="views[currentView]['size-cols']"
|
||||
:sm="views[currentView]['size-sm']"
|
||||
@@ -20,7 +36,7 @@ const { currentView } = storeToRefs(galleryViewStore);
|
||||
:lg="views[currentView]['size-lg']"
|
||||
:xl="views[currentView]['size-xl']"
|
||||
>
|
||||
<v-skeleton-loader type="card" />
|
||||
<skeleton :platformId="props.platformId" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import RSection from "@/components/common/RSection.vue";
|
||||
import Skeleton from "@/components/common/Game/Card/Skeleton.vue";
|
||||
import { views } from "@/utils";
|
||||
import { RECENT_ROMS_LIMIT } from "@/services/api/rom";
|
||||
|
||||
defineProps<{ title: string }>();
|
||||
</script>
|
||||
@@ -9,7 +11,7 @@ defineProps<{ title: string }>();
|
||||
<template #content>
|
||||
<v-row class="flex-nowrap overflow-x-auto pa-1" no-gutters>
|
||||
<v-col
|
||||
v-for="_ in 15"
|
||||
v-for="_ in RECENT_ROMS_LIMIT"
|
||||
class="align-self-end pa-1"
|
||||
:cols="views[0]['size-cols']"
|
||||
:sm="views[0]['size-sm']"
|
||||
@@ -17,7 +19,7 @@ defineProps<{ title: string }>();
|
||||
:lg="views[0]['size-lg']"
|
||||
:xl="views[0]['size-xl']"
|
||||
>
|
||||
<v-skeleton-loader type="card" />
|
||||
<skeleton type="image" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
@@ -6,6 +6,7 @@ import Sources from "@/components/common/Game/Card/Sources.vue";
|
||||
import storePlatforms from "@/stores/platforms";
|
||||
import PlatformIcon from "@/components/common/Platform/Icon.vue";
|
||||
import MissingFromFSIcon from "@/components/common/MissingFromFSIcon.vue";
|
||||
import Skeleton from "@/components/common/Game/Card/Skeleton.vue";
|
||||
import storeCollections from "@/stores/collections";
|
||||
import storeGalleryView from "@/stores/galleryView";
|
||||
import { ROUTES } from "@/plugins/router";
|
||||
@@ -356,7 +357,15 @@ onBeforeUnmount(() => {
|
||||
eager
|
||||
:src="smallCover || fallbackCoverImage"
|
||||
:aspect-ratio="computedAspectRatio"
|
||||
></v-img>
|
||||
>
|
||||
<template #placeholder>
|
||||
<skeleton
|
||||
:platformId="rom.platform_id"
|
||||
:aspectRatio="computedAspectRatio"
|
||||
type="image"
|
||||
/>
|
||||
</template>
|
||||
</v-img>
|
||||
</template>
|
||||
</v-img>
|
||||
</v-hover>
|
||||
|
||||
86
frontend/src/components/common/Game/Card/Skeleton.vue
Normal file
86
frontend/src/components/common/Game/Card/Skeleton.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<script setup lang="ts">
|
||||
import storePlatforms from "@/stores/platforms";
|
||||
import storeGalleryView from "@/stores/galleryView";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
platformId?: number;
|
||||
aspectRatio?: string | number;
|
||||
type?: string;
|
||||
}>(),
|
||||
{
|
||||
platformId: undefined,
|
||||
aspectRatio: undefined,
|
||||
type: "image, avatar, chip, chip",
|
||||
},
|
||||
);
|
||||
|
||||
const platformsStore = storePlatforms();
|
||||
const galleryViewStore = storeGalleryView();
|
||||
|
||||
const computedAspectRatio = computed(() => {
|
||||
const ratio =
|
||||
props.aspectRatio ||
|
||||
platformsStore.getAspectRatio(props.platformId || 0) ||
|
||||
galleryViewStore.defaultAspectRatioCover;
|
||||
return parseFloat(ratio.toString());
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-skeleton-loader
|
||||
class="card-skeleton"
|
||||
:type="type"
|
||||
:style="{ aspectRatio: computedAspectRatio }"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.card-skeleton .v-skeleton-loader__card-avatar {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.card-skeleton .v-skeleton-loader__image {
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.card-skeleton .v-skeleton-loader__avatar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
margin: 4px;
|
||||
min-height: 32px;
|
||||
min-width: 32px;
|
||||
max-height: 32px;
|
||||
max-width: 32px;
|
||||
|
||||
&:after {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
.card-skeleton .v-skeleton-loader__chip {
|
||||
position: absolute;
|
||||
font-size: 0.875rem;
|
||||
padding: 0 12px;
|
||||
height: 24px;
|
||||
margin: 4px;
|
||||
|
||||
&:after {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
.card-skeleton .v-skeleton-loader__chip:nth-of-type(3) {
|
||||
bottom: 0;
|
||||
right: 4px;
|
||||
}
|
||||
|
||||
.card-skeleton .v-skeleton-loader__chip:nth-of-type(4) {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -150,12 +150,15 @@ async function getRoms({
|
||||
});
|
||||
}
|
||||
|
||||
export const RECENT_ROMS_LIMIT = 15;
|
||||
export const RECENT_PLAYED_ROMS_LIMIT = 15;
|
||||
|
||||
async function getRecentRoms(): Promise<{ data: GetRomsResponse }> {
|
||||
return api.get("/roms", {
|
||||
params: {
|
||||
order_by: "id",
|
||||
order_dir: "desc",
|
||||
limit: 15,
|
||||
limit: RECENT_ROMS_LIMIT,
|
||||
with_char_index: false,
|
||||
},
|
||||
});
|
||||
@@ -166,7 +169,7 @@ async function getRecentPlayedRoms(): Promise<{ data: GetRomsResponse }> {
|
||||
params: {
|
||||
order_by: "last_played",
|
||||
order_dir: "desc",
|
||||
limit: 15,
|
||||
limit: RECENT_PLAYED_ROMS_LIMIT,
|
||||
with_char_index: false,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -234,8 +234,10 @@ onBeforeUnmount(() => {
|
||||
<template>
|
||||
<template v-if="!noCollectionError">
|
||||
<gallery-app-bar-collection />
|
||||
<template v-if="fetchingRoms && filteredRoms.length === 0">
|
||||
<skeleton />
|
||||
<template
|
||||
v-if="currentCollection && fetchingRoms && filteredRoms.length === 0"
|
||||
>
|
||||
<skeleton :romCount="currentCollection.rom_count" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="filteredRoms.length > 0">
|
||||
|
||||
@@ -17,7 +17,7 @@ import type { Emitter } from "mitt";
|
||||
import { isNull, throttle } from "lodash";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { inject, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||
import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
|
||||
import { onBeforeRouteUpdate, useRoute } from "vue-router";
|
||||
|
||||
const route = useRoute();
|
||||
const galleryViewStore = storeGalleryView();
|
||||
@@ -36,7 +36,6 @@ const {
|
||||
fetchTotalRoms,
|
||||
} = storeToRefs(romsStore);
|
||||
const noPlatformError = ref(false);
|
||||
const router = useRouter();
|
||||
const emitter = inject<Emitter<Events>>("emitter");
|
||||
const isHovering = ref(false);
|
||||
const hoveringRomId = ref();
|
||||
@@ -238,8 +237,13 @@ onBeforeUnmount(() => {
|
||||
<template>
|
||||
<template v-if="!noPlatformError">
|
||||
<gallery-app-bar />
|
||||
<template v-if="fetchingRoms && filteredRoms.length === 0">
|
||||
<skeleton />
|
||||
<template
|
||||
v-if="currentPlatform && fetchingRoms && filteredRoms.length === 0"
|
||||
>
|
||||
<skeleton
|
||||
:platformId="currentPlatform.id"
|
||||
:romCount="currentPlatform.rom_count"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="filteredRoms.length > 0">
|
||||
@@ -263,7 +267,6 @@ onBeforeUnmount(() => {
|
||||
}"
|
||||
>
|
||||
<game-card
|
||||
v-if="currentPlatform"
|
||||
:key="rom.updated_at"
|
||||
:rom="rom"
|
||||
titleOnHover
|
||||
|
||||
Reference in New Issue
Block a user