forked from BRETT/BRETT
Initial Commit
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
<script setup lang="ts">
|
||||
import AudioPlayerDialog from '@/components/AudioPlayerDialog.vue'
|
||||
import ImageLightbox from '@/components/ImageLightbox.vue'
|
||||
import VoiceMessagePlayer from '@/components/VoiceMessagePlayer.vue'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { formatFileSize, isAudioAttachment, isImageAttachment, isVoiceAttachment } from '~~/shared/utils/attachments'
|
||||
import type { CommentAttachment } from '~~/shared/utils/attachments'
|
||||
|
||||
const props = defineProps<{
|
||||
attachments: CommentAttachment[]
|
||||
compact?: boolean
|
||||
}>()
|
||||
|
||||
const voices = computed(() => props.attachments.filter(isVoiceAttachment))
|
||||
const images = computed(() => props.attachments.filter(a => isImageAttachment(a) && !isVoiceAttachment(a)))
|
||||
const audios = computed(() => props.attachments.filter(a => isAudioAttachment(a) && !isImageAttachment(a) && !isVoiceAttachment(a)))
|
||||
const files = computed(() => props.attachments.filter(a => !isImageAttachment(a) && !isAudioAttachment(a) && !isVoiceAttachment(a)))
|
||||
const hasFileAttachments = computed(() => images.value.length + audios.value.length + files.value.length > 0)
|
||||
|
||||
const lightboxSrc = ref<string | null>(null)
|
||||
const lightboxAlt = ref('')
|
||||
const audioAtt = ref<CommentAttachment | null>(null)
|
||||
|
||||
function fileIcon(a: CommentAttachment) {
|
||||
const mime = a.mimeType ?? ''
|
||||
const name = a.name.toLowerCase()
|
||||
if (mime.startsWith('video/')) return 'ph:file-video-fill'
|
||||
if (mime === 'application/pdf' || /\.(txt|md|pdf|docx?|odt)$/.test(name)) return 'ph:file-text-fill'
|
||||
if (/\.(zip|rar|7z|tar|gz|bz2|xz)$/.test(name) || /zip|compressed|x-tar|gzip/.test(mime)) return 'ph:file-archive-fill'
|
||||
return 'ph:file-fill'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Kompakt-Modus (Sidebar): nur ein Hinweis-Chip -->
|
||||
<span
|
||||
v-if="compact && attachments.length"
|
||||
class="mx-0.5 inline-flex items-center gap-1 rounded-sm bg-muted px-1.5 py-0.5 align-middle text-xs text-muted-foreground"
|
||||
>
|
||||
<Icon name="ph:folder-plus-fill" class="size-3.5" />
|
||||
{{ attachments.length }}
|
||||
</span>
|
||||
|
||||
<div v-else-if="attachments.length" class="mt-2.5 space-y-2.5">
|
||||
<!-- Sprachnachrichten gehören direkt zum Kommentar, nicht in den Anhang-Bereich -->
|
||||
<div v-if="voices.length" class="flex flex-col gap-1.5">
|
||||
<VoiceMessagePlayer
|
||||
v-for="a in voices"
|
||||
:key="a.id"
|
||||
:src="a.url"
|
||||
:name="a.name"
|
||||
/>
|
||||
</div>
|
||||
<template v-if="hasFileAttachments">
|
||||
<div class="mb-1.5 flex items-center gap-2">
|
||||
<span class="text-[11px] font-medium uppercase tracking-wider text-muted-foreground">Anhänge</span>
|
||||
<Separator class="flex-1" />
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-1.5">
|
||||
<button
|
||||
v-for="a in images"
|
||||
:key="a.id"
|
||||
type="button"
|
||||
class="group relative size-16 cursor-pointer overflow-hidden rounded-md border bg-muted transition-colors hover:border-primary"
|
||||
:title="a.name"
|
||||
@click.stop="lightboxSrc = a.url; lightboxAlt = a.name"
|
||||
>
|
||||
<NuxtImg
|
||||
:src="a.url"
|
||||
:alt="a.name"
|
||||
width="64"
|
||||
height="64"
|
||||
fit="cover"
|
||||
loading="lazy"
|
||||
class="size-full object-cover"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
v-for="a in audios"
|
||||
:key="a.id"
|
||||
type="button"
|
||||
class="flex max-w-48 cursor-pointer items-center gap-1.5 rounded-md border bg-muted/50 px-2 py-1 text-xs transition-colors hover:border-primary hover:bg-accent"
|
||||
:title="`${a.name} abspielen`"
|
||||
@click.stop="audioAtt = a"
|
||||
>
|
||||
<img v-if="a.cover" :src="a.cover" :alt="a.name" class="size-5 shrink-0 rounded-sm object-cover">
|
||||
<Icon name="ph:file-audio-fill" v-else class="size-3.5 shrink-0 text-muted-foreground" />
|
||||
<span class="truncate">{{ a.name }}</span>
|
||||
<span v-if="formatFileSize(a.size)" class="shrink-0 text-muted-foreground">{{ formatFileSize(a.size) }}</span>
|
||||
<Icon name="ph:play-fill" class="size-3.5 shrink-0 text-muted-foreground" />
|
||||
</button>
|
||||
<a
|
||||
v-for="a in files"
|
||||
:key="a.id"
|
||||
:href="a.url"
|
||||
:download="a.name"
|
||||
class="flex max-w-48 items-center gap-1.5 rounded-md border bg-muted/50 px-2 py-1 text-xs transition-colors hover:border-primary hover:bg-accent"
|
||||
:title="a.name"
|
||||
@click.stop
|
||||
>
|
||||
<Icon :name="fileIcon(a)" class="size-3.5 shrink-0 text-muted-foreground" />
|
||||
<span class="truncate">{{ a.name }}</span>
|
||||
<span v-if="formatFileSize(a.size)" class="shrink-0 text-muted-foreground">{{ formatFileSize(a.size) }}</span>
|
||||
<Icon name="ph:download-fill" class="size-3.5 shrink-0 text-muted-foreground" />
|
||||
</a>
|
||||
|
||||
<ImageLightbox
|
||||
v-if="lightboxSrc"
|
||||
:src="lightboxSrc"
|
||||
:alt="lightboxAlt"
|
||||
@close="lightboxSrc = null"
|
||||
/>
|
||||
<AudioPlayerDialog
|
||||
v-if="audioAtt"
|
||||
:open="true"
|
||||
:src="audioAtt.url"
|
||||
:name="audioAtt.name"
|
||||
:size="audioAtt.size"
|
||||
:cover="audioAtt.cover"
|
||||
:meta="audioAtt.meta"
|
||||
@update:open="audioAtt = null"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user