Files
josedario87 80d0042c7e
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m4s
Feature: Agregar botón para crear webhook de debug automáticamente
- 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
2025-12-02 21:21:33 -06:00

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>