Variety of bug fixes (#432)

* Fix #414

* Implement #268

* Add #269
This commit is contained in:
DecDuck
2026-06-21 20:07:59 +10:00
committed by GitHub
parent cbecd1161d
commit 38c11567ef
5 changed files with 97 additions and 38 deletions
+6 -1
View File
@@ -9,7 +9,12 @@ export const updateUser = async () => {
const user = useUser(); const user = useUser();
if (user.value === null) return; if (user.value === null) return;
user.value = await $dropFetch<UserModel | null>("/api/v1/user"); user.value = await $dropFetch<UserModel | null>("/api/v1/user", {
// Forward headers manually when called outside a component
headers: import.meta.server
? useRequestHeaders(["cookie", "authorization"])
: undefined,
});
}; };
export async function completeSignin() { export async function completeSignin() {
+3
View File
@@ -547,6 +547,9 @@
"sources": { "sources": {
"create": "Create source", "create": "Create source",
"createDesc": "Drop will use this source to access your game library, and make them available.", "createDesc": "Drop will use this source to access your game library, and make them available.",
"deleteButton": "Delete source",
"deleteDesc": "Deleting \"{0}\" will cascade delete the library, all of its games, all of their versions, and all of their metadata. This action cannot be undone.",
"deleteTitle": "Delete library source?",
"desc": "Configure your library sources, where Drop will look for new games and versions to import.", "desc": "Configure your library sources, where Drop will look for new games and versions to import.",
"documentationLink": "Documentation {arrow}", "documentationLink": "Documentation {arrow}",
"edit": "Edit source", "edit": "Edit source",
+51 -14
View File
@@ -126,16 +126,50 @@
</div> </div>
<div <div
class="sticky top-0 z-40 flex items-center gap-x-6 bg-zinc-900 px-4 py-4 shadow-sm sm:px-6 lg:hidden" class="sticky top-0 z-40 lg:pl-20 border-b border-zinc-800 bg-zinc-950 shadow-sm"
> >
<button <div class="flex items-center gap-x-4 px-4 py-2 sm:px-6 lg:px-8">
type="button" <button
class="-m-2.5 p-2.5 text-zinc-400 lg:hidden" type="button"
@click="sidebarOpen = true" class="-m-2.5 p-2.5 text-zinc-400 lg:hidden"
> @click="sidebarOpen = true"
<span class="sr-only">{{ $t("header.openSidebar") }}</span> >
<Bars3Icon class="h-6 w-6" aria-hidden="true" /> <span class="sr-only">{{ $t("header.openSidebar") }}</span>
</button> <Bars3Icon class="h-6 w-6" aria-hidden="true" />
</button>
<div class="flex-1" />
<ol class="inline-flex items-center gap-3">
<li>
<Menu as="div" class="relative inline-block">
<MenuButton>
<UserHeaderWidget :notifications="unreadNotifications.length">
<BellIcon class="h-5" />
</UserHeaderWidget>
</MenuButton>
<transition
enter-active-class="transition ease-out duration-100"
enter-from-class="transform opacity-0 scale-95"
enter-to-class="transform opacity-100 scale-100"
leave-active-class="transition ease-in duration-75"
leave-from-class="transform opacity-100 scale-100"
leave-to-class="transform opacity-0 scale-95"
>
<MenuItems
class="absolute right-0 top-10 z-50 w-96 focus:outline-none shadow-md"
>
<UserHeaderNotificationWidgetPanel
:notifications="unreadNotifications"
/>
</MenuItems>
</transition>
</Menu>
</li>
<UserHeaderUserWidget />
</ol>
</div>
</div> </div>
<main class="lg:pl-20 min-h-screen bg-zinc-900 flex flex-col"> <main class="lg:pl-20 min-h-screen bg-zinc-900 flex flex-col">
@@ -156,6 +190,9 @@ import {
DialogPanel, DialogPanel,
TransitionChild, TransitionChild,
TransitionRoot, TransitionRoot,
Menu,
MenuButton,
MenuItems,
} from "@headlessui/vue"; } from "@headlessui/vue";
import { import {
Bars3Icon, Bars3Icon,
@@ -168,7 +205,7 @@ import {
} from "@heroicons/vue/24/outline"; } from "@heroicons/vue/24/outline";
import type { NavigationItem } from "~/composables/types"; import type { NavigationItem } from "~/composables/types";
import { useCurrentNavigationIndex } from "~/composables/current-page-engine"; import { useCurrentNavigationIndex } from "~/composables/current-page-engine";
import { ArrowLeftIcon } from "@heroicons/vue/16/solid"; import { ArrowLeftIcon, BellIcon } from "@heroicons/vue/16/solid";
import { XMarkIcon } from "@heroicons/vue/24/solid"; import { XMarkIcon } from "@heroicons/vue/24/solid";
import type { Settings } from "~/server/internal/utils/types"; import type { Settings } from "~/server/internal/utils/types";
@@ -219,10 +256,10 @@ const navigation: Array<NavigationItem & { icon: Component }> = [
}, },
]; ];
// const notifications = useNotifications(); const notifications = useNotifications();
// const unreadNotifications = computed(() => const unreadNotifications = computed(() =>
// notifications.value.filter((e) => !e.read) notifications.value.filter((e) => !e.read),
// ); );
const currentNavigationIndex = useCurrentNavigationIndex(navigation); const currentNavigationIndex = useCurrentNavigationIndex(navigation);
+1 -2
View File
@@ -2,14 +2,13 @@ const whitelistedPrefixes = ["/auth", "/api", "/setup"];
const requireAdmin = ["/admin"]; const requireAdmin = ["/admin"];
export default defineNuxtRouteMiddleware(async (to, _from) => { export default defineNuxtRouteMiddleware(async (to, _from) => {
if (import.meta.server) return;
const error = useError(); const error = useError();
if (error.value !== undefined) return; if (error.value !== undefined) return;
if (whitelistedPrefixes.findIndex((e) => to.fullPath.startsWith(e)) != -1) if (whitelistedPrefixes.findIndex((e) => to.fullPath.startsWith(e)) != -1)
return; return;
const user = useUser(); const user = useUser();
if (user === undefined) { if (user.value === undefined) {
await updateUser(); await updateUser();
} }
if (!user.value) { if (!user.value) {
+36 -21
View File
@@ -347,30 +347,45 @@ function edit(index: number) {
actionSourceOpen.value = true; actionSourceOpen.value = true;
} }
async function deleteSource(index: number) { function deleteSource(index: number) {
const source = sources.value[index]; const source = sources.value[index];
if (!source) return; if (!source) return;
try { createModal(
await $dropFetch("/api/v1/admin/library/sources", { ModalType.Confirmation,
method: "DELETE", {
body: { id: source.id }, title: t("library.admin.sources.deleteTitle"),
headers, description: t("library.admin.sources.deleteDesc", [source.name]),
}); buttonText: t("library.admin.sources.deleteButton"),
} catch (e) { },
createModal( async (event, close) => {
ModalType.Notification, if (event !== "confirm") return close();
{
title: t("errors.library.source.delete.title"),
description: t("errors.library.source.delete.desc", [
// @ts-expect-error attempt to display statusMessage on error
e?.statusMessage ?? t("errors.unknown"),
]),
},
(_, c) => c(),
);
}
sources.value.splice(index, 1); try {
await $dropFetch("/api/v1/admin/library/sources", {
method: "DELETE",
body: { id: source.id },
headers,
});
} catch (e) {
createModal(
ModalType.Notification,
{
title: t("errors.library.source.delete.title"),
description: t("errors.library.source.delete.desc", [
// @ts-expect-error attempt to display statusMessage on error
e?.statusMessage ?? t("errors.unknown"),
]),
},
(_, c) => c(),
);
return close();
}
const currentIndex = sources.value.findIndex((s) => s.id === source.id);
if (currentIndex !== -1) sources.value.splice(currentIndex, 1);
close();
},
);
} }
</script> </script>