frontend/app/components/preferences/dialog.vue

211 lines
8.2 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import {
InfoIcon,
PaletteIcon,
SettingsIcon,
ShieldCheckIcon,
SmileIcon,
TerminalSquareIcon,
UserIcon,
} from "lucide-vue-next";
2025-06-26 22:39:02 +02:00
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
2025-07-16 07:48:39 +02:00
import pkg from "~~/package.json";
import Avatar from "../profiles/avatar.vue";
import TinyCard from "../profiles/tiny-card.vue";
import { Separator } from "../ui/separator";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
import Category from "./category.vue";
import Developer from "./developer.vue";
import Emojis from "./emojis/index.vue";
import Page from "./page.vue";
import { preferences } from "./preferences";
import Profile from "./profile.vue";
import Stats from "./stats.vue";
const pages = Object.values(preferences)
.map((p) => p.options.category)
.filter((c) => c !== undefined)
.map((c) => c.split("/")[0] as string)
.concat(["Account", "Emojis", "Developer", "About"])
// Remove duplicates
.filter((c, i, a) => a.indexOf(c) === i);
const extraPages = ["Account", "Emojis", "Developer", "About"];
const icons: Record<string, Component> = {
Account: UserIcon,
Appearance: PaletteIcon,
Emojis: SmileIcon,
Behaviour: SettingsIcon,
Roles: ShieldCheckIcon,
Developer: TerminalSquareIcon,
About: InfoIcon,
};
// For each page, map the associated categories
const categories = Object.fromEntries(
pages.map((page) => {
const categories = Object.values(preferences)
.map((p) => p.options.category)
.filter((c) => c !== undefined)
.filter((c) => c.split("/")[0] === page)
.map((c) => c.split("/")[1] as string)
// Remove duplicates
.filter((c, i, a) => a.indexOf(c) === i);
return [page, categories];
}),
);
const { account: author1 } = useAccountFromAcct("jessew@vs.cpluspatch.com");
const { account: author2 } = useAccountFromAcct("aprl@social.lysand.org");
const { account: author3 } = useAccountFromAcct("lina@social.lysand.org");
const { account: author4 } = useAccountFromAcct("nyx@v.everypizza.im");
const authStore = useAuthStore();
const open = ref(false);
useListen("preferences:open", () => {
open.value = true;
});
</script>
<template>
<Dialog v-model:open="open" v-if="authStore.isSignedIn">
2025-12-09 22:32:22 +01:00
<DialogContent
class="md:max-w-5xl w-full h-full p-0 md:max-h-[70dvh] overflow-hidden"
>
<Tabs
class="md:grid-cols-[auto_minmax(0,1fr)] !grid gap-2 *:p-4 overflow-hidden *:overflow-y-auto *:h-full"
orientation="vertical"
:default-value="pages[0]"
>
<DialogHeader
class="gap-6 grid grid-rows-[auto_minmax(0,1fr)] border-b md:border-b-0 md:border-r min-w-60 text-left"
>
<div
class="grid gap-3 items-center grid-cols-[auto_minmax(0,1fr)]"
>
<Avatar
:name="authStore.account!.display_name || authStore.account!.username"
:src="authStore.account!.avatar"
/>
<DialogTitle>Preferences</DialogTitle>
</div>
<DialogDescription class="sr-only">
Make changes to your preferences here.
</DialogDescription>
2025-12-09 22:32:22 +01:00
<TabsList
class="md:grid md:grid-cols-1 w-full h-fit *:justify-start !justify-start"
>
<TabsTrigger
v-for="page in pages"
:key="page"
:value="page"
>
<component :is="icons[page]" class="size-4 mr-2"/>
{{ page }}
</TabsTrigger>
</TabsList>
</DialogHeader>
2025-12-09 22:32:22 +01:00
<TabsContent
v-for="page in pages.filter(p => !extraPages.includes(p))"
:key="page"
:value="page"
as-child
>
<Page :title="page">
2025-12-09 22:32:22 +01:00
<Category
v-for="category in categories[page]"
:key="category"
:preferences="Object.entries(preferences).filter(([, p]) => p.options.category === `${page}/${category}`).map(([k,]) => k as keyof typeof preferences)"
2025-12-09 22:32:22 +01:00
:name="category"
/>
</Page>
</TabsContent>
<TabsContent value="Emojis" as-child>
<Page title="Emojis">
2025-12-09 22:32:22 +01:00
<Emojis/>
</Page>
</TabsContent>
<TabsContent value="Account" as-child>
<Page title="Account">
2025-12-09 22:32:22 +01:00
<Profile/>
</Page>
</TabsContent>
<TabsContent value="Developer" as-child>
<Page title="Developer">
2025-12-09 22:32:22 +01:00
<Developer/>
</Page>
</TabsContent>
<TabsContent value="About" as-child>
<Page title="About">
<section class="space-y-4">
<p class="leading-7 text-sm max-w-xl">
{{ pkg.description }}
</p>
2025-12-09 22:32:22 +01:00
<Stats/>
</section>
2025-12-09 22:32:22 +01:00
<Separator/>
<section class="space-y-2">
2025-12-09 22:32:22 +01:00
<h3 class="text-lg font-semibold tracking-tight">
Developers
</h3>
<div
class="grid lg:grid-cols-3 md:grid-cols-2 grid-cols-1 gap-4"
>
<TinyCard
v-if="author1"
:account="author1"
domain="vs.cpluspatch.com"
/>
<TinyCard
v-if="author2"
:account="author2"
domain="social.lysand.org"
/>
<TinyCard
v-if="author3"
:account="author3"
domain="social.lysand.org"
/>
<TinyCard
v-if="author4"
:account="author4"
domain="v.everypizza.im"
/>
</div>
</section>
2025-12-09 22:32:22 +01:00
<Separator/>
<section class="space-y-2">
2025-12-09 22:32:22 +01:00
<h3 class="text-lg font-semibold tracking-tight">
Dependencies
</h3>
<ul
class="grid lg:grid-cols-2 gap-2 grid-cols-1 items-center justify-center list-disc ml-6"
>
<li
v-for="[dep, version] in Object.entries(pkg.dependencies)"
:key="dep"
>
<code
2025-12-09 22:32:22 +01:00
class="rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-semibold"
>
{{ dep }}@{{ version }}
</code>
</li>
</ul>
</section>
</Page>
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
</template>