mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 08:28:20 +01:00
chore: ⬆️ Upgrade dependencies
This commit is contained in:
parent
3e28801709
commit
7649ecfb80
14
app.vue
14
app.vue
|
|
@ -18,13 +18,14 @@ import "~/styles/index.css";
|
||||||
import { convert } from "html-to-text";
|
import { convert } from "html-to-text";
|
||||||
import ConfirmationModal from "./components/modals/confirm.vue";
|
import ConfirmationModal from "./components/modals/confirm.vue";
|
||||||
import { Toaster } from "./components/ui/sonner";
|
import { Toaster } from "./components/ui/sonner";
|
||||||
import { defineGetLocale } from "./paraglide/runtime";
|
import { overwriteGetLocale } from "./paraglide/runtime";
|
||||||
import { type EnumSetting, SettingIds } from "./settings";
|
import { type EnumSetting, SettingIds } from "./settings";
|
||||||
|
import { TooltipProvider } from "./components/ui/tooltip";
|
||||||
// Sin
|
// Sin
|
||||||
//import "~/styles/mcdonalds.css";
|
//import "~/styles/mcdonalds.css";
|
||||||
|
|
||||||
const lang = useLanguage();
|
const lang = useLanguage();
|
||||||
defineGetLocale(() => lang.value);
|
overwriteGetLocale(() => lang.value);
|
||||||
|
|
||||||
const code = useRequestURL().searchParams.get("code");
|
const code = useRequestURL().searchParams.get("code");
|
||||||
const origin = useRequestURL().searchParams.get("origin");
|
const origin = useRequestURL().searchParams.get("origin");
|
||||||
|
|
@ -57,7 +58,7 @@ useSeoMeta({
|
||||||
ogImage: computed(() => instance.value?.banner?.url),
|
ogImage: computed(() => instance.value?.banner?.url),
|
||||||
twitterTitle: computed(() => instance.value?.title ?? ""),
|
twitterTitle: computed(() => instance.value?.title ?? ""),
|
||||||
twitterDescription: computed(() =>
|
twitterDescription: computed(() =>
|
||||||
convert(description.value?.content ?? ""),
|
convert(description.value?.content ?? "")
|
||||||
),
|
),
|
||||||
twitterImage: computed(() => instance.value?.banner?.url),
|
twitterImage: computed(() => instance.value?.banner?.url),
|
||||||
description: computed(() => convert(description.value?.content ?? "")),
|
description: computed(() => convert(description.value?.content ?? "")),
|
||||||
|
|
@ -75,7 +76,7 @@ useHead({
|
||||||
|
|
||||||
if (code && origin && appData.value && route.path !== "/oauth/code") {
|
if (code && origin && appData.value && route.path !== "/oauth/code") {
|
||||||
const newOrigin = new URL(
|
const newOrigin = new URL(
|
||||||
URL.canParse(origin) ? origin : `https://${origin}`,
|
URL.canParse(origin) ? origin : `https://${origin}`
|
||||||
);
|
);
|
||||||
|
|
||||||
signInWithCode(code, appData.value, newOrigin);
|
signInWithCode(code, appData.value, newOrigin);
|
||||||
|
|
@ -83,7 +84,7 @@ if (code && origin && appData.value && route.path !== "/oauth/code") {
|
||||||
|
|
||||||
if (origin && !code) {
|
if (origin && !code) {
|
||||||
const newOrigin = new URL(
|
const newOrigin = new URL(
|
||||||
URL.canParse(origin) ? origin : `https://${origin}`,
|
URL.canParse(origin) ? origin : `https://${origin}`
|
||||||
);
|
);
|
||||||
|
|
||||||
signIn(appData, newOrigin);
|
signIn(appData, newOrigin);
|
||||||
|
|
@ -104,6 +105,7 @@ body {
|
||||||
|
|
||||||
html.theme-changing * {
|
html.theme-changing * {
|
||||||
/* Stroke and fill aren't animatable */
|
/* Stroke and fill aren't animatable */
|
||||||
transition: background-color 1s ease, border 1s ease, color 1s ease, box-shadow 1s ease !important;
|
transition: background-color 1s ease, border 1s ease, color 1s ease,
|
||||||
|
box-shadow 1s ease !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,28 @@
|
||||||
<Note :note="relation.note" :hide-actions="true" :small-layout="true" />
|
<Note :note="relation.note" :hide-actions="true" :small-layout="true" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Input v-model:model-value="state.contentWarning" v-if="state.sensitive"
|
<Input
|
||||||
placeholder="Put your content warning here" />
|
v-model:model-value="state.contentWarning"
|
||||||
|
v-if="state.sensitive"
|
||||||
|
placeholder="Put your content warning here"
|
||||||
|
/>
|
||||||
|
|
||||||
<EditorContent v-model:content="state.content" :placeholder="chosenSplash"
|
<EditorContent
|
||||||
|
v-model:content="state.content"
|
||||||
|
:placeholder="chosenSplash"
|
||||||
class="*:!border-none *:!ring-0 *:!outline-none *:rounded-none p-0 *:max-h-[50dvh] *:overflow-y-auto *:min-h-48 *:!ring-offset-0 *:h-full"
|
class="*:!border-none *:!ring-0 *:!outline-none *:rounded-none p-0 *:max-h-[50dvh] *:overflow-y-auto *:min-h-48 *:!ring-offset-0 *:h-full"
|
||||||
:disabled="sending" :mode="state.contentType === 'text/html' ? 'rich' : 'plain'" />
|
:disabled="sending"
|
||||||
|
:mode="state.contentType === 'text/html' ? 'rich' : 'plain'"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="w-full flex flex-row gap-2 overflow-x-auto *:shrink-0 pb-2">
|
<div class="w-full flex flex-row gap-2 overflow-x-auto *:shrink-0 pb-2">
|
||||||
<input type="file" ref="fileInput" @change="uploadFileFromEvent" class="hidden" multiple />
|
<input
|
||||||
|
type="file"
|
||||||
|
ref="fileInput"
|
||||||
|
@change="uploadFileFromEvent"
|
||||||
|
class="hidden"
|
||||||
|
multiple
|
||||||
|
/>
|
||||||
<Files v-model:files="state.files" />
|
<Files v-model:files="state.files" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -28,11 +41,15 @@
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger as="div">
|
<TooltipTrigger as="div">
|
||||||
<Toggle variant="default" size="sm" :pressed="state.contentType === 'text/html'" @update:pressed="(i) =>
|
<Toggle
|
||||||
(state.contentType = i
|
variant="default"
|
||||||
? 'text/html'
|
size="sm"
|
||||||
: 'text/plain')
|
:pressed="state.contentType === 'text/html'"
|
||||||
">
|
@update:pressed="
|
||||||
|
(i) =>
|
||||||
|
(state.contentType = i ? 'text/html' : 'text/plain')
|
||||||
|
"
|
||||||
|
>
|
||||||
<LetterText class="!size-5" />
|
<LetterText class="!size-5" />
|
||||||
</Toggle>
|
</Toggle>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
|
|
@ -41,14 +58,27 @@
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Select v-model:model-value="state.visibility">
|
<Select v-model:model-value="state.visibility">
|
||||||
<SelectTrigger :as-child="true" :disabled="relation?.type === 'edit'">
|
<SelectTrigger
|
||||||
|
:as-child="true"
|
||||||
|
:disabled="relation?.type === 'edit'"
|
||||||
|
>
|
||||||
<Button variant="ghost" size="icon">
|
<Button variant="ghost" size="icon">
|
||||||
<component :is="visibilities[state.visibility].icon" class="!size-5" />
|
<component
|
||||||
|
:is="visibilities[state.visibility].icon"
|
||||||
|
class="!size-5"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem v-for="(v, k) in visibilities" :key="k" @click="state.visibility = k" :value="k">
|
<SelectItem
|
||||||
<div class="flex flex-row gap-4 items-center w-full justify-between">
|
v-for="(v, k) in visibilities"
|
||||||
|
:key="k"
|
||||||
|
@click="state.visibility = k"
|
||||||
|
:value="k"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex flex-row gap-4 items-center w-full justify-between"
|
||||||
|
>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span class="font-semibold">{{ v.name }}</span>
|
<span class="font-semibold">{{ v.name }}</span>
|
||||||
<span>{{ v.text }}</span>
|
<span>{{ v.text }}</span>
|
||||||
|
|
@ -80,7 +110,11 @@
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger as="div">
|
<TooltipTrigger as="div">
|
||||||
<Toggle variant="default" size="sm" v-model:pressed="state.sensitive">
|
<Toggle
|
||||||
|
variant="default"
|
||||||
|
size="sm"
|
||||||
|
v-model:pressed="state.sensitive"
|
||||||
|
>
|
||||||
<TriangleAlert class="!size-5" />
|
<TriangleAlert class="!size-5" />
|
||||||
</Toggle>
|
</Toggle>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
|
|
@ -88,7 +122,13 @@
|
||||||
<p>{{ m.frail_broad_mallard_dart() }}</p>
|
<p>{{ m.frail_broad_mallard_dart() }}</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Button type="submit" size="lg" class="ml-auto" :disabled="sending" @click="submit">
|
<Button
|
||||||
|
type="submit"
|
||||||
|
size="lg"
|
||||||
|
class="ml-auto"
|
||||||
|
:disabled="sending"
|
||||||
|
@click="submit"
|
||||||
|
>
|
||||||
<Loader v-if="sending" class="!size-5 animate-spin" />
|
<Loader v-if="sending" class="!size-5 animate-spin" />
|
||||||
{{
|
{{
|
||||||
relation?.type === "edit"
|
relation?.type === "edit"
|
||||||
|
|
@ -123,7 +163,9 @@ import EditorContent from "../editor/content.vue";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { Input } from "../ui/input";
|
import { Input } from "../ui/input";
|
||||||
import { Toggle } from "../ui/toggle";
|
import { Toggle } from "../ui/toggle";
|
||||||
|
import { DialogFooter } from "../ui/dialog";
|
||||||
import Files from "./files.vue";
|
import Files from "./files.vue";
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
|
||||||
|
|
||||||
const { Control_Enter, Command_Enter } = useMagicKeys();
|
const { Control_Enter, Command_Enter } = useMagicKeys();
|
||||||
const ctrlEnterSend = useSetting(SettingIds.CtrlEnterToSend);
|
const ctrlEnterSend = useSetting(SettingIds.CtrlEnterToSend);
|
||||||
|
|
@ -179,7 +221,7 @@ const state = reactive({
|
||||||
contentType: "text/html" as "text/html" | "text/plain",
|
contentType: "text/html" as "text/html" | "text/plain",
|
||||||
visibility: (relation?.type === "edit"
|
visibility: (relation?.type === "edit"
|
||||||
? relation.note.visibility
|
? relation.note.visibility
|
||||||
: (defaultVisibility.value.value ?? "public")) as Status["visibility"],
|
: defaultVisibility.value.value ?? "public") as Status["visibility"],
|
||||||
files: (relation?.type === "edit"
|
files: (relation?.type === "edit"
|
||||||
? relation.note.media_attachments.map((a) => ({
|
? relation.note.media_attachments.map((a) => ({
|
||||||
apiId: a.id,
|
apiId: a.id,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
import type { Status, StatusSource } from "@versia/client/types";
|
import type { Status, StatusSource } from "@versia/client/types";
|
||||||
import { toast } from "vue-sonner";
|
import { toast } from "vue-sonner";
|
||||||
import * as m from "~/paraglide/messages.js";
|
import * as m from "~/paraglide/messages.js";
|
||||||
|
|
@ -55,26 +60,43 @@ const relation = ref(
|
||||||
type: "reply" | "quote" | "edit";
|
type: "reply" | "quote" | "edit";
|
||||||
note: Status;
|
note: Status;
|
||||||
source?: StatusSource;
|
source?: StatusSource;
|
||||||
} | null,
|
} | null
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dialog v-model:open="open" @update:open="o => {
|
<Dialog
|
||||||
if (!o) {
|
v-model:open="open"
|
||||||
relation = null; // Unfocus the active element
|
@update:open="
|
||||||
activeElement?.blur();
|
(o) => {
|
||||||
}
|
if (!o) {
|
||||||
}">
|
relation = null; // Unfocus the active element
|
||||||
<DialogContent :hide-close="true"
|
activeElement?.blur();
|
||||||
class="sm:max-w-xl max-w-full w-[calc(100%-2*0.5rem)] grid-rows-[minmax(0,1fr)_auto] max-h-[90dvh] p-5 pt-6 top-2 sm:top-1/2 translate-y-0 sm:-translate-y-1/2 rounded">
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<DialogContent
|
||||||
|
:hide-close="true"
|
||||||
|
class="sm:max-w-xl max-w-full w-[calc(100%-2*0.5rem)] grid-rows-[minmax(0,1fr)_auto] max-h-[90dvh] p-5 pt-6 top-2 sm:top-1/2 translate-y-0 sm:-translate-y-1/2 rounded"
|
||||||
|
>
|
||||||
<DialogTitle class="sr-only">
|
<DialogTitle class="sr-only">
|
||||||
{{ relation?.type === "reply" ? m.loved_busy_mantis_slide() : relation?.type === "quote" ? "Quote" :
|
{{
|
||||||
m.chunky_dull_marlin_trip() }}
|
relation?.type === "reply"
|
||||||
|
? m.loved_busy_mantis_slide()
|
||||||
|
: relation?.type === "quote"
|
||||||
|
? "Quote"
|
||||||
|
: m.chunky_dull_marlin_trip()
|
||||||
|
}}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription class="sr-only">
|
<DialogDescription class="sr-only">
|
||||||
{{ relation?.type === "reply" ? m.tired_grassy_vulture_forgive() : relation?.type === "quote" ?
|
{{
|
||||||
m.livid_livid_nils_snip() : m.brief_cool_capybara_fear() }}
|
relation?.type === "reply"
|
||||||
|
? m.tired_grassy_vulture_forgive()
|
||||||
|
: relation?.type === "quote"
|
||||||
|
? m.livid_livid_nils_snip()
|
||||||
|
: m.brief_cool_capybara_fear()
|
||||||
|
}}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
<Composer :relation="relation ?? undefined" />
|
<Composer :relation="relation ?? undefined" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import type { Status } from "@versia/client/types";
|
import type { Status } from "@versia/client/types";
|
||||||
import { Card, CardFooter, CardHeader } from "../ui/card";
|
import { Card, CardContent, CardFooter, CardHeader } from "../ui/card";
|
||||||
import Actions from "./actions.vue";
|
import Actions from "./actions.vue";
|
||||||
import Content from "./content.vue";
|
import Content from "./content.vue";
|
||||||
import Header from "./header.vue";
|
import Header from "./header.vue";
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<Card
|
<Card
|
||||||
:class="cn('grid hover:cursor-pointer gap-4 items-center p-4', canEdit ? 'grid-cols-[auto,1fr,auto]' : 'grid-cols-[auto,1fr]')">
|
:class="
|
||||||
|
cn(
|
||||||
|
'grid hover:cursor-pointer gap-4 items-center p-4',
|
||||||
|
canEdit
|
||||||
|
? 'grid-cols-[auto,1fr,auto]'
|
||||||
|
: 'grid-cols-[auto,1fr]'
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
<Avatar shape="square">
|
<Avatar shape="square">
|
||||||
<AvatarImage :src="emoji.url" />
|
<AvatarImage :src="emoji.url" />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
|
|
@ -10,7 +18,11 @@
|
||||||
{{ emoji.shortcode }}
|
{{ emoji.shortcode }}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
{{ emoji.global ? m.real_tame_moose_greet() : m.witty_heroic_trout_cry() }}
|
{{
|
||||||
|
emoji.global
|
||||||
|
? m.real_tame_moose_greet()
|
||||||
|
: m.witty_heroic_trout_cry()
|
||||||
|
}}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardFooter class="p-0" v-if="canEdit">
|
<CardFooter class="p-0" v-if="canEdit">
|
||||||
|
|
@ -45,12 +57,13 @@ import { type Emoji, RolePermission } from "@versia/client/types";
|
||||||
import { Delete, Ellipsis, TextCursorInput } from "lucide-vue-next";
|
import { Delete, Ellipsis, TextCursorInput } from "lucide-vue-next";
|
||||||
import { toast } from "vue-sonner";
|
import { toast } from "vue-sonner";
|
||||||
import { confirmModalService } from "~/components/modals/composable";
|
import { confirmModalService } from "~/components/modals/composable";
|
||||||
import { Avatar } from "~/components/ui/avatar";
|
import { Avatar, AvatarImage } from "~/components/ui/avatar";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
CardFooter,
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "~/components/ui/card";
|
} from "~/components/ui/card";
|
||||||
import {
|
import {
|
||||||
|
|
@ -94,7 +107,7 @@ const editName = async () => {
|
||||||
toast.success(m.gaudy_lime_bison_adore());
|
toast.success(m.gaudy_lime_bison_adore());
|
||||||
|
|
||||||
identity.value.emojis = identity.value.emojis.map((e) =>
|
identity.value.emojis = identity.value.emojis.map((e) =>
|
||||||
e.id === emoji.id ? data : e,
|
e.id === emoji.id ? data : e
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
|
|
@ -121,7 +134,7 @@ const _delete = async () => {
|
||||||
toast.success(m.crisp_whole_canary_tear());
|
toast.success(m.crisp_whole_canary_tear());
|
||||||
|
|
||||||
identity.value.emojis = identity.value.emojis.filter(
|
identity.value.emojis = identity.value.emojis.filter(
|
||||||
(e) => e.id !== emoji.id,
|
(e) => e.id !== emoji.id
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,37 @@
|
||||||
{{ m.frail_great_marten_pet() }}
|
{{ m.frail_great_marten_pet() }}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
<form class="p-4 grid gap-6" @submit="submit">
|
<form class="p-4 grid gap-6" @submit="submit">
|
||||||
<div v-if="values.image" class="flex items-center justify-around *:size-20 *:p-2 *:rounded *:border *:shadow">
|
<div
|
||||||
|
v-if="values.image"
|
||||||
|
class="flex items-center justify-around *:size-20 *:p-2 *:rounded *:border *:shadow"
|
||||||
|
>
|
||||||
<div class="bg-background">
|
<div class="bg-background">
|
||||||
<img class="h-full object-cover" :src="createObjectURL(values.image as File)" :alt="values.alt" />
|
<img
|
||||||
|
class="h-full object-cover"
|
||||||
|
:src="createObjectURL(values.image as File)"
|
||||||
|
:alt="values.alt"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-zinc-700">
|
<div class="bg-zinc-700">
|
||||||
<img class="h-full object-cover" :src="createObjectURL(values.image as File)" :alt="values.alt" />
|
<img
|
||||||
|
class="h-full object-cover"
|
||||||
|
:src="createObjectURL(values.image as File)"
|
||||||
|
:alt="values.alt"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-zinc-400">
|
<div class="bg-zinc-400">
|
||||||
<img class="h-full object-cover" :src="createObjectURL(values.image as File)" :alt="values.alt" />
|
<img
|
||||||
|
class="h-full object-cover"
|
||||||
|
:src="createObjectURL(values.image as File)"
|
||||||
|
:alt="values.alt"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-foreground">
|
<div class="bg-foreground">
|
||||||
<img class="h-full object-cover" :src="createObjectURL(values.image as File)" :alt="values.alt" />
|
<img
|
||||||
|
class="h-full object-cover"
|
||||||
|
:src="createObjectURL(values.image as File)"
|
||||||
|
:alt="values.alt"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -32,13 +51,19 @@
|
||||||
{{ m.active_direct_bear_compose() }}
|
{{ m.active_direct_bear_compose() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input type="file" accept="image/*" @change="(e: any) => {
|
<Input
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
@change="(e: any) => {
|
||||||
handleChange(e);
|
handleChange(e);
|
||||||
|
|
||||||
if (!values.shortcode) {
|
if (!values.shortcode) {
|
||||||
setFieldValue('shortcode', e.target.files[0].name.replace(/\.[^/.]+$/, ''));
|
setFieldValue('shortcode', e.target.files[0].name.replace(/\.[^/.]+$/, ''));
|
||||||
}
|
}
|
||||||
}" @blur="handleBlur" :disabled="isSubmitting" />
|
}"
|
||||||
|
@blur="handleBlur"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
{{ m.lime_late_millipede_urge() }}
|
{{ m.lime_late_millipede_urge() }}
|
||||||
|
|
@ -53,7 +78,10 @@
|
||||||
{{ m.happy_mild_fox_gleam() }}
|
{{ m.happy_mild_fox_gleam() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input v-bind="componentField" :disabled="isSubmitting" />
|
<Input
|
||||||
|
v-bind="componentField"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
{{ m.glad_day_kestrel_amaze() }}
|
{{ m.glad_day_kestrel_amaze() }}
|
||||||
|
|
@ -68,7 +96,10 @@
|
||||||
{{ m.short_cute_jackdaw_comfort() }}
|
{{ m.short_cute_jackdaw_comfort() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input v-bind="componentField" :disabled="isSubmitting" />
|
<Input
|
||||||
|
v-bind="componentField"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
@ -80,7 +111,11 @@
|
||||||
{{ m.watery_left_shrimp_bless() }}
|
{{ m.watery_left_shrimp_bless() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea rows="2" v-bind="componentField" :disabled="isSubmitting" />
|
<Textarea
|
||||||
|
rows="2"
|
||||||
|
v-bind="componentField"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
{{ m.weird_fun_jurgen_arise() }}
|
{{ m.weird_fun_jurgen_arise() }}
|
||||||
|
|
@ -89,9 +124,15 @@
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ componentField, value, handleChange }" v-if="hasEmojiAdmin" name="global"
|
<FormField
|
||||||
:as="Card">
|
v-slot="{ componentField, value, handleChange }"
|
||||||
<FormItem class="grid grid-cols-[1fr,auto] items-center p-6 gap-2">
|
v-if="hasEmojiAdmin"
|
||||||
|
name="global"
|
||||||
|
:as="Card"
|
||||||
|
>
|
||||||
|
<FormItem
|
||||||
|
class="grid grid-cols-[1fr,auto] items-center p-6 gap-2"
|
||||||
|
>
|
||||||
<CardHeader class="space-y-0.5 p-0">
|
<CardHeader class="space-y-0.5 p-0">
|
||||||
<FormLabel :as="CardTitle">
|
<FormLabel :as="CardTitle">
|
||||||
{{ m.pink_sharp_carp_work() }}
|
{{ m.pink_sharp_carp_work() }}
|
||||||
|
|
@ -101,7 +142,12 @@
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Switch :checked="value" @update:checked="handleChange" v-bind="componentField" :disabled="isSubmitting" />
|
<Switch
|
||||||
|
:checked="value"
|
||||||
|
@update:checked="handleChange"
|
||||||
|
v-bind="componentField"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
@ -113,7 +159,11 @@
|
||||||
{{ m.soft_bold_ant_attend() }}
|
{{ m.soft_bold_ant_attend() }}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
<Button type="submit" variant="default" :disabled="isSubmitting">
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="default"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
>
|
||||||
{{ m.flat_safe_haddock_gaze() }}
|
{{ m.flat_safe_haddock_gaze() }}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|
@ -129,7 +179,12 @@ import { useForm } from "vee-validate";
|
||||||
import { toast } from "vue-sonner";
|
import { toast } from "vue-sonner";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import { Card, CardTitle } from "~/components/ui/card";
|
import {
|
||||||
|
Card,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "~/components/ui/card";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogClose,
|
DialogClose,
|
||||||
|
|
@ -147,6 +202,9 @@ import {
|
||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "~/components/ui/form";
|
} from "~/components/ui/form";
|
||||||
|
import { Input } from "~/components/ui/input";
|
||||||
|
import { Switch } from "~/components/ui/switch";
|
||||||
|
import { Textarea } from "~/components/ui/textarea";
|
||||||
import * as m from "~/paraglide/messages.js";
|
import * as m from "~/paraglide/messages.js";
|
||||||
|
|
||||||
const open = ref(false);
|
const open = ref(false);
|
||||||
|
|
@ -169,7 +227,7 @@ const formSchema = toTypedSchema(
|
||||||
count:
|
count:
|
||||||
identity.value?.instance.configuration.emojis
|
identity.value?.instance.configuration.emojis
|
||||||
.emoji_size_limit ?? Number.POSITIVE_INFINITY,
|
.emoji_size_limit ?? Number.POSITIVE_INFINITY,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
shortcode: z
|
shortcode: z
|
||||||
.string()
|
.string()
|
||||||
|
|
@ -182,7 +240,7 @@ const formSchema = toTypedSchema(
|
||||||
identity.value?.instance.configuration.emojis
|
identity.value?.instance.configuration.emojis
|
||||||
.max_emoji_shortcode_characters ??
|
.max_emoji_shortcode_characters ??
|
||||||
Number.POSITIVE_INFINITY,
|
Number.POSITIVE_INFINITY,
|
||||||
}),
|
})
|
||||||
)
|
)
|
||||||
.regex(emojiValidator),
|
.regex(emojiValidator),
|
||||||
global: z.boolean().default(false),
|
global: z.boolean().default(false),
|
||||||
|
|
@ -192,7 +250,7 @@ const formSchema = toTypedSchema(
|
||||||
64,
|
64,
|
||||||
m.home_cool_orangutan_hug({
|
m.home_cool_orangutan_hug({
|
||||||
count: 64,
|
count: 64,
|
||||||
}),
|
})
|
||||||
)
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
alt: z
|
alt: z
|
||||||
|
|
@ -206,10 +264,10 @@ const formSchema = toTypedSchema(
|
||||||
identity.value?.instance.configuration.emojis
|
identity.value?.instance.configuration.emojis
|
||||||
.max_emoji_description_characters ??
|
.max_emoji_description_characters ??
|
||||||
Number.POSITIVE_INFINITY,
|
Number.POSITIVE_INFINITY,
|
||||||
}),
|
})
|
||||||
)
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
const { isSubmitting, handleSubmit, values, setFieldValue } = useForm({
|
const { isSubmitting, handleSubmit, values, setFieldValue } = useForm({
|
||||||
validationSchema: formSchema,
|
validationSchema: formSchema,
|
||||||
|
|
@ -230,7 +288,7 @@ const submit = handleSubmit(async (values) => {
|
||||||
alt: values.alt,
|
alt: values.alt,
|
||||||
category: values.category,
|
category: values.category,
|
||||||
global: values.global,
|
global: values.global,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,12 @@
|
||||||
{{ m.bright_late_osprey_renew() }}
|
{{ m.bright_late_osprey_renew() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input type="file" accept="image/*" @change="handleChange" @blur="handleBlur" />
|
<Input
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
@change="handleChange"
|
||||||
|
@blur="handleBlur"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
{{ m.great_level_lamb_sway() }}
|
{{ m.great_level_lamb_sway() }}
|
||||||
|
|
@ -22,7 +27,11 @@
|
||||||
{{ m.safe_icy_bulldog_quell() }}
|
{{ m.safe_icy_bulldog_quell() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<ImageUploader v-model:image="identity.account.avatar" @submit-file="file => setValue(file)" @submit-url="url => setValue(url)" />
|
<ImageUploader
|
||||||
|
v-model:image="identity.account.avatar"
|
||||||
|
@submit-file="(file) => setValue(file)"
|
||||||
|
@submit-url="(url) => setValue(url)"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
@ -80,21 +89,61 @@
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div class="grid gap-4">
|
<div class="grid gap-4">
|
||||||
<div v-for="(field, index) in value" :key="index"
|
<div
|
||||||
class="grid items-center grid-cols-[auto,repeat(3,minmax(0,1fr))] gap-2">
|
v-for="(field, index) in value"
|
||||||
<Button variant="destructive" size="icon"
|
:key="index"
|
||||||
@click="handleChange([...value.slice(0, index), ...value.slice(index + 1)])">
|
class="grid items-center grid-cols-[auto,repeat(3,minmax(0,1fr))] gap-2"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
size="icon"
|
||||||
|
@click="
|
||||||
|
handleChange([
|
||||||
|
...value.slice(0, index),
|
||||||
|
...value.slice(index + 1),
|
||||||
|
])
|
||||||
|
"
|
||||||
|
>
|
||||||
<Trash />
|
<Trash />
|
||||||
</Button>
|
</Button>
|
||||||
<Input v-model="field.name" placeholder="Name" @update:model-value="e => {
|
<Input
|
||||||
handleChange([...value.slice(0, index), { name: e, value: field.value }, ...value.slice(index + 1)]);
|
v-model="field.name"
|
||||||
}" />
|
placeholder="Name"
|
||||||
<Input v-model="field.value" placeholder="Value" class="col-span-2" @update:model-value="e => {
|
@update:model-value="
|
||||||
handleChange([...value.slice(0, index), { name: field.name, value: e }, ...value.slice(index + 1)]);
|
(e) => {
|
||||||
}" />
|
handleChange([
|
||||||
|
...value.slice(0, index),
|
||||||
|
{ name: e, value: field.value },
|
||||||
|
...value.slice(index + 1),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
v-model="field.value"
|
||||||
|
placeholder="Value"
|
||||||
|
class="col-span-2"
|
||||||
|
@update:model-value="
|
||||||
|
(e) => {
|
||||||
|
handleChange([
|
||||||
|
...value.slice(0, index),
|
||||||
|
{ name: field.name, value: e },
|
||||||
|
...value.slice(index + 1),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button type="button" variant="secondary"
|
<Button
|
||||||
@click="handleChange([...value, { name: '', value: '' }])">
|
type="button"
|
||||||
|
variant="secondary"
|
||||||
|
@click="
|
||||||
|
handleChange([
|
||||||
|
...value,
|
||||||
|
{ name: '', value: '' },
|
||||||
|
])
|
||||||
|
"
|
||||||
|
>
|
||||||
{{ m.front_north_eel_gulp() }}
|
{{ m.front_north_eel_gulp() }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -103,8 +152,14 @@
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ componentField, value, handleChange }" name="bot" :as="Card">
|
<FormField
|
||||||
<FormItem class="grid grid-cols-[1fr,auto] items-center p-6 gap-2">
|
v-slot="{ componentField, value, handleChange }"
|
||||||
|
name="bot"
|
||||||
|
:as="Card"
|
||||||
|
>
|
||||||
|
<FormItem
|
||||||
|
class="grid grid-cols-[1fr,auto] items-center p-6 gap-2"
|
||||||
|
>
|
||||||
<CardHeader class="space-y-0.5 p-0">
|
<CardHeader class="space-y-0.5 p-0">
|
||||||
<FormLabel :as="CardTitle">
|
<FormLabel :as="CardTitle">
|
||||||
{{ m.gaudy_each_opossum_play() }}
|
{{ m.gaudy_each_opossum_play() }}
|
||||||
|
|
@ -114,14 +169,24 @@
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Switch :checked="value" @update:checked="handleChange" v-bind="componentField" />
|
<Switch
|
||||||
|
:checked="value"
|
||||||
|
@update:checked="handleChange"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ componentField, value, handleChange }" name="locked" :as="Card">
|
<FormField
|
||||||
<FormItem class="grid grid-cols-[1fr,auto] items-center p-6 gap-2">
|
v-slot="{ componentField, value, handleChange }"
|
||||||
|
name="locked"
|
||||||
|
:as="Card"
|
||||||
|
>
|
||||||
|
<FormItem
|
||||||
|
class="grid grid-cols-[1fr,auto] items-center p-6 gap-2"
|
||||||
|
>
|
||||||
<CardHeader class="space-y-0.5 p-0">
|
<CardHeader class="space-y-0.5 p-0">
|
||||||
<FormLabel :as="CardTitle">
|
<FormLabel :as="CardTitle">
|
||||||
{{ m.dirty_moving_shark_emerge() }}
|
{{ m.dirty_moving_shark_emerge() }}
|
||||||
|
|
@ -131,14 +196,24 @@
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Switch :checked="value" @update:checked="handleChange" v-bind="componentField" />
|
<Switch
|
||||||
|
:checked="value"
|
||||||
|
@update:checked="handleChange"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ componentField, value, handleChange }" name="discoverable" :as="Card">
|
<FormField
|
||||||
<FormItem class="grid grid-cols-[1fr,auto] items-center p-6 gap-2">
|
v-slot="{ componentField, value, handleChange }"
|
||||||
|
name="discoverable"
|
||||||
|
:as="Card"
|
||||||
|
>
|
||||||
|
<FormItem
|
||||||
|
class="grid grid-cols-[1fr,auto] items-center p-6 gap-2"
|
||||||
|
>
|
||||||
<CardHeader class="space-y-0.5 p-0">
|
<CardHeader class="space-y-0.5 p-0">
|
||||||
<FormLabel :as="CardTitle">
|
<FormLabel :as="CardTitle">
|
||||||
{{ m.red_vivid_cuckoo_spark() }}
|
{{ m.red_vivid_cuckoo_spark() }}
|
||||||
|
|
@ -148,14 +223,21 @@
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Switch :checked="value" @update:checked="handleChange" v-bind="componentField" />
|
<Switch
|
||||||
|
:checked="value"
|
||||||
|
@update:checked="handleChange"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
</form>
|
</form>
|
||||||
</Card>
|
</Card>
|
||||||
<Profile :account="account" class="max-w-lg overflow-auto hidden xl:block" />
|
<Profile
|
||||||
|
:account="account"
|
||||||
|
class="max-w-lg overflow-auto hidden xl:block"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
@ -167,7 +249,12 @@ import { toast } from "vue-sonner";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import Profile from "~/components/profiles/profile.vue";
|
import Profile from "~/components/profiles/profile.vue";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import { Card, CardTitle } from "~/components/ui/card";
|
import {
|
||||||
|
Card,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "~/components/ui/card";
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormDescription,
|
FormDescription,
|
||||||
|
|
@ -200,7 +287,7 @@ const formSchema = toTypedSchema(
|
||||||
m.civil_icy_ant_mend({
|
m.civil_icy_ant_mend({
|
||||||
size: identity.value?.instance.configuration.accounts
|
size: identity.value?.instance.configuration.accounts
|
||||||
.header_size_limit,
|
.header_size_limit,
|
||||||
}),
|
})
|
||||||
)
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
avatar: z
|
avatar: z
|
||||||
|
|
@ -213,7 +300,7 @@ const formSchema = toTypedSchema(
|
||||||
m.zippy_caring_raven_edit({
|
m.zippy_caring_raven_edit({
|
||||||
size: identity.value?.instance.configuration.accounts
|
size: identity.value?.instance.configuration.accounts
|
||||||
.avatar_size_limit,
|
.avatar_size_limit,
|
||||||
}),
|
})
|
||||||
)
|
)
|
||||||
.or(z.string().url())
|
.or(z.string().url())
|
||||||
.optional(),
|
.optional(),
|
||||||
|
|
@ -221,26 +308,26 @@ const formSchema = toTypedSchema(
|
||||||
.string()
|
.string()
|
||||||
.max(
|
.max(
|
||||||
identity.value.instance.configuration.accounts
|
identity.value.instance.configuration.accounts
|
||||||
.max_displayname_characters,
|
.max_displayname_characters
|
||||||
),
|
),
|
||||||
username: z
|
username: z
|
||||||
.string()
|
.string()
|
||||||
.regex(/^[a-z0-9_-]+$/, m.still_upper_otter_dine())
|
.regex(/^[a-z0-9_-]+$/, m.still_upper_otter_dine())
|
||||||
.max(
|
.max(
|
||||||
identity.value.instance.configuration.accounts
|
identity.value.instance.configuration.accounts
|
||||||
.max_username_characters,
|
.max_username_characters
|
||||||
),
|
),
|
||||||
bio: z
|
bio: z
|
||||||
.string()
|
.string()
|
||||||
.max(
|
.max(
|
||||||
identity.value.instance.configuration.accounts
|
identity.value.instance.configuration.accounts
|
||||||
.max_note_characters,
|
.max_note_characters
|
||||||
),
|
),
|
||||||
bot: z.boolean(),
|
bot: z.boolean(),
|
||||||
locked: z.boolean(),
|
locked: z.boolean(),
|
||||||
discoverable: z.boolean(),
|
discoverable: z.boolean(),
|
||||||
fields: z.array(z.object({ name: z.string(), value: z.string() })),
|
fields: z.array(z.object({ name: z.string(), value: z.string() })),
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
|
|
@ -280,8 +367,8 @@ const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
// Can't compare two arrays directly in JS, so we need to check if all fields are the same
|
// Can't compare two arrays directly in JS, so we need to check if all fields are the same
|
||||||
fields_attributes: values.fields.every((field) =>
|
fields_attributes: values.fields.every((field) =>
|
||||||
account.value.source?.fields?.some(
|
account.value.source?.fields?.some(
|
||||||
(f) => f.name === field.name && f.value === field.value,
|
(f) => f.name === field.name && f.value === field.value
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
? undefined
|
? undefined
|
||||||
: values.fields,
|
: values.fields,
|
||||||
|
|
@ -300,8 +387,8 @@ const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
try {
|
try {
|
||||||
const { data } = await client.value.updateCredentials(
|
const { data } = await client.value.updateCredentials(
|
||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
Object.entries(changedData).filter(([, v]) => v !== undefined),
|
Object.entries(changedData).filter(([, v]) => v !== undefined)
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<Dialog v-model:open="open">
|
<Dialog v-model:open="open">
|
||||||
<DialogTrigger :as-child="true">
|
<DialogTrigger :as-child="true">
|
||||||
<Button v-bind="$attrs" variant="ghost" class="h-fit w-fit p-0 m-0 relative group border overflow-hidden">
|
<Button
|
||||||
|
v-bind="$attrs"
|
||||||
|
variant="ghost"
|
||||||
|
class="h-fit w-fit p-0 m-0 relative group border overflow-hidden"
|
||||||
|
>
|
||||||
<Avatar size="lg" :src="image" :name="displayName" />
|
<Avatar size="lg" :src="image" :name="displayName" />
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 bg-background/80 flex group-hover:opacity-100 opacity-0 duration-200 items-center justify-center">
|
class="absolute inset-0 bg-background/80 flex group-hover:opacity-100 opacity-0 duration-200 items-center justify-center"
|
||||||
|
>
|
||||||
<Upload />
|
<Upload />
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -17,7 +22,10 @@
|
||||||
{{ m.suave_broad_albatross_drop() }}
|
{{ m.suave_broad_albatross_drop() }}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
<form class="p-4 grid gap-6" @submit="submit">
|
<form class="p-4 grid gap-6" @submit="submit">
|
||||||
<Tabs default-value="upload" class="mt-2 data-[component=tabpanel]:*:mt-6">
|
<Tabs
|
||||||
|
default-value="upload"
|
||||||
|
class="mt-2 data-[component=tabpanel]:*:mt-6"
|
||||||
|
>
|
||||||
<TabsList class="w-full *:w-full">
|
<TabsList class="w-full *:w-full">
|
||||||
<TabsTrigger value="upload">
|
<TabsTrigger value="upload">
|
||||||
{{ m.flat_safe_haddock_gaze() }}
|
{{ m.flat_safe_haddock_gaze() }}
|
||||||
|
|
@ -30,14 +38,22 @@
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent value="upload">
|
<TabsContent value="upload">
|
||||||
<FormField v-slot="{ handleChange, handleBlur }" name="image">
|
<FormField
|
||||||
|
v-slot="{ handleChange, handleBlur }"
|
||||||
|
name="image"
|
||||||
|
>
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel class="sr-only">
|
<FormLabel class="sr-only">
|
||||||
{{ m.flat_safe_haddock_gaze() }}
|
{{ m.flat_safe_haddock_gaze() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input type="file" accept="image/*" @change="handleChange" @blur="handleBlur"
|
<Input
|
||||||
:disabled="isSubmitting" />
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
@change="handleChange"
|
||||||
|
@blur="handleBlur"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
{{ m.lime_late_millipede_urge() }}
|
{{ m.lime_late_millipede_urge() }}
|
||||||
|
|
@ -47,38 +63,57 @@
|
||||||
</FormField>
|
</FormField>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="gravatar">
|
<TabsContent value="gravatar">
|
||||||
<FormField v-slot="{ componentField, errors, value }" name="email" @update:model-value="async (value) => {
|
<FormField
|
||||||
gravatarUrl = await emailToGravatar(value)
|
v-slot="{ componentField, value }"
|
||||||
}">
|
name="email"
|
||||||
|
@update:model-value="
|
||||||
|
async (value) => {
|
||||||
|
gravatarUrl = await emailToGravatar(value);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{{ m.lower_formal_kudu_lift() }}
|
{{ m.lower_formal_kudu_lift() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input v-bind="componentField" :disabled="isSubmitting"
|
<Input
|
||||||
placeholder="peter.griffin@fox.com" />
|
v-bind="componentField"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
placeholder="peter.griffin@fox.com"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
<div v-if="value" class="grid gap-4 !mt-4">
|
<div v-if="value" class="grid gap-4 !mt-4">
|
||||||
<Label>{{ m.witty_honest_wallaby_support() }}</Label>
|
<Label>{{
|
||||||
|
m.witty_honest_wallaby_support()
|
||||||
|
}}</Label>
|
||||||
<Avatar size="lg" :src="gravatarUrl" />
|
<Avatar size="lg" :src="gravatarUrl" />
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="url">
|
<TabsContent value="url">
|
||||||
<FormField v-slot="{ componentField, errors, value }" name="url">
|
<FormField
|
||||||
|
v-slot="{ componentField, value }"
|
||||||
|
name="url"
|
||||||
|
>
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{{ m.proud_next_elk_beam() }}
|
{{ m.proud_next_elk_beam() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input v-bind="componentField" :disabled="isSubmitting"
|
<Input
|
||||||
placeholder="https://mysite.com/avatar.webp" />
|
v-bind="componentField"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
placeholder="https://mysite.com/avatar.webp"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
<div v-if="value" class="grid gap-4 !mt-4">
|
<div v-if="value" class="grid gap-4 !mt-4">
|
||||||
<Label>{{ m.witty_honest_wallaby_support() }}</Label>
|
<Label>{{
|
||||||
|
m.witty_honest_wallaby_support()
|
||||||
|
}}</Label>
|
||||||
<Avatar size="lg" :src="value" />
|
<Avatar size="lg" :src="value" />
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
@ -91,7 +126,11 @@
|
||||||
{{ m.soft_bold_ant_attend() }}
|
{{ m.soft_bold_ant_attend() }}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
<Button type="submit" variant="default" :disabled="isSubmitting">
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="default"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
>
|
||||||
{{ m.teary_antsy_panda_aid() }}
|
{{ m.teary_antsy_panda_aid() }}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|
@ -106,6 +145,7 @@ import { Upload } from "lucide-vue-next";
|
||||||
import { useForm } from "vee-validate";
|
import { useForm } from "vee-validate";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import Avatar from "~/components/profiles/avatar.vue";
|
import Avatar from "~/components/profiles/avatar.vue";
|
||||||
|
import { Button } from "~/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogClose,
|
DialogClose,
|
||||||
|
|
@ -113,6 +153,7 @@ import {
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
} from "~/components/ui/dialog";
|
} from "~/components/ui/dialog";
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
|
|
@ -123,6 +164,7 @@ import {
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "~/components/ui/form";
|
} from "~/components/ui/form";
|
||||||
import { Input } from "~/components/ui/input";
|
import { Input } from "~/components/ui/input";
|
||||||
|
import { Label } from "~/components/ui/label";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
|
||||||
import * as m from "~/paraglide/messages.js";
|
import * as m from "~/paraglide/messages.js";
|
||||||
|
|
||||||
|
|
@ -151,25 +193,25 @@ const schema = toTypedSchema(
|
||||||
(v) => v.size <= (maxSize ?? Number.MAX_SAFE_INTEGER),
|
(v) => v.size <= (maxSize ?? Number.MAX_SAFE_INTEGER),
|
||||||
m.zippy_caring_raven_edit({
|
m.zippy_caring_raven_edit({
|
||||||
size: maxSize ?? Number.MAX_SAFE_INTEGER,
|
size: maxSize ?? Number.MAX_SAFE_INTEGER,
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
.or(
|
.or(
|
||||||
z.object({
|
z.object({
|
||||||
url: z.string().url(),
|
url: z.string().url(),
|
||||||
}),
|
})
|
||||||
)
|
)
|
||||||
.or(
|
.or(
|
||||||
z.object({
|
z.object({
|
||||||
email: z.string().email(),
|
email: z.string().email(),
|
||||||
}),
|
})
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const emailToGravatar = async (email: string) => {
|
const emailToGravatar = async (email: string) => {
|
||||||
const sha256 = await crypto.subtle.digest(
|
const sha256 = await crypto.subtle.digest(
|
||||||
"SHA-256",
|
"SHA-256",
|
||||||
new TextEncoder().encode(email),
|
new TextEncoder().encode(email)
|
||||||
);
|
);
|
||||||
|
|
||||||
return `https://www.gravatar.com/avatar/${Array.from(new Uint8Array(sha256))
|
return `https://www.gravatar.com/avatar/${Array.from(new Uint8Array(sha256))
|
||||||
|
|
@ -192,7 +234,7 @@ const submit = handleSubmit(async (values) => {
|
||||||
} else if ((values as { email: string }).email) {
|
} else if ((values as { email: string }).email) {
|
||||||
emit(
|
emit(
|
||||||
"submitUrl",
|
"submitUrl",
|
||||||
await emailToGravatar((values as { email: string }).email),
|
await emailToGravatar((values as { email: string }).email)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
34
package.json
34
package.json
|
|
@ -30,7 +30,7 @@
|
||||||
"check": "bunx tsc -p ."
|
"check": "bunx tsc -p ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/fonts": "^0.10.3",
|
"@nuxt/fonts": "^0.11.0",
|
||||||
"@nuxtjs/color-mode": "3.5.2",
|
"@nuxtjs/color-mode": "3.5.2",
|
||||||
"@tailwindcss/typography": "^0.5.16",
|
"@tailwindcss/typography": "^0.5.16",
|
||||||
"@tiptap/extension-highlight": "^2.11.5",
|
"@tiptap/extension-highlight": "^2.11.5",
|
||||||
|
|
@ -50,25 +50,25 @@
|
||||||
"@vee-validate/zod": "^4.15.0",
|
"@vee-validate/zod": "^4.15.0",
|
||||||
"@versia/client": "0.1.5",
|
"@versia/client": "0.1.5",
|
||||||
"@videojs-player/vue": "^1.0.0",
|
"@videojs-player/vue": "^1.0.0",
|
||||||
"@vite-pwa/nuxt": "^0.10.6",
|
"@vite-pwa/nuxt": "^0.10.8",
|
||||||
"@vueuse/core": "^12.6.1",
|
"@vueuse/core": "^13.0.0",
|
||||||
"@vueuse/nuxt": "^12.6.1",
|
"@vueuse/nuxt": "^13.0.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"embla-carousel-vue": "^8.5.2",
|
"embla-carousel-vue": "^8.5.2",
|
||||||
"fuzzysort": "^3.1.0",
|
"fuzzysort": "^3.1.0",
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
"lucide-vue-next": "^0.475.0",
|
"lucide-vue-next": "^0.484.0",
|
||||||
"magic-regexp": "^0.8.0",
|
"magic-regexp": "^0.8.0",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"nanoid": "^5.0.9",
|
"nanoid": "^5.1.5",
|
||||||
"nuxt": "^3.15.4",
|
"nuxt": "^3.16.1",
|
||||||
"nuxt-security": "^2.1.5",
|
"nuxt-security": "^2.2.0",
|
||||||
"radix-vue": "^1.9.14",
|
"radix-vue": "^1.9.17",
|
||||||
"shadcn-nuxt": "0.11.3",
|
"shadcn-nuxt": "1.0.3",
|
||||||
"tailwind-merge": "^3.0.1",
|
"tailwind-merge": "^3.0.2",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"vaul-vue": "^0.2.1",
|
"vaul-vue": "^0.4.1",
|
||||||
"vee-validate": "^4.15.0",
|
"vee-validate": "^4.15.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.0",
|
"vue-router": "^4.5.0",
|
||||||
|
|
@ -79,15 +79,15 @@
|
||||||
"@biomejs/biome": "^1.9.4",
|
"@biomejs/biome": "^1.9.4",
|
||||||
"@iconify-json/fluent-emoji": "^1.2.3",
|
"@iconify-json/fluent-emoji": "^1.2.3",
|
||||||
"@iconify-json/fluent-emoji-flat": "^1.2.3",
|
"@iconify-json/fluent-emoji-flat": "^1.2.3",
|
||||||
"@iconify-json/noto": "^1.2.2",
|
"@iconify-json/noto": "^1.2.3",
|
||||||
"@iconify-json/twemoji": "^1.2.2",
|
"@iconify-json/twemoji": "^1.2.2",
|
||||||
"@iconify/utils": "^2.3.0",
|
"@iconify/utils": "^2.3.0",
|
||||||
"@inlang/paraglide-js": "2.0.0-beta.17",
|
"@inlang/paraglide-js": "2.0.6",
|
||||||
"@nuxtjs/tailwindcss": "^6.13.1",
|
"@nuxtjs/tailwindcss": "^6.13.2",
|
||||||
"@tailwindcss/forms": "^0.5.10",
|
"@tailwindcss/forms": "^0.5.10",
|
||||||
"@types/html-to-text": "^9.0.4",
|
"@types/html-to-text": "^9.0.4",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.8.2",
|
||||||
"vue-tsc": "^2.2.0"
|
"vue-tsc": "^2.2.8"
|
||||||
},
|
},
|
||||||
"trustedDependencies": [
|
"trustedDependencies": [
|
||||||
"@biomejs/biome",
|
"@biomejs/biome",
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="h-svh flex items-center justify-center px-4 bg-center bg-no-repeat bg-cover" :style="{
|
<div
|
||||||
backgroundImage: 'url(/images/banner.webp)'
|
class="h-svh flex items-center justify-center px-4 bg-center bg-no-repeat bg-cover"
|
||||||
}">
|
:style="{
|
||||||
|
backgroundImage: 'url(/images/banner.webp)',
|
||||||
|
}"
|
||||||
|
>
|
||||||
<Card class="w-full max-w-md">
|
<Card class="w-full max-w-md">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>{{ m.aware_awful_crow_spur() }}</CardTitle>
|
<CardTitle>{{ m.aware_awful_crow_spur() }}</CardTitle>
|
||||||
<CardDescription>{{ m.mushy_soft_lizard_propel() }}<br />{{ m.short_arable_leopard_zap() }}
|
<CardDescription
|
||||||
|
>{{ m.mushy_soft_lizard_propel() }}<br />{{
|
||||||
|
m.short_arable_leopard_zap()
|
||||||
|
}}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent class="grid">
|
<CardContent class="grid">
|
||||||
<pre class="rounded bg-muted px-4 py-2 border text-center w-full font-mono text-sm font-semibold select-all">{{ code }}</pre>
|
<pre
|
||||||
|
class="rounded bg-muted px-4 py-2 border text-center w-full font-mono text-sm font-semibold select-all"
|
||||||
|
>{{ code }}</pre
|
||||||
|
>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,83 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex h-svh items-center justify-center px-6 py-12 lg:px-8 bg-center bg-no-repeat bg-cover" :style="{
|
<div
|
||||||
backgroundImage: 'url(/images/banner.webp)'
|
class="flex h-svh items-center justify-center px-6 py-12 lg:px-8 bg-center bg-no-repeat bg-cover"
|
||||||
}">
|
:style="{
|
||||||
<Card class="w-full max-w-md" as="form" method="POST" :action="url.pathname.replace('/oauth/consent', '/oauth/authorize')">
|
backgroundImage: 'url(/images/banner.webp)',
|
||||||
<input type="hidden" v-for="([key, value]) in url.searchParams" :key="key" :name="key" :value="value" />
|
}"
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
class="w-full max-w-md"
|
||||||
|
as="form"
|
||||||
|
method="POST"
|
||||||
|
:action="url.pathname.replace('/oauth/consent', '/oauth/authorize')"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
v-for="[key, value] in url.searchParams"
|
||||||
|
:key="key"
|
||||||
|
:name="key"
|
||||||
|
:value="value"
|
||||||
|
/>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle as="h1" class="text-2xl break-words">{{ m.fresh_broad_cockroach_radiate({
|
<CardTitle as="h1" class="text-2xl break-words">{{
|
||||||
application: application ?? "",
|
m.fresh_broad_cockroach_radiate({
|
||||||
}) }}</CardTitle>
|
application: application ?? "",
|
||||||
|
})
|
||||||
|
}}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent class="flex flex-col px-4 py-2">
|
<CardContent class="flex flex-col px-4 py-2">
|
||||||
<CardTitle as="h2" class="text-lg">{{ application }}</CardTitle>
|
<CardTitle as="h2" class="text-lg">{{
|
||||||
<a v-if="website" :href="website" target="_blank" rel="noopener noreferrer" class="underline">{{ website }}</a>
|
application
|
||||||
|
}}</CardTitle>
|
||||||
|
<a
|
||||||
|
v-if="website"
|
||||||
|
:href="website"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="underline"
|
||||||
|
>{{ website }}</a
|
||||||
|
>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<ul class="list-none my-6 [&>li]:mt-2">
|
<ul class="list-none my-6 [&>li]:mt-2">
|
||||||
<li v-for="text in getScopeText(scopes)" :key="text[1]" class="flex flex-row gap-1 items-center">
|
<li
|
||||||
|
v-for="text in getScopeText(scopes)"
|
||||||
|
:key="text[1]"
|
||||||
|
class="flex flex-row gap-1 items-center"
|
||||||
|
>
|
||||||
<Check class="size-4" />
|
<Check class="size-4" />
|
||||||
<h2 class="text-sm">
|
<h2 class="text-sm">
|
||||||
<strong class="font-bold">{{ text[0] }}</strong> {{ text[1] }}
|
<strong class="font-bold">{{ text[0] }}</strong>
|
||||||
|
{{ text[1] }}
|
||||||
</h2>
|
</h2>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="flex-col flex gap-y-1 text-sm">
|
<div class="flex-col flex gap-y-1 text-sm">
|
||||||
<p v-html="m.gross_antsy_kangaroo_succeed({
|
<p
|
||||||
application: application ?? '',
|
v-html="
|
||||||
})"></p>
|
m.gross_antsy_kangaroo_succeed({
|
||||||
<p v-html="m.hour_close_giraffe_mop({
|
application: application ?? '',
|
||||||
application: application ?? '',
|
})
|
||||||
})"></p>
|
"
|
||||||
|
></p>
|
||||||
|
<p
|
||||||
|
v-html="
|
||||||
|
m.hour_close_giraffe_mop({
|
||||||
|
application: application ?? '',
|
||||||
|
})
|
||||||
|
"
|
||||||
|
></p>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter class="grid gap-2">
|
<CardFooter class="grid gap-2">
|
||||||
<Button variant="default" type="submit">{{ m.last_spare_polecat_reside() }}</Button>
|
<Button variant="default" type="submit">{{
|
||||||
<Button :as="NuxtLink" href="/" variant="secondary">{{ m.soft_bold_ant_attend() }}</Button>
|
m.last_spare_polecat_reside()
|
||||||
|
}}</Button>
|
||||||
|
<Button :as="NuxtLink" href="/" variant="secondary">{{
|
||||||
|
m.soft_bold_ant_attend()
|
||||||
|
}}</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -44,7 +86,13 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Check } from "lucide-vue-next";
|
import { Check } from "lucide-vue-next";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "~/components/ui/card";
|
||||||
import * as m from "~/paraglide/messages.js";
|
import * as m from "~/paraglide/messages.js";
|
||||||
import { NuxtLink } from "#components";
|
import { NuxtLink } from "#components";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex h-svh items-center justify-center px-6 py-12 lg:px-8 bg-center bg-no-repeat bg-cover" :style="{
|
<div
|
||||||
backgroundImage: 'url(/images/banner.webp)'
|
class="flex h-svh items-center justify-center px-6 py-12 lg:px-8 bg-center bg-no-repeat bg-cover"
|
||||||
}">
|
:style="{
|
||||||
|
backgroundImage: 'url(/images/banner.webp)',
|
||||||
|
}"
|
||||||
|
>
|
||||||
<Card v-if="params.success" class="w-full max-w-md">
|
<Card v-if="params.success" class="w-full max-w-md">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>{{ m.late_mean_capybara_fade() }}</CardTitle>
|
<CardTitle>{{ m.late_mean_capybara_fade() }}</CardTitle>
|
||||||
|
|
@ -16,23 +19,39 @@
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
<Card v-else class="w-full max-w-md">
|
<Card v-else class="w-full max-w-md">
|
||||||
<form method="POST" action="/api/auth/reset" @submit="form.submitForm">
|
<form
|
||||||
|
method="POST"
|
||||||
|
action="/api/auth/reset"
|
||||||
|
@submit="form.submitForm"
|
||||||
|
>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<Alert v-if="params.login_reset" variant="default" class="mb-4">
|
<Alert
|
||||||
|
v-if="params.login_reset"
|
||||||
|
variant="default"
|
||||||
|
class="mb-4"
|
||||||
|
>
|
||||||
<AlertCircle class="size-4" />
|
<AlertCircle class="size-4" />
|
||||||
<AlertTitle>{{ m.east_loud_lobster_explore() }}</AlertTitle>
|
<AlertTitle>{{
|
||||||
|
m.east_loud_lobster_explore()
|
||||||
|
}}</AlertTitle>
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
{{ m.good_plane_gazelle_glow() }}
|
{{ m.good_plane_gazelle_glow() }}
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
<Alert v-if="params.error" variant="destructive" class="mb-4">
|
<Alert
|
||||||
|
v-if="params.error"
|
||||||
|
variant="destructive"
|
||||||
|
class="mb-4"
|
||||||
|
>
|
||||||
<AlertCircle class="size-4" />
|
<AlertCircle class="size-4" />
|
||||||
<AlertTitle>{{ params.error }}</AlertTitle>
|
<AlertTitle>{{ params.error }}</AlertTitle>
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
{{ params.error_description }}
|
{{ params.error_description }}
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
<CardTitle as="h1">{{ m.tired_green_sloth_evoke() }}</CardTitle>
|
<CardTitle as="h1">{{
|
||||||
|
m.tired_green_sloth_evoke()
|
||||||
|
}}</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
{{ m.solid_slow_platypus_talk() }}
|
{{ m.solid_slow_platypus_talk() }}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
|
|
@ -51,27 +70,42 @@
|
||||||
{{ m.true_male_gadfly_stab() }}
|
{{ m.true_male_gadfly_stab() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="hunter2" type="password" auto-capitalize="none" auto-correct="off"
|
<Input
|
||||||
v-bind="componentField" />
|
placeholder="hunter2"
|
||||||
|
type="password"
|
||||||
|
auto-capitalize="none"
|
||||||
|
auto-correct="off"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
<FormField v-slot="{ componentField }" name="password-confirm">
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="password-confirm"
|
||||||
|
>
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{{ m.awful_cozy_jannes_rise() }}
|
{{ m.awful_cozy_jannes_rise() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="hunter2" type="password" auto-capitalize="none" auto-correct="off"
|
<Input
|
||||||
v-bind="componentField" />
|
placeholder="hunter2"
|
||||||
|
type="password"
|
||||||
|
auto-capitalize="none"
|
||||||
|
auto-correct="off"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter class="grid gap-2">
|
<CardFooter class="grid gap-2">
|
||||||
<Button variant="default" type="submit">{{ m.noisy_round_skate_yell() }}</Button>
|
<Button variant="default" type="submit">{{
|
||||||
|
m.noisy_round_skate_yell()
|
||||||
|
}}</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</form>
|
</form>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -88,6 +122,7 @@ import { Button } from "~/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
CardFooter,
|
CardFooter,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
|
|
@ -147,7 +182,7 @@ const formSchema = toTypedSchema(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const params = useUrlSearchParams();
|
const params = useUrlSearchParams();
|
||||||
|
|
@ -158,4 +193,4 @@ const form = useForm({
|
||||||
token: (params.token as string) ?? undefined,
|
token: (params.token as string) ?? undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,28 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="md:px-8 px-4 py-2 max-w-7xl mx-auto w-full space-y-6">
|
<div class="md:px-8 px-4 py-2 max-w-7xl mx-auto w-full space-y-6">
|
||||||
<div :class="cn('grid gap-2', canUpload && 'grid-cols-[1fr,auto]')">
|
<div :class="cn('grid gap-2', canUpload && 'grid-cols-[1fr,auto]')">
|
||||||
<h1 class="scroll-m-20 text-3xl font-extrabold tracking-tight lg:text-4xl capitalize">
|
<h1
|
||||||
|
class="scroll-m-20 text-3xl font-extrabold tracking-tight lg:text-4xl capitalize"
|
||||||
|
>
|
||||||
{{ m.suave_smart_mantis_climb() }}
|
{{ m.suave_smart_mantis_climb() }}
|
||||||
</h1>
|
</h1>
|
||||||
<Uploader v-if="canUpload">
|
<Uploader v-if="canUpload">
|
||||||
<Button variant="default">
|
<Button variant="default"> <Upload /> Upload </Button>
|
||||||
<Upload /> Upload
|
|
||||||
</Button>
|
|
||||||
</Uploader>
|
</Uploader>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="emojis.length > 0" class="max-w-sm w-full relative">
|
<div v-if="emojis.length > 0" class="max-w-sm w-full relative">
|
||||||
<Input v-model="search" placeholder="Search" class="pl-8" />
|
<Input v-model="search" placeholder="Search" class="pl-8" />
|
||||||
<Search class="absolute size-4 top-1/2 left-2.5 transform -translate-y-1/2" />
|
<Search
|
||||||
|
class="absolute size-4 top-1/2 left-2.5 transform -translate-y-1/2"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Category v-if="emojis.length > 0" v-for="([name, emojis]) in categories" :key="name" :emojis="emojis"
|
<Category
|
||||||
:name="name" />
|
v-if="emojis.length > 0"
|
||||||
|
v-for="[name, emojis] in categories"
|
||||||
|
:key="name"
|
||||||
|
:emojis="emojis"
|
||||||
|
:name="name"
|
||||||
|
/>
|
||||||
<Card v-else class="shadow-none bg-transparent border-none p-4">
|
<Card v-else class="shadow-none bg-transparent border-none p-4">
|
||||||
<CardHeader class="text-center gap-y-4">
|
<CardHeader class="text-center gap-y-4">
|
||||||
<CardTitle>{{ m.actual_steep_llama_rest() }}</CardTitle>
|
<CardTitle>{{ m.actual_steep_llama_rest() }}</CardTitle>
|
||||||
|
|
@ -33,6 +40,7 @@ import { type Emoji, RolePermission } from "@versia/client/types";
|
||||||
import { Search, Upload } from "lucide-vue-next";
|
import { Search, Upload } from "lucide-vue-next";
|
||||||
import Category from "~/components/preferences/emojis/category.vue";
|
import Category from "~/components/preferences/emojis/category.vue";
|
||||||
import Uploader from "~/components/preferences/emojis/uploader.vue";
|
import Uploader from "~/components/preferences/emojis/uploader.vue";
|
||||||
|
import { Button } from "~/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
|
|
@ -60,14 +68,14 @@ const permissions = usePermissions();
|
||||||
const canUpload = computed(
|
const canUpload = computed(
|
||||||
() =>
|
() =>
|
||||||
permissions.value.includes(RolePermission.ManageOwnEmojis) ||
|
permissions.value.includes(RolePermission.ManageOwnEmojis) ||
|
||||||
permissions.value.includes(RolePermission.ManageEmojis),
|
permissions.value.includes(RolePermission.ManageEmojis)
|
||||||
);
|
);
|
||||||
|
|
||||||
const emojis = computed(
|
const emojis = computed(
|
||||||
() =>
|
() =>
|
||||||
identity.value?.emojis?.filter((emoji) =>
|
identity.value?.emojis?.filter((emoji) =>
|
||||||
emoji.shortcode.toLowerCase().includes(search.value.toLowerCase()),
|
emoji.shortcode.toLowerCase().includes(search.value.toLowerCase())
|
||||||
) ?? [],
|
) ?? []
|
||||||
);
|
);
|
||||||
|
|
||||||
const search = ref("");
|
const search = ref("");
|
||||||
|
|
@ -95,4 +103,4 @@ const categories = computed(() => {
|
||||||
}
|
}
|
||||||
return categories;
|
return categories;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue