All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m4s
- Agregar botón "Crear Webhook de Debug" en WebhookReceiverSection - Detectar si ya existe un webhook apuntando al receptor de debug - Permitir eliminar el webhook de debug - Incluir todos los eventos disponibles al crear el webhook - También incluye mejoras previas de manejo de media y mensajes
151 lines
3.9 KiB
Vue
151 lines
3.9 KiB
Vue
<template>
|
|
<div class="relative group">
|
|
<!-- Video player -->
|
|
<video
|
|
v-if="!error"
|
|
ref="videoRef"
|
|
:src="videoUrl"
|
|
:poster="posterUrl"
|
|
class="rounded-lg max-w-full"
|
|
:class="{ 'max-h-80': !fullscreen }"
|
|
:controls="isPlaying"
|
|
preload="metadata"
|
|
@loadedmetadata="onLoadedMetadata"
|
|
@play="isPlaying = true"
|
|
@pause="isPlaying = false"
|
|
@ended="isPlaying = false"
|
|
@error="onError"
|
|
/>
|
|
|
|
<!-- Placeholder con play button cuando no está reproduciendo -->
|
|
<div
|
|
v-if="!isPlaying && !error"
|
|
class="absolute inset-0 flex items-center justify-center cursor-pointer bg-black/20 rounded-lg"
|
|
@click="playVideo"
|
|
>
|
|
<!-- Play button -->
|
|
<div class="w-14 h-14 rounded-full bg-black/50 flex items-center justify-center backdrop-blur-sm">
|
|
<UIcon name="i-lucide-play" class="w-8 h-8 text-white ml-1" />
|
|
</div>
|
|
|
|
<!-- Duration badge -->
|
|
<div
|
|
v-if="duration"
|
|
class="absolute bottom-2 left-2 px-1.5 py-0.5 rounded bg-black/60 text-white text-xs"
|
|
>
|
|
{{ formattedDuration }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error state -->
|
|
<div
|
|
v-if="error"
|
|
class="flex flex-col items-center justify-center p-8 bg-[var(--wa-bg-light)] rounded-lg"
|
|
>
|
|
<UIcon name="i-lucide-video-off" class="w-12 h-12 text-[var(--wa-text-muted)] mb-2" />
|
|
<p class="text-sm text-[var(--wa-text-muted)]">No se pudo cargar el video</p>
|
|
<UButton
|
|
size="xs"
|
|
variant="ghost"
|
|
class="mt-2"
|
|
@click="retryLoad"
|
|
>
|
|
Reintentar
|
|
</UButton>
|
|
</div>
|
|
|
|
<!-- Loading overlay -->
|
|
<div
|
|
v-if="loading"
|
|
class="absolute inset-0 flex items-center justify-center bg-[var(--wa-bg-light)] rounded-lg"
|
|
>
|
|
<UIcon name="i-lucide-loader-2" class="w-8 h-8 text-[var(--wa-text-muted)] animate-spin" />
|
|
</div>
|
|
|
|
<!-- Fullscreen button -->
|
|
<button
|
|
v-if="!error && isPlaying"
|
|
class="absolute top-2 right-2 p-1.5 rounded-full bg-black/50 text-white opacity-0 group-hover:opacity-100 transition-opacity"
|
|
@click="toggleFullscreen"
|
|
>
|
|
<UIcon :name="fullscreen ? 'i-lucide-minimize' : 'i-lucide-maximize'" class="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { MediaInfo } from '~/types/message'
|
|
import { formatDuration } from '~/types/message'
|
|
|
|
interface Props {
|
|
media: MediaInfo
|
|
instanceId: string
|
|
messageId: string
|
|
}
|
|
|
|
const props = defineProps<Props>()
|
|
|
|
const videoRef = ref<HTMLVideoElement | null>(null)
|
|
const isPlaying = ref(false)
|
|
const loading = ref(true)
|
|
const error = ref(false)
|
|
const fullscreen = ref(false)
|
|
const duration = ref(props.media.duration || 0)
|
|
|
|
const videoUrl = computed(() => {
|
|
if (props.media.url) return props.media.url
|
|
return `/api/media/${props.instanceId}/${props.messageId}`
|
|
})
|
|
|
|
const posterUrl = computed(() => {
|
|
if (props.media.thumbnail) {
|
|
return `data:image/jpeg;base64,${props.media.thumbnail}`
|
|
}
|
|
return undefined
|
|
})
|
|
|
|
const formattedDuration = computed(() => formatDuration(duration.value))
|
|
|
|
const playVideo = () => {
|
|
if (videoRef.value) {
|
|
videoRef.value.play()
|
|
}
|
|
}
|
|
|
|
const onLoadedMetadata = () => {
|
|
loading.value = false
|
|
if (videoRef.value && !props.media.duration) {
|
|
duration.value = videoRef.value.duration
|
|
}
|
|
}
|
|
|
|
const onError = () => {
|
|
loading.value = false
|
|
error.value = true
|
|
}
|
|
|
|
const retryLoad = () => {
|
|
error.value = false
|
|
loading.value = true
|
|
}
|
|
|
|
const toggleFullscreen = async () => {
|
|
if (!videoRef.value) return
|
|
|
|
if (!fullscreen.value) {
|
|
await videoRef.value.requestFullscreen()
|
|
fullscreen.value = true
|
|
} else {
|
|
await document.exitFullscreen()
|
|
fullscreen.value = false
|
|
}
|
|
}
|
|
|
|
// Escuchar cambios de fullscreen
|
|
onMounted(() => {
|
|
document.addEventListener('fullscreenchange', () => {
|
|
fullscreen.value = !!document.fullscreenElement
|
|
})
|
|
})
|
|
</script>
|