forked from BRETT/BRETT
99 lines
3.7 KiB
Vue
99 lines
3.7 KiB
Vue
<script setup lang="ts">
|
|
import {
|
|
Sidebar,
|
|
SidebarContent,
|
|
} from '@/components/ui/sidebar'
|
|
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'
|
|
import type { CommentAttachment } from '~~/shared/utils/attachments'
|
|
|
|
interface LatestComment {
|
|
id: number
|
|
content: string
|
|
createdAt: string
|
|
userName: string
|
|
userId: number
|
|
userColor: string | null
|
|
userAvatarUrl: string | null
|
|
uploadId: string
|
|
attachments?: CommentAttachment[]
|
|
}
|
|
|
|
const { data: comments } = await useFetch<LatestComment[]>('/api/comments/latest', {
|
|
key: 'latest-comments',
|
|
default: () => [],
|
|
})
|
|
const { liveComments } = useLiveComments()
|
|
const timeAgo = useTimeAgo()
|
|
|
|
const mergedComments = computed(() => {
|
|
const seen = new Set<number>()
|
|
return [...liveComments.value, ...comments.value]
|
|
.filter((c) => {
|
|
if (seen.has(c.id)) return false
|
|
seen.add(c.id)
|
|
return true
|
|
})
|
|
.slice(0, 15)
|
|
})
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<Sidebar side="right" collapsible="none" class="sticky top-0 hidden h-svh shrink-0 overflow-hidden rounded-l-md border-l lg:flex">
|
|
<SidebarContent class="px-2 py-2">
|
|
<p v-if="!mergedComments.length" class="px-2 py-1 text-sm text-muted-foreground">
|
|
{{ $t('comments.none') }}
|
|
</p>
|
|
<NuxtLink
|
|
v-for="comment in mergedComments"
|
|
:key="comment.id"
|
|
:to="`/thread/${comment.uploadId}#comment-${comment.id}`"
|
|
class="block rounded-md border border-sidebar-border/60 bg-sidebar px-3 py-2.5 text-sm transition-colors hover:border-sidebar-border hover:bg-sidebar-accent"
|
|
>
|
|
<div class="flex items-center gap-2 text-xs text-muted-foreground">
|
|
<NuxtImg
|
|
v-if="comment.userAvatarUrl"
|
|
:src="comment.userAvatarUrl"
|
|
:alt="comment.userName"
|
|
width="28"
|
|
height="28"
|
|
fit="cover"
|
|
class="size-7 shrink-0 rounded-sm"
|
|
/>
|
|
<span
|
|
v-else
|
|
class="flex size-7 shrink-0 items-center justify-center rounded-sm text-xs font-medium"
|
|
:style="comment.userColor ? { backgroundColor: comment.userColor, color: '#fff' } : { backgroundColor: 'var(--color-muted)' }"
|
|
>{{ comment.userName.slice(0, 2).toUpperCase() }}</span>
|
|
<UserHoverCard
|
|
:user-id="comment.userId"
|
|
:user-name="comment.userName"
|
|
:user-color="comment.userColor"
|
|
:user-avatar-url="comment.userAvatarUrl"
|
|
>
|
|
<span
|
|
class="truncate text-sm font-medium text-foreground hover:underline"
|
|
:style="comment.userColor ? { color: comment.userColor } : undefined"
|
|
@click.stop.prevent="navigateTo(`/user/${comment.userId}`)"
|
|
>{{ comment.userName }}</span>
|
|
</UserHoverCard>
|
|
<span class="shrink-0">· {{ timeAgo(comment.createdAt) }}</span>
|
|
</div>
|
|
<HoverCard :open-delay="250" :close-delay="100">
|
|
<HoverCardTrigger as-child>
|
|
<div class="mt-1.5 line-clamp-3 break-words">
|
|
<CommentContent :content="comment.content" compact /><CommentAttachments v-if="comment.attachments?.length" :attachments="comment.attachments" compact />
|
|
</div>
|
|
</HoverCardTrigger>
|
|
<HoverCardContent side="left" align="start" class="w-80 max-w-[90vw] text-sm">
|
|
<div class="max-h-64 overflow-y-auto break-words">
|
|
<CommentContent :content="comment.content" />
|
|
<CommentAttachments v-if="comment.attachments?.length" :attachments="comment.attachments" />
|
|
</div>
|
|
</HoverCardContent>
|
|
</HoverCard>
|
|
</NuxtLink>
|
|
</SidebarContent>
|
|
</Sidebar>
|
|
</template>
|