mirror of
https://github.com/rommapp/romm.git
synced 2026-02-18 00:27:41 +01:00
Merge pull request #1962 from rommapp/home-fallback-empty
Add fallback for homepage when library is empty
This commit is contained in:
@@ -53,14 +53,14 @@ function onHover(emitData: { isHovering: boolean; id: number }) {
|
||||
:class="{
|
||||
'flex-nowrap overflow-x-auto': !gridCollections,
|
||||
}"
|
||||
class="pa-1"
|
||||
class="py-1"
|
||||
no-gutters
|
||||
style="overflow-y: hidden"
|
||||
>
|
||||
<v-col
|
||||
v-for="collection in collections.allCollections"
|
||||
:key="collection.name"
|
||||
class="pa-1 my-4"
|
||||
class="pa-1"
|
||||
:cols="views[0]['size-cols']"
|
||||
:sm="views[0]['size-sm']"
|
||||
:md="views[0]['size-md']"
|
||||
|
||||
@@ -69,14 +69,14 @@ function onClosedMenu() {
|
||||
:class="{
|
||||
'flex-nowrap overflow-x-auto': !gridContinuePlayingRoms,
|
||||
}"
|
||||
class="pa-1"
|
||||
class="py-1"
|
||||
no-gutters
|
||||
style="overflow-y: hidden"
|
||||
>
|
||||
<v-col
|
||||
v-for="rom in continuePlayingRoms"
|
||||
:key="rom.id"
|
||||
class="pa-1 align-self-end my-4"
|
||||
class="pa-1 align-self-end"
|
||||
:cols="views[0]['size-cols']"
|
||||
:sm="views[0]['size-sm']"
|
||||
:md="views[0]['size-md']"
|
||||
|
||||
13
frontend/src/components/Home/EmptyHome.vue
Normal file
13
frontend/src/components/Home/EmptyHome.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-empty-state
|
||||
:headline="t('emptyStates.home-headline')"
|
||||
:title="t('emptyStates.home-title')"
|
||||
:text="t('emptyStates.home-text')"
|
||||
/>
|
||||
</template>
|
||||
@@ -54,14 +54,14 @@ function onHover(emitData: { isHovering: boolean; id: number }) {
|
||||
:class="{
|
||||
'flex-nowrap overflow-x-auto': !gridPlatforms,
|
||||
}"
|
||||
class="pa-1"
|
||||
class="py-1"
|
||||
no-gutters
|
||||
style="overflow-y: hidden"
|
||||
>
|
||||
<v-col
|
||||
v-for="platform in filledPlatforms"
|
||||
:key="platform.slug"
|
||||
class="pa-1 my-4"
|
||||
class="pa-1"
|
||||
:cols="views[0]['size-cols']"
|
||||
:sm="views[0]['size-sm']"
|
||||
:md="views[0]['size-md']"
|
||||
|
||||
@@ -67,14 +67,14 @@ function onClosedMenu() {
|
||||
:class="{
|
||||
'flex-nowrap overflow-x-auto': !gridRecentRoms,
|
||||
}"
|
||||
class="pa-1"
|
||||
class="py-1"
|
||||
no-gutters
|
||||
style="overflow-y: hidden"
|
||||
>
|
||||
<v-col
|
||||
v-for="rom in recentRoms"
|
||||
:key="rom.id"
|
||||
class="pa-1 align-self-end my-4"
|
||||
class="pa-1 align-self-end"
|
||||
:cols="views[0]['size-cols']"
|
||||
:sm="views[0]['size-sm']"
|
||||
:md="views[0]['size-md']"
|
||||
|
||||
@@ -74,7 +74,7 @@ onBeforeUnmount(() => {
|
||||
:class="{
|
||||
'flex-nowrap overflow-x-auto': !gridVirtualCollections,
|
||||
}"
|
||||
class="pa-1"
|
||||
class="py-1"
|
||||
no-gutters
|
||||
style="overflow-y: hidden"
|
||||
>
|
||||
@@ -84,7 +84,7 @@ onBeforeUnmount(() => {
|
||||
visibleCollections,
|
||||
)"
|
||||
:key="collection.name"
|
||||
class="pa-1 my-4"
|
||||
class="pa-1"
|
||||
:cols="views[0]['size-cols']"
|
||||
:sm="views[0]['size-sm']"
|
||||
:md="views[0]['size-md']"
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"404-subtitle": "Die Seite, die Sie suchen, existiert nicht",
|
||||
"404-title": "Seite nicht gefunden",
|
||||
"search-for-games": "Suche nach Spielen auf allen Plattformen",
|
||||
"manual-match": "Suche nach Spielen bei allen Metadatenanbietern"
|
||||
"manual-match": "Suche nach Spielen bei allen Metadatenanbietern",
|
||||
"home-headline": "Keine Spiele in Ihrer Bibliothek",
|
||||
"home-title": "Führen Sie einen Schnellscan durch, um Spiele auf Ihrem System automatisch zu erkennen",
|
||||
"home-text": "Bereit zum Spielen?"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"404-subtitle": "The page you are looking for does not exist",
|
||||
"404-title": "Page not found",
|
||||
"search-for-games": "Search for games across all platforms",
|
||||
"manual-match": "Search for games across all metadata providers"
|
||||
"manual-match": "Search for games across all metadata providers",
|
||||
"home-headline": "No games in your library",
|
||||
"home-title": "Run a quick scan to automatically detect games on your system",
|
||||
"home-text": "Ready to play?"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"404-subtitle": "The page you are looking for does not exist",
|
||||
"404-title": "Page not found",
|
||||
"search-for-games": "Search for games across all platforms",
|
||||
"manual-match": "Search for games across all metadata providers"
|
||||
"manual-match": "Search for games across all metadata providers",
|
||||
"home-headline": "No games in your library",
|
||||
"home-title": "Run a quick scan to automatically detect games on your system",
|
||||
"home-text": "Ready to play?"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"404-subtitle": "La página que buscas no existe",
|
||||
"404-title": "Página no encontrada",
|
||||
"search-for-games": "Busca juegos en todas las plataformas",
|
||||
"manual-match": "Busca juegos en todos los proveedores de metadatos"
|
||||
"manual-match": "Busca juegos en todos los proveedores de metadatos",
|
||||
"home-headline": "No hay juegos en tu biblioteca",
|
||||
"home-title": "Ejecuta un escaneo rápido para detectar juegos en tu sistema automáticamente",
|
||||
"home-text": "¿Listo para jugar?"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"404-subtitle": "La page que vous recherchez n'existe pas",
|
||||
"404-title": "Page introuvable",
|
||||
"search-for-games": "Recherchez des jeux sur toutes les plateformes",
|
||||
"manual-match": "Recherchez des jeux auprès de tous les fournisseurs de métadonnées"
|
||||
"manual-match": "Recherchez des jeux auprès de tous les fournisseurs de métadonnées",
|
||||
"home-headline": "Aucun jeu dans votre bibliothèque",
|
||||
"home-title": "Effectuez une analyse rapide pour détecter automatiquement les jeux sur votre système",
|
||||
"home-text": "Prêt à jouer ?"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"404-subtitle": "La pagina che stai cercando non esiste",
|
||||
"404-title": "Pagina non trovata",
|
||||
"search-for-games": "Cerca giochi su tutte le piattaforme",
|
||||
"manual-match": "Cerca giochi su tutti i fornitori di metadati"
|
||||
"manual-match": "Cerca giochi su tutti i fornitori di metadati",
|
||||
"home-headline": "Nessun gioco nella tua libreria",
|
||||
"home-title": "Esegui una scansione rapida per rilevare automaticamente i giochi sul tuo sistema",
|
||||
"home-text": "Pronto a giocare?"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"404-subtitle": "表示しようとしたページは見つかりませんでした",
|
||||
"404-title": "ページが見つかりませんでした",
|
||||
"search-for-games": "全てのプラットフォームから検索",
|
||||
"manual-match": "全てのメタデータプロバイダーから検索"
|
||||
"manual-match": "全てのメタデータプロバイダーから検索",
|
||||
"home-headline": "ライブラリにゲームがありません",
|
||||
"home-title": "システム上のゲームを自動的に検出するためにクイックスキャンを実行します",
|
||||
"home-text": "プレイする準備はできましたか?"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"404-subtitle": "찾으려고 하는 페이지가 존재하지 않습니다",
|
||||
"404-title": "페이지 찾을 수 없음",
|
||||
"search-for-games": "모든 기종에서 게임 찾기",
|
||||
"manual-match": "모든 메타데이터 제공업체에서 게임 찾기"
|
||||
"manual-match": "모든 메타데이터 제공업체에서 게임 찾기",
|
||||
"home-headline": "라이브러리에 게임이 없습니다",
|
||||
"home-title": "시스템에서 게임을 자동으로 감지하기 위해 빠른 스캔을 실행하세요",
|
||||
"home-text": "게임을 할 준비가 되셨나요?"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"404-subtitle": "A página que você está procurando não existe",
|
||||
"404-title": "Página não encontrada",
|
||||
"search-for-games": "Pesquisar jogos em todas as plataformas",
|
||||
"manual-match": "Pesquisar jogos em todos os provedores de metadados"
|
||||
"manual-match": "Pesquisar jogos em todos os provedores de metadados",
|
||||
"home-headline": "Nenhum jogo na sua biblioteca",
|
||||
"home-title": "Execute uma varredura rápida para detectar jogos no seu sistema automaticamente",
|
||||
"home-text": "Pronto para jogar?"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"404-subtitle": "Pagina pe care o căutați nu există",
|
||||
"404-title": "Pagină negăsită",
|
||||
"search-for-games": "Căutați jocuri pe toate platformele",
|
||||
"manual-match": "Căutați jocuri la toți furnizorii de metadate"
|
||||
"manual-match": "Căutați jocuri la toți furnizorii de metadate",
|
||||
"home-headline": "Nu există jocuri în biblioteca dvs.",
|
||||
"home-title": "Rulați o scanare rapidă pentru a detecta automat jocurile de pe sistemul dvs.",
|
||||
"home-text": "Sunteți gata de joacă?"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"404-subtitle": "Страница, которую вы ищете, не существует",
|
||||
"404-title": "Страница не найдена",
|
||||
"search-for-games": "Искать игры на всех платформах",
|
||||
"manual-match": "Искать игры у всех поставщиков метаданных"
|
||||
"manual-match": "Искать игры у всех поставщиков метаданных",
|
||||
"home-headline": "В вашей библиотеке нет игр",
|
||||
"home-title": "Запустите быструю проверку, чтобы автоматически обнаружить игры на вашей системе",
|
||||
"home-text": "Готовы играть?"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"404-subtitle": "找不到页面",
|
||||
"404-title": "页面未找到",
|
||||
"search-for-games": "搜索游戏",
|
||||
"manual-match": "在所有元数据提供商中搜索游戏"
|
||||
"manual-match": "在所有元数据提供商中搜索游戏",
|
||||
"home-headline": "您的库中没有游戏",
|
||||
"home-title": "运行快速扫描以自动检测系统上的游戏",
|
||||
"home-text": "准备好游戏了吗?"
|
||||
}
|
||||
|
||||
@@ -337,6 +337,7 @@ export function languageToEmoji(language: string) {
|
||||
*/
|
||||
const _EJS_CORES_MAP = {
|
||||
"3do": ["opera"],
|
||||
acpc: ["cap32", "crocods"],
|
||||
amiga: ["puae"],
|
||||
"amiga-cd32": ["puae"],
|
||||
arcade: [
|
||||
@@ -359,6 +360,7 @@ const _EJS_CORES_MAP = {
|
||||
c128: ["vice_x128"],
|
||||
"commmodore-128": ["vice_x128"],
|
||||
colecovision: ["gearcoleco"],
|
||||
doom: ["prboom"],
|
||||
jaguar: ["virtualjaguar"],
|
||||
lynx: ["handy"],
|
||||
"atari-lynx-mkii": ["handy"],
|
||||
@@ -413,6 +415,7 @@ const _EJS_CORES_MAP = {
|
||||
wonderswan: ["mednafen_wswan"],
|
||||
swancrystal: ["mednafen_wswan"],
|
||||
"wonderswan-color": ["mednafen_wswan"],
|
||||
"zx-spectrum": ["fuse"],
|
||||
} as const;
|
||||
|
||||
export type EJSPlatformSlug = keyof typeof _EJS_CORES_MAP;
|
||||
|
||||
@@ -1,109 +1,126 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, computed } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import Collections from "@/components/Home/Collections.vue";
|
||||
import VirtualCollections from "@/components/Home/VirtualCollections.vue";
|
||||
import Platforms from "@/components/Home/Platforms.vue";
|
||||
import RecentSkeletonLoader from "@/components/Home/RecentSkeletonLoader.vue";
|
||||
import RecentAdded from "@/components/Home/RecentAdded.vue";
|
||||
import ContinuePlaying from "@/components/Home/ContinuePlaying.vue";
|
||||
import EmptyHome from "@/components/Home/EmptyHome.vue";
|
||||
import romApi from "@/services/api/rom";
|
||||
import storeCollections from "@/stores/collections";
|
||||
import storePlatforms from "@/stores/platforms";
|
||||
import storeRoms from "@/stores/roms";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { isNull } from "lodash";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
// Props
|
||||
const { t } = useI18n();
|
||||
const romsStore = storeRoms();
|
||||
const { recentRoms, continuePlayingRoms: recentPlayedRoms } =
|
||||
storeToRefs(romsStore);
|
||||
const platforms = storePlatforms();
|
||||
const { filledPlatforms } = storeToRefs(platforms);
|
||||
const collections = storeCollections();
|
||||
const { allCollections, virtualCollections } = storeToRefs(collections);
|
||||
const showRecentRoms = isNull(localStorage.getItem("settings.showRecentRoms"))
|
||||
? true
|
||||
: localStorage.getItem("settings.showRecentRoms") === "true";
|
||||
const showContinuePlaying = isNull(
|
||||
localStorage.getItem("settings.showContinuePlaying"),
|
||||
)
|
||||
? true
|
||||
: localStorage.getItem("settings.showContinuePlaying") === "true";
|
||||
const showPlatforms = isNull(localStorage.getItem("settings.showPlatforms"))
|
||||
? true
|
||||
: localStorage.getItem("settings.showPlatforms") === "true";
|
||||
const showCollections = isNull(localStorage.getItem("settings.showCollections"))
|
||||
? true
|
||||
: localStorage.getItem("settings.showCollections") === "true";
|
||||
const showVirtualCollections = isNull(
|
||||
localStorage.getItem("settings.showVirtualCollections"),
|
||||
)
|
||||
? true
|
||||
: localStorage.getItem("settings.showVirtualCollections") === "true";
|
||||
const platformsStore = storePlatforms();
|
||||
const { filledPlatforms } = storeToRefs(platformsStore);
|
||||
const collectionsStore = storeCollections();
|
||||
const { allCollections, virtualCollections } = storeToRefs(collectionsStore);
|
||||
|
||||
function getSettingValue(key: string, defaultValue: boolean = true): boolean {
|
||||
const stored = localStorage.getItem(`settings.${key}`);
|
||||
return stored === null ? defaultValue : stored === "true";
|
||||
}
|
||||
|
||||
const showRecentRoms = getSettingValue("showRecentRoms");
|
||||
const showContinuePlaying = getSettingValue("showContinuePlaying");
|
||||
const showPlatforms = getSettingValue("showPlatforms");
|
||||
const showCollections = getSettingValue("showCollections");
|
||||
const showVirtualCollections = getSettingValue("showVirtualCollections");
|
||||
|
||||
const fetchingRecentAdded = ref(false);
|
||||
const fetchingContinuePlaying = ref(false);
|
||||
|
||||
// Functions
|
||||
onMounted(async () => {
|
||||
fetchingRecentAdded.value = true;
|
||||
fetchingContinuePlaying.value = true;
|
||||
romApi
|
||||
.getRecentRoms()
|
||||
.then(({ data: { items } }) => {
|
||||
romsStore.setRecentRoms(items);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
fetchingRecentAdded.value = false;
|
||||
});
|
||||
const isEmpty = computed(
|
||||
() =>
|
||||
recentRoms.value.length === 0 &&
|
||||
recentPlayedRoms.value.length === 0 &&
|
||||
filledPlatforms.value.length === 0 &&
|
||||
allCollections.value.length === 0 &&
|
||||
virtualCollections.value.length === 0,
|
||||
);
|
||||
|
||||
romApi
|
||||
.getRecentPlayedRoms()
|
||||
.then(({ data: { items } }) => {
|
||||
romsStore.setContinuePlayedRoms(
|
||||
items.filter((rom) => rom.rom_user.last_played),
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
fetchingContinuePlaying.value = false;
|
||||
});
|
||||
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;
|
||||
const {
|
||||
data: { items },
|
||||
} = await romApi.getRecentRoms();
|
||||
romsStore.setRecentRoms(items);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch recent ROMs:", error);
|
||||
} finally {
|
||||
fetchingRecentAdded.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const fetchContinuePlayingRoms = async (): Promise<void> => {
|
||||
try {
|
||||
fetchingContinuePlaying.value = true;
|
||||
const {
|
||||
data: { items },
|
||||
} = await romApi.getRecentPlayedRoms();
|
||||
const filteredItems = items.filter((rom) => rom.rom_user.last_played);
|
||||
romsStore.setContinuePlayedRoms(filteredItems);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch continue playing ROMs:", error);
|
||||
} finally {
|
||||
fetchingContinuePlaying.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([fetchRecentRoms(), fetchContinuePlayingRoms()]);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<recent-skeleton-loader
|
||||
v-if="showRecentRoms && fetchingRecentAdded && recentRoms.length === 0"
|
||||
<RecentSkeletonLoader
|
||||
v-if="showRecentSkeleton"
|
||||
:title="t('home.recently-added')"
|
||||
class="ma-2"
|
||||
/>
|
||||
<recent-added class="ma-2" v-if="recentRoms.length > 0 && showRecentRoms" />
|
||||
<recent-skeleton-loader
|
||||
v-if="
|
||||
showContinuePlaying &&
|
||||
fetchingContinuePlaying &&
|
||||
recentPlayedRoms.length === 0
|
||||
"
|
||||
<RecentAdded
|
||||
v-else-if="recentRoms.length > 0 && showRecentRoms"
|
||||
class="ma-2"
|
||||
/>
|
||||
<RecentSkeletonLoader
|
||||
v-if="showContinuePlayingSkeleton"
|
||||
:title="t('home.continue-playing')"
|
||||
class="ma-2"
|
||||
/>
|
||||
<continue-playing
|
||||
<ContinuePlaying
|
||||
v-else-if="recentPlayedRoms.length > 0 && showContinuePlaying"
|
||||
class="ma-2"
|
||||
v-if="recentPlayedRoms.length > 0 && showContinuePlaying"
|
||||
/>
|
||||
<platforms class="ma-2" v-if="filledPlatforms.length > 0 && showPlatforms" />
|
||||
<collections
|
||||
class="ma-2"
|
||||
<Platforms v-if="filledPlatforms.length > 0 && showPlatforms" class="ma-2" />
|
||||
<Collections
|
||||
v-if="allCollections.length > 0 && showCollections"
|
||||
/>
|
||||
<virtual-collections
|
||||
class="ma-2"
|
||||
v-if="virtualCollections.length > 0 && showVirtualCollections"
|
||||
/>
|
||||
<VirtualCollections
|
||||
v-if="virtualCollections.length > 0 && showVirtualCollections"
|
||||
class="ma-2"
|
||||
/>
|
||||
<EmptyHome v-if="isEmpty" class="ma-2" />
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user