first cleanup batch

This commit is contained in:
Georges-Antoine Assi
2025-08-25 12:49:42 -04:00
parent 9ce11d528b
commit 940a4e766c
7 changed files with 109 additions and 117 deletions

View File

@@ -29,7 +29,6 @@
"ms-python.python",
"ms-python.pylint",
"ms-python.black-formatter",
"bradlc.vscode-tailwindcss",
"vue.volar",
"ms-vscode.vscode-typescript-next"
],

View File

@@ -1,4 +1,7 @@
<script setup lang="ts">
import { ROUTES } from "@/plugins/router";
import { useRoute, useRouter } from "vue-router";
withDefaults(
defineProps<{
block?: boolean;
@@ -13,7 +16,22 @@ withDefaults(
withTag: false,
},
);
const router = useRouter();
const route = useRoute();
function enterConsoleMode() {
// Navigate first so route guards run promptly
router.push({ name: ROUTES.CONSOLE_HOME });
if (!document.fullscreenElement) {
// Attempt fullscreen after a small delay to allow navigation transition
setTimeout(() => {
document.documentElement.requestFullscreen?.().catch(() => {});
}, 50);
}
}
</script>
<template>
<v-btn
icon
@@ -26,14 +44,14 @@ withDefaults(
@click="enterConsoleMode"
>
<div class="d-flex flex-column align-center">
<v-icon :color="$route.path.startsWith('/console') ? 'primary' : ''">
<v-icon :color="route.path.startsWith('/console') ? 'primary' : ''">
mdi-television-play
</v-icon>
<v-expand-transition>
<span
v-if="withTag"
class="text-caption text-center"
:class="{ 'text-primary': $route.path.startsWith('/console') }"
:class="{ 'text-primary': route.path.startsWith('/console') }"
>
Play
</span>
@@ -41,29 +59,3 @@ withDefaults(
</div>
</v-btn>
</template>
<script lang="ts">
export default {
methods: {
async enterConsoleMode() {
try {
// navigate first so route guards run promptly
this.$router.push({ name: "console-home" });
const docEl = document.documentElement as HTMLElement & {
requestFullscreen?: () => Promise<void>;
};
if (!document.fullscreenElement && docEl.requestFullscreen) {
// Attempt fullscreen after a small delay to allow navigation transition
setTimeout(() => {
docEl.requestFullscreen?.().catch(() => {
/* ignore */
});
}, 50);
}
} catch {
/* swallow */
}
},
},
};
</script>

View File

@@ -1,3 +1,18 @@
<script setup lang="ts">
import { useRouter } from "vue-router";
const props = defineProps<{
text?: string;
onBack?: () => void;
}>();
const router = useRouter();
function goBack() {
props.onBack?.() ?? router.back();
}
</script>
<template>
<div
class="absolute top-4 left-4 z-30 inline-flex items-center pointer-events-none"
@@ -12,27 +27,8 @@
<span
v-if="text"
class="ml-3 text-white/90 text-3xl font-bold drop-shadow select-none"
>{{ text }}</span
>
{{ text }}
</span>
</div>
</template>
<script setup lang="ts">
import { toRefs } from "vue";
import { useRouter } from "vue-router";
const props = defineProps<{
text?: string;
onBack?: () => void;
}>();
const { text, onBack } = toRefs(props);
const router = useRouter();
function goBack() {
if (onBack?.value) {
onBack.value();
} else {
router.back();
}
}
</script>

View File

@@ -1,3 +1,64 @@
<script setup lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { computed, onMounted, ref, watchEffect } from "vue";
import type { CollectionSchema } from "@/__generated__/models/CollectionSchema";
import { getFavoriteCoverImage } from "@/utils/covers";
const props = defineProps<{
collection: CollectionSchema;
index: number;
selected?: boolean;
loaded?: boolean;
}>();
const emit = defineEmits(["click", "mouseenter", "focus", "loaded"]);
const el = ref<HTMLElement>();
const isFavorite = computed(() => props.collection.is_favorite);
const coverSrc = computed(
() =>
props.collection.path_cover_large ||
props.collection.path_cover_small ||
props.collection.url_cover ||
"",
);
// Composite favourite logic (two diagonally split images)
const firstCover = ref("");
const secondCover = ref("");
const compositeReady = ref(false);
watchEffect(() => {
if (!isFavorite.value) {
compositeReady.value = false;
return;
}
const large = props.collection.path_covers_large || [];
const small = props.collection.path_covers_small || [];
// Choose source list preferring large
const source = large.length ? large : small;
if (source.length >= 2) {
const shuffled = [...source].sort(() => Math.random() - 0.5);
firstCover.value = shuffled[0];
secondCover.value = shuffled[1];
} else if (source.length === 1) {
firstCover.value = source[0];
secondCover.value = getFavoriteCoverImage(props.collection.name);
} else {
const gen = getFavoriteCoverImage(props.collection.name);
firstCover.value = gen;
secondCover.value = gen;
}
compositeReady.value = true;
});
onMounted(() => {
if (!el.value) return;
(window as any).collectionCardElements =
(window as any).collectionCardElements || [];
(window as any).collectionCardElements[props.index] = el.value;
});
</script>
<template>
<div class="flex flex-col items-center w-[250px] shrink-0">
<button
@@ -84,67 +145,6 @@
</div>
</template>
<script setup lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { computed, onMounted, ref, watchEffect } from "vue";
import type { CollectionSchema } from "@/__generated__/models/CollectionSchema";
import { getFavoriteCoverImage } from "@/utils/covers";
const props = defineProps<{
collection: CollectionSchema;
index: number;
selected?: boolean;
loaded?: boolean;
}>();
const emit = defineEmits(["click", "mouseenter", "focus", "loaded"]);
const el = ref<HTMLElement>();
const isFavorite = computed(() => props.collection.is_favorite);
const coverSrc = computed(
() =>
props.collection.path_cover_large ||
props.collection.path_cover_small ||
props.collection.url_cover ||
"",
);
// Composite favourite logic (two diagonally split images)
const firstCover = ref("");
const secondCover = ref("");
const compositeReady = ref(false);
watchEffect(() => {
if (!isFavorite.value) {
compositeReady.value = false;
return;
}
const large = props.collection.path_covers_large || [];
const small = props.collection.path_covers_small || [];
// Choose source list preferring large
const source = large.length ? large : small;
if (source.length >= 2) {
const shuffled = [...source].sort(() => Math.random() - 0.5);
firstCover.value = shuffled[0];
secondCover.value = shuffled[1];
} else if (source.length === 1) {
firstCover.value = source[0];
secondCover.value = getFavoriteCoverImage(props.collection.name);
} else {
const gen = getFavoriteCoverImage(props.collection.name);
firstCover.value = gen;
secondCover.value = gen;
}
compositeReady.value = true;
});
onMounted(() => {
if (!el.value) return;
(window as any).collectionCardElements =
(window as any).collectionCardElements || [];
(window as any).collectionCardElements[props.index] = el.value;
});
</script>
<style>
@keyframes shimmer {
0% {

View File

@@ -34,6 +34,11 @@ export const ROUTES = {
ADMINISTRATION: "administration",
SERVER_STATS: "server-stats",
NOT_FOUND: "404",
CONSOLE_HOME: "console-home",
CONSOLE_PLATFORM: "console-platform",
CONSOLE_COLLECTION: "console-collection",
CONSOLE_ROM: "console-rom",
CONSOLE_PLAY: "console-play",
} as const;
const routes = [
@@ -231,27 +236,27 @@ const routes = [
children: [
{
path: "",
name: "console-home",
name: ROUTES.CONSOLE_HOME,
component: () => import("@/console/views/Home.vue"),
},
{
path: "platform/:id",
name: "console-platform",
name: ROUTES.CONSOLE_PLATFORM,
component: () => import("@/console/views/GamesList.vue"),
},
{
path: "collection/:id",
name: "console-collection",
name: ROUTES.CONSOLE_COLLECTION,
component: () => import("@/console/views/GamesList.vue"),
},
{
path: "rom/:rom",
name: "console-rom",
name: ROUTES.CONSOLE_ROM,
component: () => import("@/console/views/Game.vue"),
},
{
path: "rom/:rom/play",
name: "console-play",
name: ROUTES.CONSOLE_PLAY,
component: () => import("@/console/views/Play.vue"),
},
],

View File

@@ -11,7 +11,7 @@ import { storeToRefs } from "pinia";
import { computed, ref, watch, type DefineComponent } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
import { useAutoScroll } from "@/composables/use-auto-scroll";
import { useAutoScroll } from "@/composables/useAutoScroll";
const LOCAL_STORAGE_METADATA_SOURCES_KEY = "scan.metadataSources";