mirror of
https://github.com/rommapp/romm.git
synced 2026-02-18 23:42:07 +01:00
Merge pull request #2306 from rommapp/replace-v-progress
Replace v-progress loaders with skeletons
This commit is contained in:
@@ -18,8 +18,14 @@ storeLanguage.setLanguage(selectedLanguage.value);
|
||||
|
||||
<template>
|
||||
<v-app>
|
||||
<v-main>
|
||||
<v-main id="main" class="no-transition">
|
||||
<router-view />
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
#main.no-transition {
|
||||
transition: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,40 +20,40 @@ const unmatchedCoverImage = computed(() =>
|
||||
|
||||
<template>
|
||||
<v-card
|
||||
id="background-header"
|
||||
elevation="0"
|
||||
rounded="0"
|
||||
class="w-100"
|
||||
:key="currentRom.updated_at"
|
||||
v-if="currentRom"
|
||||
>
|
||||
<v-img
|
||||
id="background-image"
|
||||
:src="currentRom?.path_cover_large || unmatchedCoverImage"
|
||||
class="background-image"
|
||||
:src="currentRom?.path_cover_small || unmatchedCoverImage"
|
||||
cover
|
||||
>
|
||||
<template #error>
|
||||
<v-img :src="missingCoverImage" />
|
||||
</template>
|
||||
<template #placeholder>
|
||||
<div class="d-flex align-center justify-center fill-height">
|
||||
<v-progress-circular
|
||||
:width="2"
|
||||
:size="40"
|
||||
color="primary"
|
||||
indeterminate
|
||||
/>
|
||||
</div>
|
||||
<v-skeleton-loader class="background-skeleton" type="image" />
|
||||
</template>
|
||||
</v-img>
|
||||
</v-card>
|
||||
</template>
|
||||
<style scoped>
|
||||
#background-header {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#background-image {
|
||||
<style scoped>
|
||||
.background-image {
|
||||
height: 18rem;
|
||||
filter: blur(30px);
|
||||
}
|
||||
|
||||
.background-skeleton {
|
||||
height: 18rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.background-skeleton .v-skeleton-loader__image {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -117,27 +117,30 @@ onMounted(() => {
|
||||
buffer-opacity="0.6"
|
||||
:buffer-value="achievementsPercentage"
|
||||
height="32"
|
||||
><p class="text-shadow">
|
||||
>
|
||||
<p class="text-shadow">
|
||||
{{
|
||||
Math.max(
|
||||
Math.ceil(achievementsPercentage),
|
||||
Math.ceil(achievementsPercentageHardcore),
|
||||
)
|
||||
}}%
|
||||
</p></v-progress-linear
|
||||
>
|
||||
</p>
|
||||
</v-progress-linear>
|
||||
</v-list-item>
|
||||
<v-chip
|
||||
label
|
||||
:color="showEarned ? 'primary' : 'gray'"
|
||||
@click="toggleShowEarned"
|
||||
class="mt-4"
|
||||
><template #prepend
|
||||
><v-icon class="mr-2">{{
|
||||
showEarned ? "mdi-checkbox-outline" : "mdi-checkbox-blank-outline"
|
||||
}}</v-icon></template
|
||||
>{{ t("rom.show-earned-only") }}</v-chip
|
||||
>
|
||||
<template #prepend>
|
||||
<v-icon class="mr-2">
|
||||
{{ showEarned ? "mdi-checkbox-outline" : "mdi-checkbox-blank-outline" }}
|
||||
</v-icon>
|
||||
</template>
|
||||
{{ t("rom.show-earned-only") }}
|
||||
</v-chip>
|
||||
<v-list v-if="rom.merged_ra_metadata?.achievements" class="bg-background">
|
||||
<v-list-item
|
||||
v-for="achievement in filteredAchievements"
|
||||
|
||||
53
frontend/src/components/Home/PlatformsSkeleton.vue
Normal file
53
frontend/src/components/Home/PlatformsSkeleton.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<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 { ref } from "vue";
|
||||
import { isNull } from "lodash";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
const storedPlatforms = localStorage.getItem("settings.gridPlatforms");
|
||||
const gridPlatforms = ref(
|
||||
isNull(storedPlatforms) ? false : storedPlatforms === "true",
|
||||
);
|
||||
const PLATFORM_SKELETON_COUNT = 12;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<r-section icon="mdi-shimmer" :title="t('common.platforms')">
|
||||
<template #toolbar-append>
|
||||
<v-skeleton-loader type="button" />
|
||||
</template>
|
||||
<template #content>
|
||||
<v-row
|
||||
:class="{ 'flex-nowrap overflow-x-auto': !gridPlatforms }"
|
||||
class="py-1 overflow-y-hidden"
|
||||
no-gutters
|
||||
>
|
||||
<v-col
|
||||
v-for="_ in PLATFORM_SKELETON_COUNT"
|
||||
class="align-self-end pa-1"
|
||||
:cols="views[0]['size-cols']"
|
||||
:sm="views[0]['size-sm']"
|
||||
:md="views[0]['size-md']"
|
||||
:lg="views[0]['size-lg']"
|
||||
:xl="views[0]['size-xl']"
|
||||
>
|
||||
<v-skeleton-loader
|
||||
class="platform-skeleton"
|
||||
type="heading, image"
|
||||
aspect-ratio="1.2"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
</r-section>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.platform-skeleton .v-skeleton-loader__heading {
|
||||
margin: 8px;
|
||||
height: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import InterfaceOption from "@/components/Settings/UserInterface/InterfaceOption.vue";
|
||||
import RSection from "@/components/common/RSection.vue";
|
||||
import collectionApi from "@/services/api/collection";
|
||||
import storeCollections from "@/stores/collections";
|
||||
import { computed, ref } from "vue";
|
||||
import { useDisplay } from "vuetify";
|
||||
@@ -12,7 +11,7 @@ const { t } = useI18n();
|
||||
const { smAndDown } = useDisplay();
|
||||
const collectionsStore = storeCollections();
|
||||
|
||||
// Initializing refs from localStorage
|
||||
// Initialize refs from localStorage
|
||||
|
||||
// Home
|
||||
const storedShowStats = localStorage.getItem("settings.showStats");
|
||||
@@ -233,12 +232,7 @@ const toggleShowVirtualCollections = (value: boolean) => {
|
||||
const setVirtualCollectionType = async (value: string) => {
|
||||
virtualCollectionTypeRef.value = value;
|
||||
localStorage.setItem("settings.virtualCollectionType", value);
|
||||
|
||||
await collectionApi
|
||||
.getVirtualCollections({ type: value })
|
||||
.then(({ data: virtualCollections }) => {
|
||||
collectionsStore.setVirtualCollections(virtualCollections);
|
||||
});
|
||||
collectionsStore.fetchVirtualCollections(value);
|
||||
};
|
||||
|
||||
const toggleShowStats = (value: boolean) => {
|
||||
|
||||
@@ -29,7 +29,7 @@ async function deleteCollection() {
|
||||
show.value = false;
|
||||
await collectionApi
|
||||
.deleteCollection({ collection: collection.value })
|
||||
.then((response) => {
|
||||
.then(() => {
|
||||
emitter?.emit("snackbarShow", {
|
||||
msg: "Collection deleted",
|
||||
icon: "mdi-check-bold",
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
import type { SearchRomSchema } from "@/__generated__";
|
||||
import GameCard from "@/components/common/Game/Card/Base.vue";
|
||||
import RDialog from "@/components/common/RDialog.vue";
|
||||
import romApi from "@/services/api/rom";
|
||||
import Skeleton from "@/components/common/Game/Card/Skeleton.vue";
|
||||
import EmptyManualMatch from "@/components/common/EmptyStates/EmptyManualMatch.vue";
|
||||
import storeGalleryView from "@/stores/galleryView";
|
||||
import storeHeartbeat from "@/stores/heartbeat";
|
||||
import storeRoms, { type SimpleRom } from "@/stores/roms";
|
||||
import storePlatforms from "@/stores/platforms";
|
||||
import romApi from "@/services/api/rom";
|
||||
import type { Events } from "@/types/emitter";
|
||||
import type { Emitter } from "mitt";
|
||||
import { computed, inject, onBeforeUnmount, ref } from "vue";
|
||||
@@ -490,15 +491,10 @@ onBeforeUnmount(() => {
|
||||
cover
|
||||
>
|
||||
<template #placeholder>
|
||||
<div
|
||||
class="d-flex align-center justify-center fill-height"
|
||||
>
|
||||
<v-progress-circular
|
||||
color="primary"
|
||||
:width="2"
|
||||
indeterminate
|
||||
/>
|
||||
</div>
|
||||
<skeleton
|
||||
:aspectRatio="computedAspectRatio"
|
||||
type="image"
|
||||
/>
|
||||
</template>
|
||||
<v-row no-gutters class="text-white pa-1">
|
||||
<v-avatar class="mr-1" size="30" rounded="1">
|
||||
@@ -580,19 +576,18 @@ onBeforeUnmount(() => {
|
||||
<template #footer>
|
||||
<v-row no-gutters class="text-center">
|
||||
<v-col>
|
||||
<v-chip label class="pr-0" size="small"
|
||||
>{{ t("rom.results-found") }}:<v-chip
|
||||
color="primary"
|
||||
class="ml-2 px-2"
|
||||
label
|
||||
>{{ !searching ? matchedRoms.length : ""
|
||||
}}<v-progress-circular
|
||||
<v-chip label class="pr-0" size="small">
|
||||
{{ t("rom.results-found") }}:
|
||||
<v-chip color="primary" class="ml-2 px-2" label>
|
||||
{{ !searching ? matchedRoms.length : "" }}
|
||||
<v-progress-circular
|
||||
v-if="searching"
|
||||
:width="1"
|
||||
:size="10"
|
||||
color="primary"
|
||||
indeterminate
|
||||
/></v-chip>
|
||||
/>
|
||||
</v-chip>
|
||||
</v-chip>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -132,9 +132,9 @@ onBeforeUnmount(() => {
|
||||
:size="20"
|
||||
indeterminate
|
||||
/>
|
||||
<v-icon v-else :color="$route.name == 'scan' ? 'primary' : ''"
|
||||
>mdi-magnify-scan</v-icon
|
||||
>
|
||||
<v-icon v-else :color="$route.name == 'scan' ? 'primary' : ''">
|
||||
mdi-magnify-scan
|
||||
</v-icon>
|
||||
<v-expand-transition>
|
||||
<span
|
||||
v-if="withTag"
|
||||
|
||||
@@ -2,26 +2,24 @@
|
||||
import type { SearchCoverSchema } from "@/__generated__";
|
||||
import RDialog from "@/components/common/RDialog.vue";
|
||||
import sgdbApi from "@/services/api/sgdb";
|
||||
import Skeleton from "@/components/common/Game/Card/Skeleton.vue";
|
||||
import storeGalleryView from "@/stores/galleryView";
|
||||
import storePlatforms from "@/stores/platforms";
|
||||
import type { Events } from "@/types/emitter";
|
||||
import type { Emitter } from "mitt";
|
||||
import { inject, onBeforeUnmount, ref } from "vue";
|
||||
import { useDisplay } from "vuetify";
|
||||
|
||||
const { lgAndUp } = useDisplay();
|
||||
const galleryViewStore = storeGalleryView();
|
||||
const show = ref(false);
|
||||
const searching = ref(false);
|
||||
const searchText = ref("");
|
||||
const coverType = ref("all");
|
||||
const covers = ref<SearchCoverSchema[]>([]);
|
||||
const filteredCovers = ref<SearchCoverSchema[]>();
|
||||
const galleryViewStore = storeGalleryView();
|
||||
const panels = ref([0]);
|
||||
|
||||
const emitter = inject<Emitter<Events>>("emitter");
|
||||
const coverAspectRatio = ref(
|
||||
parseFloat(galleryViewStore.defaultAspectRatioCover.toString()),
|
||||
);
|
||||
emitter?.on("showSearchCoverDialog", ({ term, aspectRatio = null }) => {
|
||||
searchText.value = term;
|
||||
show.value = true;
|
||||
@@ -30,6 +28,10 @@ emitter?.on("showSearchCoverDialog", ({ term, aspectRatio = null }) => {
|
||||
if (searchText.value) searchCovers();
|
||||
});
|
||||
|
||||
const coverAspectRatio = ref(
|
||||
parseFloat(galleryViewStore.defaultAspectRatioCover.toString()),
|
||||
);
|
||||
|
||||
async function searchCovers() {
|
||||
covers.value = [];
|
||||
|
||||
@@ -199,16 +201,7 @@ onBeforeUnmount(() => {
|
||||
></v-img>
|
||||
</template>
|
||||
<template #placeholder>
|
||||
<div
|
||||
class="d-flex align-center justify-center fill-height"
|
||||
>
|
||||
<v-progress-circular
|
||||
:width="2"
|
||||
:size="40"
|
||||
color="primary"
|
||||
indeterminate
|
||||
/>
|
||||
</div>
|
||||
<skeleton :aspectRatio="coverAspectRatio" type="image" />
|
||||
</template>
|
||||
</v-img>
|
||||
</v-hover>
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { Events } from "@/types/emitter";
|
||||
import type { Emitter } from "mitt";
|
||||
import { inject, ref } from "vue";
|
||||
|
||||
const show = ref(false);
|
||||
const scrim = ref(false);
|
||||
const emitter = inject<Emitter<Events>>("emitter");
|
||||
emitter?.on("showLoadingDialog", (args) => {
|
||||
show.value = args.loading;
|
||||
scrim.value = args.scrim;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-dialog :model-value="show" scroll-strategy="none" width="auto" persistent>
|
||||
<v-progress-circular :width="3" :size="70" color="primary" indeterminate />
|
||||
</v-dialog>
|
||||
</template>
|
||||
@@ -18,8 +18,6 @@ import SelectStateDialog from "@/components/common/Game/Dialog/Asset/SelectState
|
||||
import DeleteSavesDialog from "@/components/common/Game/Dialog/Asset/DeleteSaves.vue";
|
||||
import DeleteStatesDialog from "@/components/common/Game/Dialog/Asset/DeleteStates.vue";
|
||||
import NoteDialog from "@/components/common/Game/Dialog/NoteDialog.vue";
|
||||
import collectionApi from "@/services/api/collection";
|
||||
import platformApi from "@/services/api/platform";
|
||||
import storeCollections from "@/stores/collections";
|
||||
import storeNavigation from "@/stores/navigation";
|
||||
import storePlatforms from "@/stores/platforms";
|
||||
@@ -33,8 +31,7 @@ const platformsStore = storePlatforms();
|
||||
const collectionsStore = storeCollections();
|
||||
const emitter = inject<Emitter<Events>>("emitter");
|
||||
emitter?.on("refreshDrawer", async () => {
|
||||
const { data: platformData } = await platformApi.getPlatforms();
|
||||
platformsStore.set(platformData);
|
||||
platformsStore.fetchPlatforms();
|
||||
});
|
||||
|
||||
const showVirtualCollections = isNull(
|
||||
@@ -53,47 +50,20 @@ const virtualCollectionTypeRef = ref(
|
||||
);
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await platformApi
|
||||
.getPlatforms()
|
||||
.then(({ data: platforms }) => {
|
||||
platformsStore.set(platforms);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
await collectionApi
|
||||
.getCollections()
|
||||
.then(({ data: collections }) => {
|
||||
collectionsStore.setCollections(collections);
|
||||
collectionsStore.setFavoriteCollection(
|
||||
collections.find(
|
||||
(collection) => collection.name.toLowerCase() === "favourites",
|
||||
),
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
await collectionApi
|
||||
.getSmartCollections()
|
||||
.then(({ data: smartCollections }) => {
|
||||
collectionsStore.setSmartCollection(smartCollections);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
if (showVirtualCollections) {
|
||||
await collectionApi
|
||||
.getVirtualCollections({ type: virtualCollectionTypeRef.value })
|
||||
.then(({ data: virtualCollections }) => {
|
||||
collectionsStore.setVirtualCollections(virtualCollections);
|
||||
});
|
||||
}
|
||||
await Promise.all([
|
||||
platformsStore.fetchPlatforms(),
|
||||
collectionsStore.fetchCollections(),
|
||||
collectionsStore.fetchSmartCollections(),
|
||||
showVirtualCollections
|
||||
? collectionsStore.fetchVirtualCollections(virtualCollectionTypeRef.value)
|
||||
: Promise.resolve(),
|
||||
]);
|
||||
|
||||
navigationStore.reset();
|
||||
|
||||
// Hack to prevent main page transition on first load
|
||||
const main = document.getElementById("main");
|
||||
if (main) main.classList.remove("no-transition");
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
import { uniqBy } from "lodash";
|
||||
import { defineStore } from "pinia";
|
||||
import type { SimpleRom } from "./roms";
|
||||
import collectionApi from "@/services/api/collection";
|
||||
|
||||
export type Collection = CollectionSchema;
|
||||
export type VirtualCollection = VirtualCollectionSchema;
|
||||
@@ -19,6 +20,9 @@ export default defineStore("collections", {
|
||||
smartCollections: [] as SmartCollection[],
|
||||
favoriteCollection: {} as Collection | undefined,
|
||||
filterText: "" as string,
|
||||
fetchingCollections: false as boolean,
|
||||
fetchingSmartCollections: false as boolean,
|
||||
fetchingVirtualCollections: false as boolean,
|
||||
}),
|
||||
getters: {
|
||||
filteredCollections: ({ allCollections, filterText }) =>
|
||||
@@ -54,6 +58,66 @@ export default defineStore("collections", {
|
||||
},
|
||||
);
|
||||
},
|
||||
fetchCollections(): Promise<Collection[]> {
|
||||
if (this.fetchingCollections) return Promise.resolve([]);
|
||||
this.fetchingCollections = true;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
collectionApi
|
||||
.getCollections()
|
||||
.then(({ data: collections }) => {
|
||||
this.allCollections = collections;
|
||||
resolve(collections);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
reject(error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.fetchingCollections = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
fetchSmartCollections(): Promise<SmartCollection[]> {
|
||||
if (this.fetchingSmartCollections) return Promise.resolve([]);
|
||||
this.fetchingSmartCollections = true;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
collectionApi
|
||||
.getSmartCollections()
|
||||
.then(({ data: smartCollections }) => {
|
||||
this.smartCollections = smartCollections;
|
||||
resolve(smartCollections);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
reject(error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.fetchingSmartCollections = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
fetchVirtualCollections(type: string): Promise<VirtualCollection[]> {
|
||||
if (this.fetchingVirtualCollections) return Promise.resolve([]);
|
||||
this.fetchingVirtualCollections = true;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
collectionApi
|
||||
.getVirtualCollections({ type })
|
||||
.then(({ data: virtualCollections }) => {
|
||||
this.virtualCollections = virtualCollections;
|
||||
resolve(virtualCollections);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
reject(error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.fetchingVirtualCollections = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
setFavoriteCollection(favoriteCollection: Collection | undefined) {
|
||||
this.favoriteCollection = favoriteCollection;
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { PlatformSchema } from "@/__generated__";
|
||||
import platformApi from "@/services/api/platform";
|
||||
import { uniqBy } from "lodash";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
@@ -8,6 +9,7 @@ export default defineStore("platforms", {
|
||||
state: () => ({
|
||||
allPlatforms: [] as Platform[],
|
||||
filterText: "" as string,
|
||||
fetchingPlatforms: false as boolean,
|
||||
}),
|
||||
|
||||
getters: {
|
||||
@@ -26,12 +28,33 @@ export default defineStore("platforms", {
|
||||
)
|
||||
.sort((a, b) => a.display_name.localeCompare(b.display_name)),
|
||||
},
|
||||
|
||||
actions: {
|
||||
_reorder() {
|
||||
this.allPlatforms = uniqBy(this.allPlatforms, "id").sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
},
|
||||
fetchPlatforms(): Promise<Platform[]> {
|
||||
if (this.fetchingPlatforms) return Promise.resolve([]);
|
||||
this.fetchingPlatforms = true;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
platformApi
|
||||
.getPlatforms()
|
||||
.then(({ data: platforms }) => {
|
||||
this.allPlatforms = platforms;
|
||||
resolve(platforms);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
reject(error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.fetchingPlatforms = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
set(platforms: Platform[]) {
|
||||
this.allPlatforms = platforms;
|
||||
},
|
||||
|
||||
@@ -129,8 +129,8 @@ export default defineStore("roms", {
|
||||
}: {
|
||||
galleryFilter: GalleryFilterStore;
|
||||
concat?: boolean;
|
||||
}) {
|
||||
if (this.fetchingRoms) return Promise.resolve();
|
||||
}): Promise<SimpleRom[]> {
|
||||
if (this.fetchingRoms) return Promise.resolve([]);
|
||||
this.fetchingRoms = true;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@@ -5,7 +5,8 @@ import { useI18n } from "vue-i18n";
|
||||
import Stats from "@/components/Home/Stats.vue";
|
||||
import Collections from "@/components/Home/Collections.vue";
|
||||
import Platforms from "@/components/Home/Platforms.vue";
|
||||
import RecentSkeletonLoader from "@/components/Home/RecentSkeletonLoader.vue";
|
||||
import PlatformsSkeleton from "@/components/Home/PlatformsSkeleton.vue";
|
||||
import RecentAddedSkeleton from "@/components/Home/RecentAddedSkeleton.vue";
|
||||
import RecentAdded from "@/components/Home/RecentAdded.vue";
|
||||
import ContinuePlaying from "@/components/Home/ContinuePlaying.vue";
|
||||
import EmptyHome from "@/components/Home/EmptyHome.vue";
|
||||
@@ -16,15 +17,17 @@ import storeRoms from "@/stores/roms";
|
||||
|
||||
const { t } = useI18n();
|
||||
const romsStore = storeRoms();
|
||||
const { recentRoms, continuePlayingRoms: recentPlayedRoms } =
|
||||
storeToRefs(romsStore);
|
||||
const { recentRoms, continuePlayingRoms } = storeToRefs(romsStore);
|
||||
const platformsStore = storePlatforms();
|
||||
const { filledPlatforms } = storeToRefs(platformsStore);
|
||||
const { filledPlatforms, fetchingPlatforms } = storeToRefs(platformsStore);
|
||||
const collectionsStore = storeCollections();
|
||||
const {
|
||||
filteredCollections,
|
||||
filteredVirtualCollections,
|
||||
filteredSmartCollections,
|
||||
fetchingCollections,
|
||||
fetchingSmartCollections,
|
||||
fetchingVirtualCollections,
|
||||
} = storeToRefs(collectionsStore);
|
||||
|
||||
function getSettingValue(key: string, defaultValue: boolean = true): boolean {
|
||||
@@ -45,28 +48,20 @@ const fetchingContinuePlaying = ref(false);
|
||||
|
||||
const isEmpty = computed(
|
||||
() =>
|
||||
!fetchingPlatforms.value &&
|
||||
!fetchingCollections.value &&
|
||||
!fetchingSmartCollections.value &&
|
||||
!fetchingVirtualCollections.value &&
|
||||
!fetchingRecentAdded.value &&
|
||||
!fetchingContinuePlaying.value &&
|
||||
recentRoms.value.length === 0 &&
|
||||
recentPlayedRoms.value.length === 0 &&
|
||||
continuePlayingRoms.value.length === 0 &&
|
||||
filledPlatforms.value.length === 0 &&
|
||||
filteredCollections.value.length === 0 &&
|
||||
filteredVirtualCollections.value.length === 0 &&
|
||||
filteredSmartCollections.value.length === 0,
|
||||
);
|
||||
|
||||
const showRecentSkeleton = computed(
|
||||
() =>
|
||||
showRecentRoms &&
|
||||
fetchingRecentAdded.value &&
|
||||
recentRoms.value.length === 0,
|
||||
);
|
||||
|
||||
const showContinuePlayingSkeleton = computed(
|
||||
() =>
|
||||
showContinuePlaying &&
|
||||
fetchingContinuePlaying.value &&
|
||||
recentPlayedRoms.value.length === 0,
|
||||
);
|
||||
|
||||
const fetchRecentRoms = async (): Promise<void> => {
|
||||
try {
|
||||
fetchingRecentAdded.value = true;
|
||||
@@ -102,63 +97,83 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="fetchingRecentAdded || fetchingContinuePlaying">
|
||||
<div class="d-flex align-center justify-center fill-height">
|
||||
<v-progress-circular
|
||||
color="primary"
|
||||
:width="4"
|
||||
size="120"
|
||||
indeterminate
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="!fetchingRecentAdded && !fetchingContinuePlaying">
|
||||
<template v-if="!isEmpty">
|
||||
<stats v-if="showStats" />
|
||||
<recent-skeleton-loader
|
||||
v-if="showRecentSkeleton"
|
||||
<empty-home v-if="isEmpty" />
|
||||
<template v-else>
|
||||
<stats v-if="showStats" />
|
||||
|
||||
<template v-if="showRecentRoms">
|
||||
<recent-added-skeleton
|
||||
v-if="fetchingRecentAdded && recentRoms.length === 0"
|
||||
:title="t('home.recently-added')"
|
||||
class="ma-2"
|
||||
/>
|
||||
<recent-added
|
||||
v-else-if="recentRoms.length > 0 && showRecentRoms"
|
||||
class="ma-2"
|
||||
/>
|
||||
<recent-skeleton-loader
|
||||
v-if="showContinuePlayingSkeleton"
|
||||
<recent-added v-else-if="recentRoms.length > 0" class="ma-2" />
|
||||
</template>
|
||||
|
||||
<template v-if="showContinuePlaying">
|
||||
<recent-added-skeleton
|
||||
v-if="fetchingContinuePlaying && continuePlayingRoms.length === 0"
|
||||
:title="t('home.continue-playing')"
|
||||
class="ma-2"
|
||||
/>
|
||||
<continue-playing
|
||||
v-else-if="recentPlayedRoms.length > 0 && showContinuePlaying"
|
||||
v-else-if="continuePlayingRoms.length > 0"
|
||||
class="ma-2"
|
||||
/>
|
||||
<platforms
|
||||
v-if="filledPlatforms.length > 0 && showPlatforms"
|
||||
</template>
|
||||
|
||||
<template v-if="showPlatforms">
|
||||
<platforms-skeleton
|
||||
v-if="fetchingPlatforms && filledPlatforms.length === 0"
|
||||
/>
|
||||
<platforms v-else-if="filledPlatforms.length > 0" class="ma-2" />
|
||||
</template>
|
||||
|
||||
<template v-if="showCollections">
|
||||
<recent-added-skeleton
|
||||
v-if="fetchingCollections && filteredCollections.length === 0"
|
||||
:title="t('common.collections')"
|
||||
class="ma-2"
|
||||
/>
|
||||
<collections
|
||||
v-if="filteredCollections.length > 0 && showCollections"
|
||||
v-if="filteredCollections.length > 0"
|
||||
:collections="filteredCollections"
|
||||
:title="t('common.collections')"
|
||||
setting="gridCollections"
|
||||
class="ma-2"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-if="showSmartCollections">
|
||||
<recent-added-skeleton
|
||||
v-if="fetchingSmartCollections && filteredSmartCollections.length === 0"
|
||||
:title="t('common.smart-collections')"
|
||||
class="ma-2"
|
||||
/>
|
||||
<collections
|
||||
v-if="filteredSmartCollections.length > 0 && showSmartCollections"
|
||||
v-if="filteredSmartCollections.length > 0"
|
||||
:collections="filteredSmartCollections"
|
||||
:title="t('common.smart-collections')"
|
||||
setting="gridSmartCollections"
|
||||
class="ma-2"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-if="showVirtualCollections">
|
||||
<recent-added-skeleton
|
||||
v-if="
|
||||
fetchingVirtualCollections && filteredVirtualCollections.length === 0
|
||||
"
|
||||
:title="t('common.virtual-collections')"
|
||||
class="ma-2"
|
||||
/>
|
||||
<collections
|
||||
v-if="filteredVirtualCollections.length > 0 && showVirtualCollections"
|
||||
v-if="filteredVirtualCollections.length > 0"
|
||||
:collections="filteredVirtualCollections"
|
||||
:title="t('common.virtual-collections')"
|
||||
setting="gridVirtualCollections"
|
||||
class="ma-2"
|
||||
/>
|
||||
</template>
|
||||
<empty-home v-else />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user