mdi-pencil
@@ -185,15 +185,3 @@ function closeDialog() {
-
-
diff --git a/frontend/src/components/Administration/Users/Table.vue b/frontend/src/components/Settings/Administration/Users/Table.vue
similarity index 95%
rename from frontend/src/components/Administration/Users/Table.vue
rename to frontend/src/components/Settings/Administration/Users/Table.vue
index 25697193c..6105d8054 100644
--- a/frontend/src/components/Administration/Users/Table.vue
+++ b/frontend/src/components/Settings/Administration/Users/Table.vue
@@ -1,4 +1,6 @@
-
+
RomM
{{ heartbeatStore.value.VERSION }}
diff --git a/frontend/src/components/Management/AddBtn.vue b/frontend/src/components/Settings/LibraryManagement/AddBtn.vue
similarity index 100%
rename from frontend/src/components/Management/AddBtn.vue
rename to frontend/src/components/Settings/LibraryManagement/AddBtn.vue
diff --git a/frontend/src/components/Management/Dialog/CreateExclusion.vue b/frontend/src/components/Settings/LibraryManagement/Dialog/CreateExclusion.vue
similarity index 100%
rename from frontend/src/components/Management/Dialog/CreateExclusion.vue
rename to frontend/src/components/Settings/LibraryManagement/Dialog/CreateExclusion.vue
diff --git a/frontend/src/components/Management/Dialog/CreatePlatformBinding.vue b/frontend/src/components/Settings/LibraryManagement/Dialog/CreatePlatformBinding.vue
similarity index 100%
rename from frontend/src/components/Management/Dialog/CreatePlatformBinding.vue
rename to frontend/src/components/Settings/LibraryManagement/Dialog/CreatePlatformBinding.vue
diff --git a/frontend/src/components/Management/Dialog/CreatePlatformVersion.vue b/frontend/src/components/Settings/LibraryManagement/Dialog/CreatePlatformVersion.vue
similarity index 100%
rename from frontend/src/components/Management/Dialog/CreatePlatformVersion.vue
rename to frontend/src/components/Settings/LibraryManagement/Dialog/CreatePlatformVersion.vue
diff --git a/frontend/src/components/Management/Dialog/DeletePlatformBinding.vue b/frontend/src/components/Settings/LibraryManagement/Dialog/DeletePlatformBinding.vue
similarity index 100%
rename from frontend/src/components/Management/Dialog/DeletePlatformBinding.vue
rename to frontend/src/components/Settings/LibraryManagement/Dialog/DeletePlatformBinding.vue
diff --git a/frontend/src/components/Management/Dialog/DeletePlatformVersion.vue b/frontend/src/components/Settings/LibraryManagement/Dialog/DeletePlatformVersion.vue
similarity index 100%
rename from frontend/src/components/Management/Dialog/DeletePlatformVersion.vue
rename to frontend/src/components/Settings/LibraryManagement/Dialog/DeletePlatformVersion.vue
diff --git a/frontend/src/layouts/Management/Excluded.vue b/frontend/src/components/Settings/LibraryManagement/Excluded.vue
similarity index 90%
rename from frontend/src/layouts/Management/Excluded.vue
rename to frontend/src/components/Settings/LibraryManagement/Excluded.vue
index 4e6593184..6bae7f9f9 100644
--- a/frontend/src/layouts/Management/Excluded.vue
+++ b/frontend/src/components/Settings/LibraryManagement/Excluded.vue
@@ -1,6 +1,7 @@
+
+
+
+ Home
+
+
+
+
+
+
+ Gallery
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/Settings/InterfaceOption.vue b/frontend/src/components/Settings/UserInterface/InterfaceOption.vue
similarity index 98%
rename from frontend/src/components/Settings/InterfaceOption.vue
rename to frontend/src/components/Settings/UserInterface/InterfaceOption.vue
index e35bc96ae..bfdfa6b92 100644
--- a/frontend/src/components/Settings/InterfaceOption.vue
+++ b/frontend/src/components/Settings/UserInterface/InterfaceOption.vue
@@ -29,6 +29,7 @@ const emit = defineEmits(["update:modelValue"]);
emit('update:modelValue', value)"
:class="{ 'pr-16': !xs }"
diff --git a/frontend/src/layouts/Settings/Theme.vue b/frontend/src/components/Settings/UserInterface/Theme.vue
similarity index 95%
rename from frontend/src/layouts/Settings/Theme.vue
rename to frontend/src/components/Settings/UserInterface/Theme.vue
index ef5ffdba7..71b0410e7 100644
--- a/frontend/src/layouts/Settings/Theme.vue
+++ b/frontend/src/components/Settings/UserInterface/Theme.vue
@@ -1,11 +1,12 @@
-
diff --git a/frontend/src/components/Settings/ThemeOption.vue b/frontend/src/components/Settings/UserInterface/ThemeOption.vue
similarity index 100%
rename from frontend/src/components/Settings/ThemeOption.vue
rename to frontend/src/components/Settings/UserInterface/ThemeOption.vue
diff --git a/frontend/src/components/common/Collection/Dialog/AddRoms.vue b/frontend/src/components/common/Collection/Dialog/AddRoms.vue
index aafd72931..f7295f5c7 100644
--- a/frontend/src/components/common/Collection/Dialog/AddRoms.vue
+++ b/frontend/src/components/common/Collection/Dialog/AddRoms.vue
@@ -1,6 +1,7 @@
-
+
- {{ collection.name }}
-
+
{{ collection.description }}
-
+
{{ collection.rom_count }}
diff --git a/frontend/src/components/common/Game/Card/Base.vue b/frontend/src/components/common/Game/Card/Base.vue
index ed287828e..9e149d315 100644
--- a/frontend/src/components/common/Game/Card/Base.vue
+++ b/frontend/src/components/common/Game/Card/Base.vue
@@ -18,6 +18,8 @@ const props = withDefaults(
defineProps<{
rom: SimpleRom | SearchRomSchema;
aspectRatio?: string | number;
+ width?: string | number;
+ height?: string | number;
transformScale?: boolean;
titleOnHover?: boolean;
showFlags?: boolean;
@@ -28,11 +30,13 @@ const props = withDefaults(
showFav?: boolean;
withBorder?: boolean;
withBorderRommAccent?: boolean;
- withLink: boolean;
+ withLink?: boolean;
src?: string;
}>(),
{
aspectRatio: undefined,
+ width: undefined,
+ height: undefined,
transformScale: false,
titleOnHover: false,
showFlags: false,
@@ -63,7 +67,6 @@ const handleTouchEnd = (event: TouchEvent) => {
emit("touchend", { event: event, rom: props.rom });
};
const downloadStore = storeDownload();
-const card = ref();
const theme = useTheme();
const galleryViewStore = storeGalleryView();
const collectionsStore = storeCollections();
@@ -79,6 +82,10 @@ const computedAspectRatio = computed(() => {
@@ -155,10 +156,6 @@ const computedAspectRatio = computed(() => {
!rom.moby_url_cover)
"
class="translucent-dark text-caption text-white"
- :class="{
- 'text-truncate':
- galleryViewStore.currentView == 0 && !isHovering,
- }"
>
{{ rom.name }}
@@ -209,7 +206,7 @@ const computedAspectRatio = computed(() => {
class="position-absolute append-inner-left"
v-if="!showPlatformIcon"
>
-
+
diff --git a/frontend/src/components/common/Game/Card/Related.vue b/frontend/src/components/common/Game/Card/Related.vue
index 3be15c761..cb94ecb05 100644
--- a/frontend/src/components/common/Game/Card/Related.vue
+++ b/frontend/src/components/common/Game/Card/Related.vue
@@ -9,49 +9,41 @@ const props = defineProps<{
}>();
const theme = useTheme();
const galleryViewStore = storeGalleryView();
-const handleClick = () => {
- if (props.game.slug) {
- window.open(
- `https://www.igdb.com/games/${props.game.slug}`,
- "_blank",
- "noopener noreferrer",
- );
- }
-};
-
- {{ game.name }}
-
-
+
+ {{ game.name }}
-
- {{ game.type }}
-
-
-
+
+
+ {{ game.type }}
+
+
+
+
diff --git a/frontend/src/components/common/Game/Dialog/SearchRom.vue b/frontend/src/components/common/Game/Dialog/SearchRom.vue
index ece56ddf3..58c17110c 100644
--- a/frontend/src/components/common/Game/Dialog/SearchRom.vue
+++ b/frontend/src/components/common/Game/Dialog/SearchRom.vue
@@ -200,11 +200,11 @@ onBeforeUnmount(() => {
title-on-hover
pointerOnHover
withLink
- show-flags
- show-fav
- transform-scale
- show-action-bar
- show-platform-icon
+ showFlags
+ showFav
+ transformScale
+ showActionBar
+ showPlatformIcon
/>
diff --git a/frontend/src/components/common/Game/ListItem.vue b/frontend/src/components/common/Game/ListItem.vue
new file mode 100644
index 000000000..6f1db5fff
--- /dev/null
+++ b/frontend/src/components/common/Game/ListItem.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+ {{ rom.name }}
+ {{ rom.file_name }}
+
+
+
+
+ {{
+ formatBytes(rom.file_size_bytes)
+ }}
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/common/Game/Table.vue b/frontend/src/components/common/Game/Table.vue
index 150ee7f7c..f1e02ba9e 100644
--- a/frontend/src/components/common/Game/Table.vue
+++ b/frontend/src/components/common/Game/Table.vue
@@ -1,7 +1,7 @@
-
-
-
-
-
+
+
+
diff --git a/frontend/src/components/common/Navigation/MainAppBar.vue b/frontend/src/components/common/Navigation/MainAppBar.vue
index 6b4327f1d..3066e8415 100644
--- a/frontend/src/components/common/Navigation/MainAppBar.vue
+++ b/frontend/src/components/common/Navigation/MainAppBar.vue
@@ -6,9 +6,25 @@ import ScanBtn from "@/components/common/Navigation/ScanBtn.vue";
import SearchBtn from "@/components/common/Navigation/SearchBtn.vue";
import UploadBtn from "@/components/common/Navigation/UploadBtn.vue";
import UserBtn from "@/components/common/Navigation/UserBtn.vue";
+import SearchRomDialog from "@/components/common/Game/Dialog/SearchRom.vue";
+import PlatformsDrawer from "@/components/common/Navigation/PlatformsDrawer.vue";
+import CollectionsDrawer from "@/components/common/Navigation/CollectionsDrawer.vue";
+import UploadRomDialog from "@/components/common/Game/Dialog/UploadRom.vue";
+import SettingsDrawer from "@/components/common/Navigation/SettingsDrawer.vue";
+import storeNavigation from "@/stores/navigation";
+import { storeToRefs } from "pinia";
+import { useDisplay } from "vuetify";
+
+// Props
+const { smAndDown } = useDisplay();
+const navigationStore = storeNavigation();
+const { activePlatformsDrawer, activeCollectionsDrawer, activeSettingsDrawer } =
+ storeToRefs(navigationStore);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/common/Navigation/MainDrawer.vue b/frontend/src/components/common/Navigation/MainDrawer.vue
deleted file mode 100644
index 57137581d..000000000
--- a/frontend/src/components/common/Navigation/MainDrawer.vue
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/src/components/common/Navigation/SettingsDrawer.vue b/frontend/src/components/common/Navigation/SettingsDrawer.vue
index cb99c62d2..7cd2b0a4b 100644
--- a/frontend/src/components/common/Navigation/SettingsDrawer.vue
+++ b/frontend/src/components/common/Navigation/SettingsDrawer.vue
@@ -72,13 +72,13 @@ async function logout() {
append-icon="mdi-account"
>Profile
-
UI SettingsUser Interface
Library Management
{
id="upload-in-progress"
v-model="show"
transition="scroll-y-transition"
- :timeout="100000000000"
+ :timeout="-1"
absolute
:location="xs ? 'bottom' : 'bottom right'"
class="mb-4 mr-4"
diff --git a/frontend/src/components/common/Platform/Dialog/DeletePlatform.vue b/frontend/src/components/common/Platform/Dialog/DeletePlatform.vue
index c34d661f1..4d3837de8 100644
--- a/frontend/src/components/common/Platform/Dialog/DeletePlatform.vue
+++ b/frontend/src/components/common/Platform/Dialog/DeletePlatform.vue
@@ -43,7 +43,7 @@ async function deletePlatform() {
return;
});
- await router.push({ name: "dashboard" });
+ await router.push({ name: "home" });
platformsStore.remove(platform.value);
emitter?.emit("refreshDrawer", null);
diff --git a/frontend/src/components/common/Platform/ListItem.vue b/frontend/src/components/common/Platform/ListItem.vue
index bdced0c83..392dbc11c 100644
--- a/frontend/src/components/common/Platform/ListItem.vue
+++ b/frontend/src/components/common/Platform/ListItem.vue
@@ -13,12 +13,14 @@ withDefaults(defineProps<{ platform: Platform; rail?: boolean }>(), {
:key="platform.slug"
:to="{ name: 'platform', params: { platform: platform.id } }"
:value="platform.slug"
+ class="py-0"
>
{
-
+
();
const galleryViewStore = storeGalleryView();
const panels = ref([0]);
const emitter = inject>("emitter");
-emitter?.on("showSearchCoverDialog", (term) => {
+const coverAspectRatio = ref(
+ parseFloat(galleryViewStore.defaultAspectRatioCover.toString()),
+);
+emitter?.on("showSearchCoverDialog", ({ term, aspectRatio = null }) => {
searchTerm.value = term;
show.value = true;
+ // TODO: set default aspect ratio to 2/3
+ if (aspectRatio) coverAspectRatio.value = aspectRatio;
if (searchTerm.value) searchCovers();
});
@@ -168,7 +174,7 @@ onBeforeUnmount(() => {
{{ game.name }}
-
+
{
:class="{ 'on-hover': isHovering }"
class="transform-scale pointer"
@click="selectCover(resource.url)"
- :aspect-ratio="galleryViewStore.defaultAspectRatioCover"
+ :aspect-ratio="coverAspectRatio"
:src="resource.thumb"
cover
>
@@ -218,3 +224,8 @@ onBeforeUnmount(() => {
+
diff --git a/frontend/src/components/common/LoadingView.vue b/frontend/src/components/common/ViewLoader.vue
similarity index 93%
rename from frontend/src/components/common/LoadingView.vue
rename to frontend/src/components/common/ViewLoader.vue
index a4d316a79..00aa794c3 100644
--- a/frontend/src/components/common/LoadingView.vue
+++ b/frontend/src/components/common/ViewLoader.vue
@@ -1,11 +1,11 @@
+
+
+
+
+
+
+
+
+ {{
+ heartbeatStore.value.VERSION
+ }}
+
+
+
+
diff --git a/frontend/src/layouts/Main.vue b/frontend/src/layouts/Main.vue
new file mode 100644
index 000000000..6802677c1
--- /dev/null
+++ b/frontend/src/layouts/Main.vue
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/layouts/Settings.vue b/frontend/src/layouts/Settings.vue
new file mode 100644
index 000000000..3e42fa4e5
--- /dev/null
+++ b/frontend/src/layouts/Settings.vue
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/frontend/src/layouts/Settings/Interface.vue b/frontend/src/layouts/Settings/Interface.vue
deleted file mode 100644
index d2c0daf44..000000000
--- a/frontend/src/layouts/Settings/Interface.vue
+++ /dev/null
@@ -1,122 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/src/main.ts b/frontend/src/main.ts
index 6ee4c59cb..e587c5181 100644
--- a/frontend/src/main.ts
+++ b/frontend/src/main.ts
@@ -1,17 +1,14 @@
import { registerPlugins } from "@/plugins";
+import "@/styles/common.css";
import "@/styles/fonts.css";
import "@/styles/scrollbar.css";
-import "@/styles/common.css";
import type { Events } from "@/types/emitter";
import mitt from "mitt";
import { createApp } from "vue";
-import App from "./App.vue";
+import App from "@/RomM.vue";
const emitter = mitt();
-
const app = createApp(App);
-
registerPlugins(app);
-
app.provide("emitter", emitter);
app.mount("#app");
diff --git a/frontend/src/plugins/router.ts b/frontend/src/plugins/router.ts
index 52a8eaed4..f1c85bd89 100644
--- a/frontend/src/plugins/router.ts
+++ b/frontend/src/plugins/router.ts
@@ -4,74 +4,106 @@ import storeHeartbeat from "@/stores/heartbeat";
const routes = [
{
- path: "/login",
- name: "login",
- component: () => import("@/views/Login.vue"),
+ path: "/setup",
+ name: "setupView",
+ component: () => import("@/layouts/Auth.vue"),
+ children: [
+ {
+ path: "",
+ name: "setup",
+ component: () => import("@/views/Auth/Setup.vue"),
+ },
+ ],
},
{
- path: "/setup",
- name: "setup",
- component: () => import("@/views/Setup.vue"),
+ path: "/login",
+ name: "loginView",
+ component: () => import("@/layouts/Auth.vue"),
+ children: [
+ {
+ path: "",
+ name: "login",
+ component: () => import("@/views/Auth/Login.vue"),
+ },
+ ],
},
{
path: "/",
- name: "home",
- component: () => import("@/views/Home.vue"),
+ name: "main",
+ component: () => import("@/layouts/Main.vue"),
children: [
{
- path: "/",
- name: "dashboard",
- component: () => import("@/views/Dashboard.vue"),
+ path: "",
+ name: "home",
+ component: () => import("@/views/Home.vue"),
},
{
- path: "/platform/:platform",
+ path: "platform/:platform",
name: "platform",
component: () => import("@/views/Gallery/Platform.vue"),
},
{
- path: "/collection/:collection",
+ path: "collection/:collection",
name: "collection",
component: () => import("@/views/Gallery/Collection.vue"),
},
{
- path: "/rom/:rom",
+ path: "rom/:rom",
name: "rom",
component: () => import("@/views/GameDetails.vue"),
},
{
- path: "/rom/:rom/ejs",
+ path: "rom/:rom/ejs",
name: "emulatorjs",
- component: () => import("@/views/EmulatorJS/Base.vue"),
+ component: () => import("@/views/Player/EmulatorJS/Base.vue"),
},
{
- path: "/rom/:rom/ruffle",
+ path: "rom/:rom/ruffle",
name: "ruffle",
- component: () => import("@/views/RuffleRS/Base.vue"),
+ component: () => import("@/views/Player/RuffleRS/Base.vue"),
},
{
- path: "/scan",
+ path: "scan",
name: "scan",
component: () => import("@/views/Scan.vue"),
},
{
- path: "/management",
- name: "management",
- component: () => import("@/views/Management.vue"),
+ path: "/user-interface",
+ component: () => import("@/layouts/Settings.vue"),
+ children: [
+ {
+ path: "",
+ name: "userInterface",
+ component: () => import("@/views/Settings/UserInterface.vue"),
+ },
+ ],
},
{
- path: "/settings",
- name: "settings",
- component: () => import("@/views/Settings.vue"),
+ path: "/library-management",
+ component: () => import("@/layouts/Settings.vue"),
+ children: [
+ {
+ path: "",
+ name: "libraryManagement",
+ component: () => import("@/views/Settings/LibraryManagement.vue"),
+ },
+ ],
},
{
path: "/administration",
- name: "administration",
- component: () => import("@/views/Administration.vue"),
+ component: () => import("@/layouts/Settings.vue"),
+ children: [
+ {
+ path: "",
+ name: "administration",
+ component: () => import("@/views/Settings/Administration.vue"),
+ },
+ ],
},
{
- path: "/:pathMatch(.*)*",
+ path: ":pathMatch(.*)*",
name: "noMatch",
- component: () => import("@/views/Dashboard.vue"),
+ component: () => import("@/views/Home.vue"),
},
],
},
@@ -82,17 +114,17 @@ const router = createRouter({
routes,
});
-router.beforeEach((to, _from, next) => {
+router.beforeEach(async (to, _from, next) => {
const heartbeat = storeHeartbeat();
if (to.name == "setup" && !heartbeat.value.SHOW_SETUP_WIZARD) {
- next({ name: "dashboard" });
+ next({ name: "home" });
} else {
next();
}
});
router.afterEach(() => {
- // Scroll to top to avoid annoying behaviour in mobile
+ // Scroll to top to avoid annoying behaviour on mobile
window.scrollTo({ top: 0, left: 0 });
});
diff --git a/frontend/src/stores/navigation.ts b/frontend/src/stores/navigation.ts
index f948cf72a..aa4c4d86a 100644
--- a/frontend/src/stores/navigation.ts
+++ b/frontend/src/stores/navigation.ts
@@ -6,6 +6,7 @@ export default defineStore("navigation", {
activeCollectionsDrawer: false,
activeSettingsDrawer: false,
activePlatformInfoDrawer: false,
+ activeCollectionInfoDrawer: false,
}),
actions: {
@@ -25,9 +26,13 @@ export default defineStore("navigation", {
this.resetDrawersExcept("activePlatformInfoDrawer");
this.activePlatformInfoDrawer = !this.activePlatformInfoDrawer;
},
+ swtichActiveCollectionInfoDrawer() {
+ this.resetDrawersExcept("activeCollectionInfoDrawer");
+ this.activeCollectionInfoDrawer = !this.activeCollectionInfoDrawer;
+ },
goHome() {
this.resetDrawers();
- this.$router.push({ name: "dashboard" });
+ this.$router.push({ name: "home" });
},
goScan() {
this.resetDrawers();
@@ -38,6 +43,7 @@ export default defineStore("navigation", {
this.activeCollectionsDrawer = false;
this.activeSettingsDrawer = false;
this.activePlatformInfoDrawer = false;
+ this.activeCollectionInfoDrawer = false;
},
resetDrawersExcept(drawer: string) {
this.activePlatformsDrawer =
@@ -52,6 +58,10 @@ export default defineStore("navigation", {
drawer === "activePlatformInfoDrawer"
? this.activePlatformInfoDrawer
: false;
+ this.activeCollectionInfoDrawer =
+ drawer === "activeCollectionInfoDrawer"
+ ? this.activeCollectionInfoDrawer
+ : false;
},
},
});
diff --git a/frontend/src/styles/common.css b/frontend/src/styles/common.css
index e754663f8..feee58f3c 100644
--- a/frontend/src/styles/common.css
+++ b/frontend/src/styles/common.css
@@ -8,6 +8,9 @@ body {
1px 1px 3px #000000,
0 0 3px #000000 !important;
}
+.v-progress-linear {
+ z-index: 9999 !important;
+}
.translucent {
backdrop-filter: blur(3px) !important;
text-shadow:
@@ -66,14 +69,3 @@ body {
#main-app-bar ~ #gallery-app-bar {
top: 45px !important;
}
-/*
-Calculation to fix 100dvh:
-main-app-bar -> 45px
-gallery-app-bar -> 48px
-*/
-.fill-height-desktop {
- height: calc(100dvh - 48px) !important;
-}
-.fill-height-mobile {
- height: calc(100dvh - 45px - 48px) !important;
-}
diff --git a/frontend/src/types/emitter.d.ts b/frontend/src/types/emitter.d.ts
index f7ca33e13..e95f3b301 100644
--- a/frontend/src/types/emitter.d.ts
+++ b/frontend/src/types/emitter.d.ts
@@ -4,11 +4,6 @@ import type { Platform } from "@/stores/platforms";
import type { SimpleRom } from "@/stores/roms";
import type { User } from "@/stores/users";
-export type UserItem = User & {
- password: string;
- avatar?: File;
-};
-
export type SnackbarStatus = {
id?: number;
msg: string;
@@ -25,7 +20,7 @@ export type Events = {
showRemoveFromCollectionDialog: SimpleRom[];
showDeleteCollectionDialog: Collection;
showMatchRomDialog: SimpleRom;
- showSearchCoverDialog: string;
+ showSearchCoverDialog: { term: string; aspectRatio: number | null };
updateUrlCover: string;
showSearchRomDialog: null;
showEditRomDialog: SimpleRom;
diff --git a/frontend/src/types/user.d.ts b/frontend/src/types/user.d.ts
new file mode 100644
index 000000000..a05129b33
--- /dev/null
+++ b/frontend/src/types/user.d.ts
@@ -0,0 +1,4 @@
+export type UserItem = User & {
+ password: string;
+ avatar?: File;
+};
diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts
index faee99ca9..642802ad4 100644
--- a/frontend/src/utils/index.ts
+++ b/frontend/src/utils/index.ts
@@ -3,6 +3,9 @@ import type { SimpleRom } from "@/stores/roms";
import type { Heartbeat } from "@/stores/heartbeat";
import type { RomUserStatus } from "@/__generated__";
+/**
+ * Views configuration object.
+ */
export const views: Record<
number,
{
@@ -44,8 +47,17 @@ export const views: Record<
},
};
+/**
+ * Default path for user avatars.
+ */
export const defaultAvatarPath = "/assets/default/user.png";
+/**
+ * Normalize a string by converting it to lowercase and removing diacritics.
+ *
+ * @param s The string to normalize.
+ * @returns The normalized string.
+ */
export function normalizeString(s: string) {
return s
.toLowerCase()
@@ -53,6 +65,12 @@ export function normalizeString(s: string) {
.replace(/[\u0300-\u036f]/g, "");
}
+/**
+ * Convert a cron expression to a human-readable string.
+ *
+ * @param expression The cron expression to convert.
+ * @returns The human-readable string.
+ */
export function convertCronExperssion(expression: string) {
let convertedExpression = cronstrue.toString(expression, { verbose: true });
convertedExpression =
@@ -61,6 +79,13 @@ export function convertCronExperssion(expression: string) {
return convertedExpression;
}
+/**
+ * Generate a download link for ROM content.
+ *
+ * @param rom The ROM object.
+ * @param files Optional array of file names to include in the download.
+ * @returns The download link.
+ */
export function getDownloadLink({
rom,
files = [],
@@ -82,8 +107,7 @@ export function getDownloadLink({
*
* @param bytes Number of bytes.
* @param decimals Number of decimal places to display.
- *
- * @return Formatted string.
+ * @returns Formatted string.
*/
export function formatBytes(bytes: number, decimals = 2) {
if (bytes === 0) return "0 Bytes";
@@ -95,11 +119,10 @@ export function formatBytes(bytes: number, decimals = 2) {
}
/**
+ * Format a timestamp to a human-readable string.
*
- * Format timestamp to human-readable text
- *
- * @param string timestamp
- * @returns string Formatted timestamp
+ * @param timestamp The timestamp to format.
+ * @returns The formatted timestamp.
*/
export function formatTimestamp(timestamp: string | null) {
if (!timestamp) return "-";
@@ -108,6 +131,12 @@ export function formatTimestamp(timestamp: string | null) {
return date.toLocaleString("en-GB");
}
+/**
+ * Convert a region code to an emoji.
+ *
+ * @param region The region code.
+ * @returns The corresponding emoji.
+ */
export function regionToEmoji(region: string) {
switch (region.toLowerCase()) {
case "as":
@@ -203,6 +232,12 @@ export function regionToEmoji(region: string) {
}
}
+/**
+ * Convert a language code to an emoji.
+ *
+ * @param language The language code.
+ * @returns The corresponding emoji.
+ */
export function languageToEmoji(language: string) {
switch (language.toLowerCase()) {
case "ar":
@@ -264,6 +299,9 @@ export function languageToEmoji(language: string) {
}
}
+/**
+ * Map of supported EJS cores for each platform.
+ */
const _EJS_CORES_MAP = {
"3do": ["opera"],
amiga: ["puae"],
@@ -343,10 +381,23 @@ const _EJS_CORES_MAP = {
export type EJSPlatformSlug = keyof typeof _EJS_CORES_MAP;
+/**
+ * Get the supported EJS cores for a given platform.
+ *
+ * @param platformSlug The platform slug.
+ * @returns An array of supported cores.
+ */
export function getSupportedEJSCores(platformSlug: string) {
return _EJS_CORES_MAP[platformSlug.toLowerCase() as EJSPlatformSlug] || [];
}
+/**
+ * Check if EJS emulation is supported for a given platform.
+ *
+ * @param platformSlug The platform slug.
+ * @param heartbeat The heartbeat object.
+ * @returns True if supported, false otherwise.
+ */
export function isEJSEmulationSupported(
platformSlug: string,
heartbeat: Heartbeat,
@@ -357,6 +408,13 @@ export function isEJSEmulationSupported(
);
}
+/**
+ * Check if Ruffle emulation is supported for a given platform.
+ *
+ * @param platformSlug The platform slug.
+ * @param heartbeat The heartbeat object.
+ * @returns True if supported, false otherwise.
+ */
export function isRuffleEmulationSupported(
platformSlug: string,
heartbeat: Heartbeat,
@@ -369,6 +427,9 @@ export function isRuffleEmulationSupported(
type PlayingStatus = RomUserStatus | "backlogged" | "now_playing" | "hidden";
+/**
+ * Array of difficulty emojis.
+ */
export const difficultyEmojis = [
"😴",
"🥱",
@@ -382,6 +443,9 @@ export const difficultyEmojis = [
"😵",
];
+/**
+ * Map of ROM statuses to their corresponding emoji and text.
+ */
export const romStatusMap: Record<
PlayingStatus,
{ emoji: string; text: string }
@@ -396,10 +460,19 @@ export const romStatusMap: Record<
hidden: { emoji: "👻", text: "Hidden" },
};
+/**
+ * Inverse map of ROM statuses from text to status key.
+ */
const inverseRomStatusMap = Object.fromEntries(
Object.entries(romStatusMap).map(([key, value]) => [value.text, key]),
) as Record;
+/**
+ * Get the emoji for a given ROM status.
+ *
+ * @param status The ROM status.
+ * @returns The corresponding emoji.
+ */
export function getEmojiForStatus(status: PlayingStatus) {
if (status) {
return romStatusMap[status].emoji;
@@ -408,6 +481,12 @@ export function getEmojiForStatus(status: PlayingStatus) {
}
}
+/**
+ * Get the text for a given ROM status.
+ *
+ * @param status The ROM status.
+ * @returns The corresponding text.
+ */
export function getTextForStatus(status: PlayingStatus) {
if (status) {
return romStatusMap[status].text;
@@ -416,6 +495,12 @@ export function getTextForStatus(status: PlayingStatus) {
}
}
+/**
+ * Get the status key for a given text.
+ *
+ * @param text The text to convert.
+ * @returns The corresponding status key.
+ */
export function getStatusKeyForText(text: string) {
return inverseRomStatusMap[text];
}
diff --git a/frontend/src/views/Auth/Login.vue b/frontend/src/views/Auth/Login.vue
new file mode 100644
index 000000000..da11ce94e
--- /dev/null
+++ b/frontend/src/views/Auth/Login.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+ Login
+
+ mdi-chevron-right-circle-outline
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/views/Auth/Setup.vue b/frontend/src/views/Auth/Setup.vue
new file mode 100644
index 000000000..8fecf8f5f
--- /dev/null
+++ b/frontend/src/views/Auth/Setup.vue
@@ -0,0 +1,191 @@
+
+
+
+
+
+
+
+
+
+
+ Create an admin user
+
+
+
+
+
+
+
+ Check metadata sources
+
+
+
+
+
+
+
+
+
+
+ Create an admin user
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Check metadata sources
+
+
+
+
+
+
+
+
+
+
+
+ ❌
+ ✅
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ isFirstStep ? "" : "previous" }}
+
+
+
+
+ {{ !isLastStep ? "Next" : "Finish" }}
+
+
+
+
+
+
+
diff --git a/frontend/src/views/Dashboard.vue b/frontend/src/views/Dashboard.vue
deleted file mode 100644
index ad0814bac..000000000
--- a/frontend/src/views/Dashboard.vue
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/frontend/src/views/Gallery/Collection.vue b/frontend/src/views/Gallery/Collection.vue
index 519eb21c0..03d7467ea 100644
--- a/frontend/src/views/Gallery/Collection.vue
+++ b/frontend/src/views/Gallery/Collection.vue
@@ -287,16 +287,11 @@ onBeforeUnmount(() => {
-
+
{
:key="rom.updated_at"
:rom="rom"
title-on-hover
- pointerOnHover
- withLink
+ pointer-on-hover
+ with-link
show-flags
show-action-bar
+ show-fav
transform-scale
with-border
- show-fav
show-platform-icon
:with-border-romm-accent="
romsStore.isSimpleRom(rom) && selectedRoms?.includes(rom)
@@ -325,16 +320,11 @@ onBeforeUnmount(() => {
@touchend="onGameTouchEnd"
/>
+
-
-
-
-
+
+
+
diff --git a/frontend/src/views/Gallery/Platform.vue b/frontend/src/views/Gallery/Platform.vue
index a10351305..e0fbebbb0 100644
--- a/frontend/src/views/Gallery/Platform.vue
+++ b/frontend/src/views/Gallery/Platform.vue
@@ -43,6 +43,7 @@ let timeout: ReturnType;
const emitter = inject>("emitter");
emitter?.on("filter", onFilterChange);
+// Functions
async function fetchRoms() {
if (gettingRoms.value) return;
@@ -303,16 +304,11 @@ onBeforeUnmount(() => {
-
+
{
:key="rom.updated_at"
:rom="rom"
title-on-hover
- pointerOnHover
- withLink
+ pointer-on-hover
+ with-link
show-flags
show-action-bar
show-fav
@@ -341,16 +337,11 @@ onBeforeUnmount(() => {
@touchend="onGameTouchEnd"
/>
+
-
-
-
-
+
+
+
diff --git a/frontend/src/views/GameDetails.vue b/frontend/src/views/GameDetails.vue
index de1a32fd2..347e77030 100644
--- a/frontend/src/views/GameDetails.vue
+++ b/frontend/src/views/GameDetails.vue
@@ -7,7 +7,6 @@ import GameInfo from "@/components/Details/Info/GameInfo.vue";
import Personal from "@/components/Details/Personal.vue";
import RelatedGames from "@/components/Details/RelatedGames.vue";
import Saves from "@/components/Details/Saves.vue";
-import storePlatforms from "@/stores/platforms";
import States from "@/components/Details/States.vue";
import TitleInfo from "@/components/Details/Title.vue";
import EmptyGame from "@/components/common/EmptyGame.vue";
@@ -37,7 +36,6 @@ const { smAndDown, mdAndDown, mdAndUp, lgAndUp } = useDisplay();
const emitter = inject>("emitter");
const noRomError = ref(false);
const romsStore = storeRoms();
-const platfotmsStore = storePlatforms();
const { currentRom, gettingRoms } = storeToRefs(romsStore);
async function fetchDetails() {
@@ -89,63 +87,36 @@ watch(
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
Details
Saves
States
@@ -176,12 +147,10 @@ watch(
Related Games
-
-
-
+
-
+
@@ -227,11 +196,11 @@ watch(
-
-
-
-
-
+
+
+
+
+
@@ -239,20 +208,10 @@ watch(
diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue
index a139734b4..9b2a5b1ae 100644
--- a/frontend/src/views/Home.vue
+++ b/frontend/src/views/Home.vue
@@ -1,122 +1,49 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue
deleted file mode 100644
index ab89c816d..000000000
--- a/frontend/src/views/Login.vue
+++ /dev/null
@@ -1,141 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Login
-
- mdi-chevron-right-circle-outline
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{
- heartbeatStore.value.VERSION
- }}
-
-
-
-
-
diff --git a/frontend/src/views/Management.vue b/frontend/src/views/Management.vue
deleted file mode 100644
index f3ed83279..000000000
--- a/frontend/src/views/Management.vue
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
diff --git a/frontend/src/views/EmulatorJS/Base.vue b/frontend/src/views/Player/EmulatorJS/Base.vue
similarity index 95%
rename from frontend/src/views/EmulatorJS/Base.vue
rename to frontend/src/views/Player/EmulatorJS/Base.vue
index 954aab42c..33a37a610 100644
--- a/frontend/src/views/EmulatorJS/Base.vue
+++ b/frontend/src/views/Player/EmulatorJS/Base.vue
@@ -1,12 +1,12 @@
-
-
-
-
-
diff --git a/frontend/src/views/Administration.vue b/frontend/src/views/Settings/Administration.vue
similarity index 58%
rename from frontend/src/views/Administration.vue
rename to frontend/src/views/Settings/Administration.vue
index 3e8f5abcf..34266b998 100644
--- a/frontend/src/views/Administration.vue
+++ b/frontend/src/views/Settings/Administration.vue
@@ -1,6 +1,6 @@
+
+
+
+
+
+
diff --git a/frontend/src/views/Settings/UserInterface.vue b/frontend/src/views/Settings/UserInterface.vue
new file mode 100644
index 000000000..dc874d4f0
--- /dev/null
+++ b/frontend/src/views/Settings/UserInterface.vue
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/frontend/src/views/Setup.vue b/frontend/src/views/Setup.vue
deleted file mode 100644
index fad9421ec..000000000
--- a/frontend/src/views/Setup.vue
+++ /dev/null
@@ -1,237 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Create an admin user
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Check metadata sources
-
-
-
-
-
-
-
-
-
-
-
- ❌
- ✅
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ isFirstStep ? "" : "previous" }}
-
-
- {{ !isLastStep ? "Next" : "Finish" }}
-
-
-
-
-
-
-
- {{ heartbeat.value.VERSION }}
-
-
-
-
-