chore: ⬆️ Upgrade to new @versia/client

This commit is contained in:
Jesse Wierzbinski 2025-05-26 11:19:15 +02:00
parent 0a157d06f6
commit f807b05784
No known key found for this signature in database
71 changed files with 451 additions and 492 deletions

View file

@ -138,7 +138,7 @@
<script lang="ts" setup>
import type { ResponseError } from "@versia/client";
import type { Status, StatusSource } from "@versia/client/types";
import type { Attachment, Status, StatusSource } from "@versia/client/schemas";
import {
AtSign,
FilePlus2,
@ -151,6 +151,7 @@ import {
TriangleAlert,
} from "lucide-vue-next";
import { toast } from "vue-sonner";
import type { z } from "zod";
import Note from "~/components/notes/note.vue";
import {
Select,
@ -182,8 +183,8 @@ watch([Control_Enter, Command_Enter], () => {
const { relation } = defineProps<{
relation?: {
type: "reply" | "quote" | "edit";
note: Status;
source?: StatusSource;
note: z.infer<typeof Status>;
source?: z.infer<typeof StatusSource>;
};
}>();
@ -279,7 +280,7 @@ const submit = async () => {
visibility: state.visibility,
});
useEvent("composer:send", data as Status);
useEvent("composer:send", data as z.infer<typeof Status>);
play("publish");
useEvent("composer:close");
}
@ -317,7 +318,9 @@ const uploadFiles = (files: File[]) => {
return;
}
state.files[index].apiId = media.data.id;
state.files[index].apiId = (
media.data as z.infer<typeof Attachment>
).id;
state.files[index].uploading = false;
})
.catch(() => {

View file

@ -5,8 +5,9 @@ import {
DialogDescription,
DialogTitle,
} from "@/components/ui/dialog";
import type { Status, StatusSource } from "@versia/client/types";
import type { Status, StatusSource } from "@versia/client/schemas";
import { toast } from "vue-sonner";
import type { z } from "zod";
import * as m from "~/paraglide/messages.js";
import Composer from "./composer.vue";
@ -58,8 +59,8 @@ const open = ref(false);
const relation = ref(
null as {
type: "reply" | "quote" | "edit";
note: Status;
source?: StatusSource;
note: z.infer<typeof Status>;
source?: z.infer<typeof StatusSource>;
} | null,
);
</script>

View file

@ -1,15 +1,15 @@
import { VueRenderer } from "@tiptap/vue-3";
import tippy, { type Instance } from "tippy.js";
import type { MentionNodeAttrs } from "@tiptap/extension-mention";
import type { SuggestionOptions } from "@tiptap/suggestion";
import type { Account } from "@versia/client/types";
import { VueRenderer } from "@tiptap/vue-3";
import type { Account } from "@versia/client/schemas";
import { go } from "fuzzysort";
import tippy, { type Instance } from "tippy.js";
import type { z } from "zod";
import MentionList from "./mentions-list.vue";
export type UserData = {
key: string;
value: Account;
value: z.infer<typeof Account>;
};
export default {

View file

@ -17,8 +17,10 @@
</template>
<script lang="ts" setup>
import type { Status } from "@versia/client/schemas";
import { Ellipsis, Heart, Quote, Repeat, Reply } from "lucide-vue-next";
import { toast } from "vue-sonner";
import type { z } from "zod";
import * as m from "~/paraglide/messages.js";
import { getLocale } from "~/paraglide/runtime";
import { confirmModalService } from "../modals/composable";
@ -33,7 +35,7 @@ const { noteId } = defineProps<{
noteId: string;
isRemote: boolean;
url: string;
remoteUrl: string;
remoteUrl?: string;
authorId: string;
liked: boolean;
reblogged: boolean;
@ -108,7 +110,10 @@ const reblog = async () => {
const { data } = await client.value.reblogStatus(noteId);
toast.dismiss(id);
toast.success(m.weird_moving_hawk_lift());
useEvent("note:edit", data.reblog || data);
useEvent(
"note:edit",
(data.reblog as z.infer<typeof Status> | null) || data,
);
};
const unreblog = async () => {

View file

@ -6,13 +6,14 @@
</template>
<script lang="ts" setup>
import type { Attachment } from "@versia/client/types";
import type { Attachment } from "@versia/client/schemas";
import type { z } from "zod";
import AudioAttachment from "./attachments/audio.vue";
import FileAttachment from "./attachments/file.vue";
import ImageAttachment from "./attachments/image.vue";
import VideoAttachment from "./attachments/video.vue";
defineProps<{
attachment: Attachment;
attachment: z.infer<typeof Attachment>;
}>();
</script>

View file

@ -6,10 +6,11 @@
</template>
<script lang="ts" setup>
import type { Attachment as AttachmentType } from "@versia/client/types";
import type { Attachment as AttachmentType } from "@versia/client/schemas";
import type { z } from "zod";
import Attachment from "./attachment.vue";
defineProps<{
attachments: AttachmentType[];
attachments: z.infer<typeof AttachmentType>[];
}>();
</script>

View file

@ -5,10 +5,11 @@
</template>
<script lang="ts" setup>
import type { Attachment } from "@versia/client/types";
import type { Attachment } from "@versia/client/schemas";
import type { z } from "zod";
import Base from "./base.vue";
const { attachment } = defineProps<{
attachment: Attachment;
attachment: z.infer<typeof Attachment>;
}>();
</script>

View file

@ -48,8 +48,9 @@
</template>
<script lang="ts" setup>
import type { Attachment } from "@versia/client/types";
import type { Attachment } from "@versia/client/schemas";
import { Captions, Download, File, X } from "lucide-vue-next";
import type { z } from "zod";
import { Button } from "~/components/ui/button";
import { Card } from "~/components/ui/card";
import {
@ -67,7 +68,7 @@ import {
} from "~/components/ui/popover";
const { attachment, lightbox = false } = defineProps<{
attachment: Attachment;
attachment: z.infer<typeof Attachment>;
lightbox?: boolean;
}>();
</script>

View file

@ -8,11 +8,12 @@
</template>
<script lang="ts" setup>
import type { Attachment } from "@versia/client/types";
import type { Attachment } from "@versia/client/schemas";
import { File } from "lucide-vue-next";
import type { z } from "zod";
import Base from "./base.vue";
const { attachment } = defineProps<{
attachment: Attachment;
attachment: z.infer<typeof Attachment>;
}>();
</script>

View file

@ -5,10 +5,11 @@
</template>
<script lang="ts" setup>
import type { Attachment } from "@versia/client/types";
import type { Attachment } from "@versia/client/schemas";
import type { z } from "zod";
import Base from "./base.vue";
const { attachment } = defineProps<{
attachment: Attachment;
attachment: z.infer<typeof Attachment>;
}>();
</script>

View file

@ -5,10 +5,11 @@
</template>
<script lang="ts" setup>
import type { Attachment } from "@versia/client/types";
import type { Attachment } from "@versia/client/schemas";
import type { z } from "zod";
import Base from "./base.vue";
const { attachment } = defineProps<{
attachment: Attachment;
attachment: z.infer<typeof Attachment>;
}>();
</script>

View file

@ -13,7 +13,8 @@
</template>
<script lang="ts" setup>
import type { Attachment, Emoji, Status } from "@versia/client/types";
import type { Attachment, CustomEmoji, Status } from "@versia/client/schemas";
import type { z } from "zod";
import Attachments from "./attachments.vue";
import ContentWarning from "./content-warning.vue";
import Note from "./note.vue";
@ -23,9 +24,9 @@ import Prose from "./prose.vue";
const { content, plainContent, sensitive, contentWarning } = defineProps<{
plainContent?: string;
content: string;
quote?: NonNullable<Status["quote"]>;
emojis: Emoji[];
attachments: Attachment[];
quote?: NonNullable<z.infer<typeof Status.shape.quote>>;
emojis: z.infer<typeof CustomEmoji>[];
attachments: z.infer<typeof Attachment>[];
sensitive: boolean;
contentWarning?: string;
}>();

View file

@ -44,12 +44,13 @@
<script lang="ts" setup>
import { cn } from "@/lib/utils";
import type { Account, StatusVisibility } from "@versia/client/types";
import type { Account, Status } from "@versia/client/schemas";
import type {
UseTimeAgoMessages,
UseTimeAgoUnitNamesDefault,
} from "@vueuse/core";
import { AtSign, Globe, Lock, LockOpen } from "lucide-vue-next";
import type { z } from "zod";
import { getLocale } from "~/paraglide/runtime";
import Avatar from "../profiles/avatar.vue";
import SmallCard from "../profiles/small-card.vue";
@ -61,11 +62,11 @@ import {
const { createdAt, noteUrl, author, authorUrl } = defineProps<{
cornerAvatar?: string;
visibility: StatusVisibility;
visibility: z.infer<typeof Status.shape.visibility>;
noteUrl: string;
createdAt: Date;
smallLayout?: boolean;
author: Account;
author: z.infer<typeof Account>;
authorUrl: string;
}>();

View file

@ -26,7 +26,7 @@ const { authorId, noteId } = defineProps<{
apiNoteString: string;
isRemote: boolean;
url: string;
remoteUrl: string;
remoteUrl?: string;
authorId: string;
noteId: string;
}>();
@ -102,7 +102,7 @@ const _delete = async () => {
<Link />
{{ m.ago_new_pelican_drip() }}
</DropdownMenuItem>
<DropdownMenuItem as="button" v-if="isRemote" @click="copyText(remoteUrl)">
<DropdownMenuItem as="button" v-if="isRemote && remoteUrl" @click="copyText(remoteUrl)">
<Link />
{{ m.solid_witty_zebra_walk() }}
</DropdownMenuItem>

View file

@ -45,7 +45,7 @@
:content="noteToUse.content"
:quote="note.quote ?? undefined"
:attachments="noteToUse.media_attachments"
:plain-content="noteToUse.plain_content ?? undefined"
:plain-content="noteToUse.text ?? undefined"
:emojis="noteToUse.emojis"
:sensitive="noteToUse.sensitive"
:content-warning="noteToUse.spoiler_text"
@ -58,7 +58,7 @@
:url="url"
:api-note-string="JSON.stringify(noteToUse, null, 4)"
:reblog-count="noteToUse.reblogs_count"
:remote-url="noteToUse.url"
:remote-url="noteToUse.url ?? undefined"
:is-remote="isRemote"
:author-id="noteToUse.account.id"
@edit="useEvent('composer:edit', noteToUse)"
@ -75,15 +75,18 @@
<script setup lang="ts">
import { cn } from "@/lib/utils";
import type { Status } from "@versia/client/types";
import type { Status } from "@versia/client/schemas";
import type { z } from "zod";
import { Card, CardContent, CardFooter, CardHeader } from "../ui/card";
import Actions from "./actions.vue";
import Content from "./content.vue";
import Header from "./header.vue";
import ReblogHeader from "./reblog-header.vue";
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
const { note } = defineProps<{
note: Status;
note: PartialBy<z.infer<typeof Status>, "reblog" | "quote">;
hideActions?: boolean;
smallLayout?: boolean;
contentUnderUsername?: boolean;
@ -92,7 +95,11 @@ const { note } = defineProps<{
}>();
// Notes can be reblogs, in which case the actual thing to render is inside the reblog property
const noteToUse = computed(() => (note.reblog ? note.reblog : note));
const noteToUse = computed(() =>
note.reblog
? (note.reblog as z.infer<typeof Status>)
: (note as z.infer<typeof Status>),
);
const url = wrapUrl(`/@${noteToUse.value.account.acct}/${noteToUse.value.id}`);
const accountUrl = wrapUrl(`/@${noteToUse.value.account.acct}`);

View file

@ -10,8 +10,9 @@
</template>
<script lang="ts" setup>
import type { Emoji } from "@versia/client/types";
import type { CustomEmoji } from "@versia/client/schemas";
import { Repeat } from "lucide-vue-next";
import type { z } from "zod";
import * as m from "~/paraglide/messages.js";
import Avatar from "../profiles/avatar.vue";
import { Card } from "../ui/card";
@ -19,7 +20,7 @@ import { Card } from "../ui/card";
const { url } = defineProps<{
avatar: string;
displayName: string;
emojis: Emoji[];
emojis: z.infer<typeof CustomEmoji>[];
url: string;
}>();

View file

@ -17,11 +17,12 @@
</template>
<script lang="ts" setup>
import type { Status } from "@versia/client/types";
import type { Status } from "@versia/client/schemas";
import type { z } from "zod";
import Note from "./note.vue";
const { note } = defineProps<{
note: Status;
note: z.infer<typeof Status>;
}>();
const parent = useNote(client, note.in_reply_to_id);

View file

@ -35,16 +35,17 @@
</template>
<script lang="ts" setup>
import type { Account } from "@versia/client/types";
import type { Account } from "@versia/client/schemas";
import { Check, Loader, X } from "lucide-vue-next";
import { toast } from "vue-sonner";
import type { z } from "zod";
import CopyableText from "~/components/notes/copyable-text.vue";
import { Button } from "~/components/ui/button";
import * as m from "~/paraglide/messages.js";
import Avatar from "../profiles/avatar.vue";
const { follower } = defineProps<{
follower: Account;
follower: z.infer<typeof Account>;
}>();
const loading = ref(true);

View file

@ -56,16 +56,16 @@
</template>
<script lang="ts" setup>
import type { Notification } from "@versia/client/types";
import type { Notification } from "@versia/client/schemas";
import {
AtSign,
ChevronDown,
Heart,
Repeat,
User,
UserCheck,
UserPlus,
} from "lucide-vue-next";
import type { z } from "zod";
import { Button } from "~/components/ui/button";
import { Card, CardContent, CardHeader } from "~/components/ui/card";
import {
@ -84,7 +84,7 @@ import Avatar from "../profiles/avatar.vue";
import FollowRequest from "./follow-request.vue";
const { notification } = defineProps<{
notification: Notification;
notification: z.infer<typeof Notification>;
}>();
const icon = computed(() => {
@ -99,8 +99,8 @@ const icon = computed(() => {
return Heart;
case "follow_request":
return User;
case "follow_accept":
return UserCheck;
// case "follow_accept":
// return UserCheck;
default:
return null;
}
@ -118,8 +118,8 @@ const text = computed(() => {
return m.swift_just_beetle_devour();
case "follow_request":
return m.seemly_short_thrush_bloom();
case "follow_accept":
return m.weird_seemly_termite_scold();
//case "follow_accept":
// return m.weird_seemly_termite_scold();
default:
return "";
}

View file

@ -7,7 +7,7 @@ import {
FormMessage,
} from "@/components/ui/form";
import { toTypedSchema } from "@vee-validate/zod";
import type { Instance } from "@versia/client";
import type { Instance } from "@versia/client/schemas";
import { Loader } from "lucide-vue-next";
import { useForm } from "vee-validate";
import * as z from "zod";
@ -16,7 +16,7 @@ import { Input } from "~/components/ui/input";
import * as m from "~/paraglide/messages.js";
const { instance } = defineProps<{
instance: Instance;
instance: z.infer<typeof Instance>;
}>();
const isLoading = ref(false);

View file

@ -13,9 +13,10 @@
</template>
<script lang="ts" setup>
import { type Emoji, RolePermission } from "@versia/client/types";
import { type CustomEmoji, RolePermission } from "@versia/client/schemas";
import { Delete } from "lucide-vue-next";
import { toast } from "vue-sonner";
import type { z } from "zod";
import { confirmModalService } from "~/components/modals/composable";
import {
DropdownMenu,
@ -26,7 +27,7 @@ import {
import * as m from "~/paraglide/messages.js";
const { emojis } = defineProps<{
emojis: Emoji[];
emojis: z.infer<typeof CustomEmoji>[];
}>();
const permissions = usePermissions();

View file

@ -24,9 +24,10 @@
</template>
<script lang="ts" setup>
import { type Emoji, RolePermission } from "@versia/client/types";
import { type CustomEmoji, RolePermission } from "@versia/client/schemas";
import { Delete, MoreHorizontal, TextCursorInput } from "lucide-vue-next";
import { toast } from "vue-sonner";
import type { z } from "zod";
import { confirmModalService } from "~/components/modals/composable";
import { Button } from "~/components/ui/button";
import {
@ -38,7 +39,7 @@ import {
import * as m from "~/paraglide/messages.js";
const { emoji } = defineProps<{
emoji: Emoji;
emoji: z.infer<typeof CustomEmoji>;
}>();
const permissions = usePermissions();

View file

@ -5,8 +5,7 @@
</template>
<script lang="ts" setup>
import { type Emoji, RolePermission } from "@versia/client/types";
import * as m from "~/paraglide/messages.js";
import { RolePermission } from "@versia/client/schemas";
import Table from "./table.vue";
const permissions = usePermissions();
@ -16,36 +15,5 @@ const canUpload = computed(
permissions.value.includes(RolePermission.ManageEmojis),
);
const emojis = computed(
() =>
identity.value?.emojis?.filter((emoji) =>
emoji.shortcode.toLowerCase().includes(search.value.toLowerCase()),
) ?? [],
);
const search = ref("");
/**
* Sort emojis by category
*/
const categories = computed(() => {
const categories = new Map<string, Emoji[]>();
for (const emoji of emojis.value) {
if (!emoji.category) {
if (!categories.has(m.lucky_ago_rat_pinch())) {
categories.set(m.lucky_ago_rat_pinch(), []);
}
categories.get(m.lucky_ago_rat_pinch())?.push(emoji);
continue;
}
if (!categories.has(emoji.category)) {
categories.set(emoji.category, []);
}
categories.get(emoji.category)?.push(emoji);
}
return categories;
});
const emojis = computed(() => identity.value?.emojis ?? []);
</script>

View file

@ -33,7 +33,7 @@ import {
getSortedRowModel,
useVueTable,
} from "@tanstack/vue-table";
import type { Emoji } from "@versia/client/types";
import type { CustomEmoji } from "@versia/client/schemas";
import {
ArrowDownAZ,
ArrowUpAz,
@ -45,13 +45,14 @@ import {
Plus,
} from "lucide-vue-next";
import { ref } from "vue";
import type { z } from "zod";
import BatchDropdown from "./batch-dropdown.vue";
import Dropdown from "./dropdown.vue";
import Uploader from "./uploader.vue";
// No destructuring props to avoid reactivity issues
const props = defineProps<{
emojis: Emoji[];
emojis: z.infer<typeof CustomEmoji>[];
canUpload: boolean;
}>();
@ -64,7 +65,7 @@ const valueUpdater = <T extends Updater<any>>(updaterOrValue: T, ref: Ref) => {
: updaterOrValue;
};
const columns: ColumnDef<Emoji>[] = [
const columns: ColumnDef<z.infer<typeof CustomEmoji>>[] = [
{
id: "select",
header: ({ table }) => (

View file

@ -160,7 +160,7 @@
<script lang="ts" setup>
import { toTypedSchema } from "@vee-validate/zod";
import { RolePermission } from "@versia/client/types";
import { RolePermission } from "@versia/client/schemas";
import { useForm } from "vee-validate";
import { toast } from "vue-sonner";
import { z } from "zod";
@ -221,11 +221,11 @@ const formSchema = toTypedSchema(
.min(1)
.max(
identity.value?.instance.configuration.emojis
.max_emoji_shortcode_characters ?? Number.POSITIVE_INFINITY,
.max_shortcode_characters ?? Number.POSITIVE_INFINITY,
m.solid_inclusive_owl_hug({
count:
identity.value?.instance.configuration.emojis
.max_emoji_shortcode_characters ??
.max_shortcode_characters ??
Number.POSITIVE_INFINITY,
}),
)
@ -244,12 +244,11 @@ const formSchema = toTypedSchema(
.string()
.max(
identity.value?.instance.configuration.emojis
.max_emoji_description_characters ??
Number.POSITIVE_INFINITY,
.max_description_characters ?? Number.POSITIVE_INFINITY,
m.key_ago_hound_emerge({
count:
identity.value?.instance.configuration.emojis
.max_emoji_description_characters ??
.max_description_characters ??
Number.POSITIVE_INFINITY,
}),
)

View file

@ -13,10 +13,10 @@ export const formSchema = (identity: Identity) =>
(v) =>
v.size <=
(identity.instance.configuration.accounts
.header_size_limit ?? Number.POSITIVE_INFINITY),
.header_limit ?? Number.POSITIVE_INFINITY),
m.civil_icy_ant_mend({
size: identity.instance.configuration.accounts
.header_size_limit,
.header_limit,
}),
)
.optional(),
@ -26,10 +26,10 @@ export const formSchema = (identity: Identity) =>
(v) =>
v.size <=
(identity.instance.configuration.accounts
.avatar_size_limit ?? Number.POSITIVE_INFINITY),
.avatar_limit ?? Number.POSITIVE_INFINITY),
m.zippy_caring_raven_edit({
size: identity.instance.configuration.accounts
.avatar_size_limit,
.avatar_limit,
}),
)
.or(z.string().url())

View file

@ -71,7 +71,7 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import type { Account } from "@versia/client/types";
import type { Account } from "@versia/client/schemas";
import {
AtSign,
Ban,
@ -84,10 +84,11 @@ import {
VolumeX,
} from "lucide-vue-next";
import { toast } from "vue-sonner";
import type { z } from "zod";
import * as m from "~/paraglide/messages.js";
const { account } = defineProps<{
account: Account;
account: z.infer<typeof Account>;
}>();
const isMe = identity.value?.account.id === account.id;

View file

@ -25,12 +25,13 @@
</template>
<script lang="ts" setup>
import type { Account } from "@versia/client/types";
import type { Account } from "@versia/client/schemas";
import type { z } from "zod";
import * as m from "~/paraglide/messages.js";
import ProfileBadge from "./profile-badge.vue";
const { account } = defineProps<{
account: Account;
account: z.infer<typeof Account>;
}>();
const config = useConfig();

View file

@ -4,11 +4,12 @@
</template>
<script lang="ts" setup>
import type { Emoji } from "@versia/client/types";
import type { CustomEmoji } from "@versia/client/schemas";
import type { z } from "zod";
const { content } = defineProps<{
content: string;
emojis: Emoji[];
emojis: z.infer<typeof CustomEmoji>[];
}>();
</script>

View file

@ -8,10 +8,11 @@
</template>
<script lang="ts" setup>
import type { Emoji, Field } from "@versia/client/types";
import type { CustomEmoji, Field } from "@versia/client/schemas";
import type { z } from "zod";
defineProps<{
fields: Field[];
emojis: Emoji[];
fields: z.infer<typeof Field>[];
emojis: z.infer<typeof CustomEmoji>[];
}>();
</script>
</script>

View file

@ -66,9 +66,10 @@
</template>
<script lang="ts" setup>
import type { Account } from "@versia/client/types";
import type { Account } from "@versia/client/schemas";
import { Ellipsis, Loader } from "lucide-vue-next";
import { toast } from "vue-sonner";
import type { z } from "zod";
import CopyableText from "~/components/notes/copyable-text.vue";
import { Button } from "~/components/ui/button";
import { Card, CardContent, CardFooter, CardTitle } from "~/components/ui/card";
@ -83,7 +84,7 @@ import ProfileHeader from "./profile-header.vue";
import ProfileStats from "./profile-stats.vue";
const { account } = defineProps<{
account: Account;
account: z.infer<typeof Account>;
}>();
const { relationship, isLoading } = useRelationship(client, account.id);

View file

@ -52,7 +52,8 @@
</template>
<script lang="ts" setup>
import type { Account } from "@versia/client/types";
import type { Account } from "@versia/client/schemas";
import type { z } from "zod";
import { Separator } from "~/components/ui/separator";
import CopyableText from "../notes/copyable-text.vue";
import Avatar from "./avatar.vue";
@ -60,7 +61,7 @@ import ProfileContent from "./profile-content.vue";
import ProfileFields from "./profile-fields.vue";
const { account } = defineProps<{
account: Account;
account: z.infer<typeof Account>;
}>();
const [username, instance] = account.acct.split("@");

View file

@ -18,12 +18,13 @@
</template>
<script lang="ts" setup>
import type { Account } from "@versia/client/types";
import type { Account } from "@versia/client/schemas";
import type { z } from "zod";
import { Card, CardContent } from "~/components/ui/card";
import Avatar from "./avatar.vue";
const { account, domain, naked } = defineProps<{
account: Account;
account: z.infer<typeof Account>;
domain: string;
naked?: boolean;
}>();

View file

@ -1,89 +0,0 @@
<template>
<!-- OIDC linked accounts manager -->
<div class="w-full ring-1 ring-white/5 pb-5 bg-dark-800 rounded overflow-hidden">
<div class="px-4 py-4">
<h3 class="font-semibold text-gray-300 text-xl">Linked accounts</h3>
</div>
<div class="px-4 py-3">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-1 xl:grid-cols-2 gap-4">
<div v-for="provider of ssoConfig?.providers" :key="provider.id"
class="flex items-center justify-between p-4 bg-dark-700 rounded">
<div class="flex items-center gap-4">
<Avatar :src="provider.icon" :alt="provider.name" class="size-8" />
<span class="font-semibold text-gray-300">{{ provider.name }}</span>
</div>
<div>
<Button theme="primary" :loading="loading"
v-if="!linkedProviders?.find(p => p.id === provider.id)" @click="link(provider.id)">
Link
</Button>
<Button theme="secondary" :loading="loading" v-else @click="unlink(provider.id)">
Unlink
</Button>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import type { ResponseError } from "@versia/client";
import Button from "~/packages/ui/components/buttons/button.vue";
import Avatar from "../avatars/avatar.vue";
const ssoConfig = useSSOConfig();
const linkedProviders = useLinkedSSO(client);
const loading = ref(false);
const link = async (providerId: string) => {
loading.value = true;
try {
const output = await client.value.post<{
link: string;
}>("/api/v1/sso", {
issuer: providerId,
});
window.location.href = output.data.link;
} catch (error) {
const e = error as ResponseError<{ error: string }>;
useEvent("notification:new", {
title: "Failed to link account",
description: e.response.data.error,
type: "error",
});
}
loading.value = false;
};
const unlink = async (providerId: string) => {
loading.value = true;
try {
await client.value.delete<void>(`/api/v1/sso/${providerId}`);
useEvent("notification:new", {
title: "Account unlinked",
type: "success",
});
linkedProviders.value = linkedProviders.value.filter(
(p) => p.id !== providerId,
);
} catch (error) {
const e = error as ResponseError<{ error: string }>;
useEvent("notification:new", {
title: "Failed to unlink account",
description: e.response.data.error,
type: "error",
});
}
loading.value = false;
};
</script>

View file

@ -44,8 +44,7 @@
</template>
<script lang="ts" setup>
import type { Account } from "@versia/client/types";
import { ChevronsUpDown, LogIn, LogOut, UserPlus } from "lucide-vue-next";
import { LogIn, LogOut, UserPlus } from "lucide-vue-next";
import { toast } from "vue-sonner";
import TinyCard from "~/components/profiles/tiny-card.vue";
import { Button } from "~/components/ui/button";

View file

@ -1,11 +1,11 @@
<template>
<Timeline type="status" :items="(items as Status[])" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
:update-item="updateItem" />
</template>
<script lang="ts" setup>
import type { Status } from "@versia/client/types";
import type { z } from "zod";
import Timeline from "./timeline.vue";
const props = defineProps<{
@ -34,4 +34,4 @@ useListen("note:edit", (updatedNote) => {
useListen("composer:send", () => {
loadPrev();
});
</script>
</script>

View file

@ -1,11 +1,11 @@
<template>
<Timeline type="status" :items="(items as Status[])" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
:update-item="updateItem" />
</template>
<script lang="ts" setup>
import type { Status } from "@versia/client/types";
import type { z } from "zod";
import { useGlobalTimeline } from "~/composables/GlobalTimeline";
import Timeline from "./timeline.vue";
@ -31,4 +31,4 @@ useListen("note:edit", (updatedNote) => {
useListen("composer:send", () => {
loadPrev();
});
</script>
</script>

View file

@ -1,13 +1,12 @@
<template>
<Timeline type="status" :items="(items as Status[])" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
:update-item="updateItem" />
</template>
<script lang="ts" setup>
import type { Status } from "@versia/client/types";
import { useHomeTimeline } from "~/composables/HomeTimeline";
<script lang="ts" setup>import { useHomeTimeline } from "~/composables/HomeTimeline";
import Timeline from "./timeline.vue";
import type { z } from "zod";
const {
error,
@ -31,4 +30,4 @@ useListen("note:edit", (updatedNote) => {
useListen("composer:send", () => {
loadPrev();
});
</script>
</script>

View file

@ -1,11 +1,11 @@
<template>
<Timeline type="status" :items="(items as Status[])" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
:update-item="updateItem" />
</template>
<script lang="ts" setup>
import type { Status } from "@versia/client/types";
import type { z } from "zod";
import { useLocalTimeline } from "~/composables/LocalTimeline";
import Timeline from "./timeline.vue";
@ -31,4 +31,4 @@ useListen("note:edit", (updatedNote) => {
useListen("composer:send", () => {
loadPrev();
});
</script>
</script>

View file

@ -1,11 +1,10 @@
<template>
<Timeline type="notification" :items="(items as Notification[])" :is-loading="isLoading"
<Timeline type="notification" :items="items" :is-loading="isLoading"
:has-reached-end="hasReachedEnd" :error="error" :load-next="loadNext" :load-prev="loadPrev"
:remove-item="removeItem" :update-item="updateItem" />
</template>
<script lang="ts" setup>
import type { Notification } from "@versia/client/types";
import { useNotificationTimeline } from "~/composables/NotificationTimeline";
import Timeline from "./timeline.vue";
@ -19,4 +18,4 @@ const {
removeItem,
updateItem,
} = useNotificationTimeline(client.value);
</script>
</script>

View file

@ -1,11 +1,10 @@
<template>
<Timeline type="status" :items="(items as Status[])" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
:update-item="updateItem" />
</template>
<script lang="ts" setup>
import type { Status } from "@versia/client/types";
import { usePublicTimeline } from "~/composables/PublicTimeline";
import Timeline from "./timeline.vue";
@ -31,4 +30,4 @@ useListen("note:edit", (updatedNote) => {
useListen("composer:send", () => {
loadPrev();
});
</script>
</script>

View file

@ -1,15 +1,16 @@
<template>
<component :is="itemComponent" :note="type === 'status' ? item : undefined" :notification="type === 'notification' ? item : undefined" @update="$emit('update', $event)"
<component :is="itemComponent" :note="type === 'status' ? item : undefined" :notification="type === 'notification' ? item : (undefined as any)" @update="$emit('update', $event)"
@delete="$emit('delete', item?.id)" />
</template>
<script lang="ts" setup>
import type { Notification, Status } from "@versia/client/types";
import type { Notification, Status } from "@versia/client/schemas";
import type { z } from "zod";
import Thread from "../notes/thread.vue";
import NotificationItem from "../notifications/notification.vue";
const props = defineProps<{
item?: Status | Notification;
item?: z.infer<typeof Status> | z.infer<typeof Notification>;
type: "status" | "notification";
}>();
@ -17,9 +18,11 @@ const itemComponent = computed(() => {
if (props.type === "status") {
return Thread;
}
if (props.type === "notification") {
return NotificationItem;
}
return null;
});
</script>

View file

@ -40,8 +40,9 @@
</template>
<script lang="ts" setup>
import type { Notification, Status } from "@versia/client/types";
import type { Notification, Status } from "@versia/client/schemas";
import { useIntersectionObserver } from "@vueuse/core";
import type { z } from "zod";
import * as m from "~/paraglide/messages.js";
import NoPosts from "../errors/NoPosts.vue";
import ReachedEnd from "../errors/ReachedEnd.vue";
@ -50,7 +51,7 @@ import { Button } from "../ui/button";
import TimelineItem from "./timeline-item.vue";
const props = defineProps<{
items: Status[] | Notification[];
items: z.infer<typeof Status>[] | z.infer<typeof Notification>[];
type: "status" | "notification";
isLoading: boolean;
hasReachedEnd: boolean;
@ -58,14 +59,15 @@ const props = defineProps<{
loadNext: () => void;
loadPrev: () => void;
removeItem: (id: string) => void;
updateItem: ((item: Status) => void) | ((item: Notification) => void);
updateItem:
| ((item: z.infer<typeof Status>) => void)
| ((item: z.infer<typeof Notification>) => void);
}>();
const emit = defineEmits<(e: "update") => void>();
const loadMoreTrigger = ref<HTMLElement | null>(null);
// @ts-expect-error Too complex?
useIntersectionObserver(loadMoreTrigger, ([observer]) => {
if (observer?.isIntersecting && !props.isLoading && !props.hasReachedEnd) {
props.loadNext();

View file

@ -5,7 +5,7 @@ import { DrawerRoot } from "vaul-vue";
const props = withDefaults(defineProps<DrawerRootProps>(), {
shouldScaleBackground: true,
});
}) as DrawerRootProps;
const emits = defineEmits<DrawerRootEmits>();