frontend/app/components/notes/header.vue

139 lines
4.4 KiB
Vue
Raw Normal View History

2024-11-30 02:19:32 +01:00
<template>
<div class="rounded grid grid-cols-[auto_1fr_auto] items-center gap-3">
2025-12-09 22:32:22 +01:00
<HoverCard
v-model:open="popupOpen"
@update:open="() => {
if (!preferences.popup_avatar_hover) {
popupOpen = false;
}
2025-12-09 22:32:22 +01:00
}"
:open-delay="2000"
>
<HoverCardTrigger :as-child="true">
2025-12-09 22:32:22 +01:00
<NuxtLink
:href="urlAsPath"
:class="cn('relative size-12', smallLayout && 'size-8')"
>
<Avatar
:class="cn('size-12 border border-card', smallLayout && 'size-8')"
:src="author.avatar"
:name="author.display_name"
/>
<Avatar
v-if="cornerAvatar"
class="size-6 border absolute -bottom-1 -right-1"
:src="cornerAvatar"
/>
</NuxtLink>
</HoverCardTrigger>
<HoverCardContent class="w-96">
2025-12-09 22:32:22 +01:00
<SmallCard :account="author"/>
</HoverCardContent>
</HoverCard>
2025-12-09 22:32:22 +01:00
<Column :class="smallLayout && 'text-sm'">
<Text class="font-semibold" v-render-emojis="author.emojis">
{{
author.display_name
2025-12-09 22:32:22 +01:00
}}
</Text>
<div class="-mt-1">
2025-12-09 22:32:22 +01:00
<Address as="span" :username="username" :domain="instance"/>
2024-11-30 02:19:32 +01:00
&middot;
2025-12-09 22:32:22 +01:00
<Text
as="span"
muted
class="ml-auto tracking-normal"
:title="fullTime"
>
{{ timeAgo }}
</Text>
</div>
2025-12-09 22:32:22 +01:00
</Column>
<div v-if="!smallLayout">
2025-12-09 22:32:22 +01:00
<NuxtLink
:href="noteUrlAsPath"
class="text-xs text-muted-foreground"
:title="visibilities[visibility].text"
>
<component :is="visibilities[visibility].icon" class="size-4"/>
2024-12-02 22:29:08 +01:00
</NuxtLink>
2024-11-30 02:19:32 +01:00
</div>
</div>
2024-11-30 02:19:32 +01:00
</template>
<script lang="ts" setup>
import type { Account, Status } from "@versia/client/schemas";
import type {
UseTimeAgoMessages,
UseTimeAgoUnitNamesDefault,
} from "@vueuse/core";
2024-11-30 02:19:32 +01:00
import { AtSign, Globe, Lock, LockOpen } from "lucide-vue-next";
import type { z } from "zod";
2025-06-26 22:39:02 +02:00
import { cn } from "@/lib/utils";
2025-07-16 07:48:39 +02:00
import { getLocale } from "~~/paraglide/runtime";
import Address from "../profiles/address.vue";
import Avatar from "../profiles/avatar.vue";
import SmallCard from "../profiles/small-card.vue";
2025-12-09 22:32:22 +01:00
import Column from "../typography/layout/col.vue";
import Text from "../typography/text.vue";
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "../ui/hover-card";
2024-11-30 02:19:32 +01:00
const { createdAt, noteUrl, author, authorUrl } = defineProps<{
cornerAvatar?: string;
visibility: z.infer<typeof Status.shape.visibility>;
2024-12-02 22:29:08 +01:00
noteUrl: string;
2024-11-30 02:19:32 +01:00
createdAt: Date;
2024-11-30 16:39:02 +01:00
smallLayout?: boolean;
author: z.infer<typeof Account>;
authorUrl: string;
2024-11-30 02:19:32 +01:00
}>();
const [username, instance] = author.acct.split("@");
const digitRegex = /\d/;
const urlAsPath = new URL(authorUrl).pathname;
2024-12-02 22:29:08 +01:00
const noteUrlAsPath = new URL(noteUrl).pathname;
const timeAgo = useTimeAgo(createdAt, {
messages: {
justNow: "now",
past: (n) => (n.match(digitRegex) ? `${n}` : n),
future: (n) => (n.match(digitRegex) ? `in ${n}` : n),
month: (n) => `${n}mo`,
year: (n) => `${n}y`,
day: (n) => `${n}d`,
week: (n) => `${n}w`,
hour: (n) => `${n}h`,
minute: (n) => `${n}m`,
second: (n) => `${n}s`,
invalid: "",
} as UseTimeAgoMessages<UseTimeAgoUnitNamesDefault>,
});
const fullTime = new Intl.DateTimeFormat(getLocale(), {
2024-11-30 02:19:32 +01:00
dateStyle: "medium",
timeStyle: "short",
}).format(createdAt);
const popupOpen = ref(false);
2024-11-30 02:19:32 +01:00
const visibilities = {
public: {
icon: Globe,
text: "This note is public: it can be seen by anyone.",
},
unlisted: {
icon: LockOpen,
text: "This note is unlisted: it can be seen by anyone with the link.",
},
private: {
icon: Lock,
text: "This note is private: it can only be seen by followers.",
},
direct: {
icon: AtSign,
text: "This note is direct: it can only be seen by mentioned users.",
},
};
</script>