2025-04-30 01:44:16 +02:00
|
|
|
<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-04-30 01:44:16 +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";
|
2025-04-30 18:03:14 +02:00
|
|
|
import Developer from "./developer.vue";
|
2025-04-30 01:44:16 +02:00
|
|
|
import Emojis from "./emojis/index.vue";
|
|
|
|
|
import Page from "./page.vue";
|
|
|
|
|
import { preferences } from "./preferences";
|
2025-05-01 01:45:46 +02:00
|
|
|
import Profile from "./profile.vue";
|
2025-04-30 01:44:16 +02:00
|
|
|
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)
|
2025-05-01 01:45:46 +02:00
|
|
|
.concat(["Account", "Emojis", "Developer", "About"])
|
2025-04-30 01:44:16 +02:00
|
|
|
// Remove duplicates
|
|
|
|
|
.filter((c, i, a) => a.indexOf(c) === i);
|
2025-05-01 01:45:46 +02:00
|
|
|
const extraPages = ["Account", "Emojis", "Developer", "About"];
|
2025-04-30 01:44:16 +02:00
|
|
|
|
|
|
|
|
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(
|
|
|
|
|
client,
|
|
|
|
|
"jessew@vs.cpluspatch.com",
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const { account: author2 } = useAccountFromAcct(
|
|
|
|
|
client,
|
|
|
|
|
"aprl@social.lysand.org",
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const { account: author3 } = useAccountFromAcct(
|
|
|
|
|
client,
|
|
|
|
|
"lina@social.lysand.org",
|
|
|
|
|
);
|
2025-05-01 01:45:46 +02:00
|
|
|
|
2025-05-05 18:17:33 +02:00
|
|
|
const { account: author4 } = useAccountFromAcct(client, "nyx@v.everypizza.im");
|
|
|
|
|
|
2025-05-01 01:45:46 +02:00
|
|
|
const open = ref(false);
|
|
|
|
|
|
|
|
|
|
useListen("preferences:open", () => {
|
|
|
|
|
open.value = true;
|
|
|
|
|
});
|
2025-04-30 01:44:16 +02:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
2025-05-01 01:45:46 +02:00
|
|
|
<Dialog v-model:open="open" v-if="identity">
|
|
|
|
|
<DialogContent class="md:max-w-5xl w-full h-full p-0 md:max-h-[70dvh] overflow-hidden">
|
2025-04-30 01:44:16 +02:00
|
|
|
<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="identity.account.display_name || identity.account.username"
|
|
|
|
|
:src="identity.account.avatar" />
|
|
|
|
|
<DialogTitle>Preferences</DialogTitle>
|
|
|
|
|
</div>
|
|
|
|
|
<DialogDescription class="sr-only">
|
|
|
|
|
Make changes to your preferences here.
|
|
|
|
|
</DialogDescription>
|
|
|
|
|
<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>
|
|
|
|
|
<TabsContent v-for="page in pages.filter(p => !extraPages.includes(p))" :key="page" :value="page"
|
|
|
|
|
as-child>
|
|
|
|
|
<Page :title="page">
|
|
|
|
|
<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)"
|
|
|
|
|
:name="category" />
|
|
|
|
|
</Page>
|
|
|
|
|
</TabsContent>
|
|
|
|
|
<TabsContent value="Emojis" as-child>
|
|
|
|
|
<Page title="Emojis">
|
|
|
|
|
<Emojis />
|
|
|
|
|
</Page>
|
|
|
|
|
</TabsContent>
|
2025-05-01 01:45:46 +02:00
|
|
|
<TabsContent value="Account" as-child>
|
|
|
|
|
<Page title="Account">
|
|
|
|
|
<Profile />
|
|
|
|
|
</Page>
|
|
|
|
|
</TabsContent>
|
2025-04-30 18:03:14 +02:00
|
|
|
<TabsContent value="Developer" as-child>
|
|
|
|
|
<Page title="Developer">
|
|
|
|
|
<Developer />
|
|
|
|
|
</Page>
|
|
|
|
|
</TabsContent>
|
2025-04-30 01:44:16 +02:00
|
|
|
<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>
|
|
|
|
|
|
|
|
|
|
<Stats />
|
|
|
|
|
</section>
|
|
|
|
|
<Separator />
|
|
|
|
|
<section class="space-y-2">
|
|
|
|
|
<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" />
|
2025-05-05 18:17:33 +02:00
|
|
|
<TinyCard v-if="author4" :account="author4" domain="v.everypizza.im" />
|
2025-04-30 01:44:16 +02:00
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
<Separator />
|
|
|
|
|
<section class="space-y-2">
|
|
|
|
|
<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
|
|
|
|
|
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>
|