fix(video-player): fix the video player to permit the nagivation through the video (it was because de fast api server refused range request)
Some checks failed
Deploy to Development / build-and-deploy (push) Failing after 2m15s

This commit is contained in:
EvanChal
2026-01-25 18:08:38 +01:00
parent 3fbf372dae
commit 0020c13bfd
17 changed files with 393 additions and 212 deletions

View File

@@ -2,29 +2,37 @@
<div class="video-player-container">
<div class="relative">
<!-- Video.js Player -->
<video
ref="videoPlayer"
class="video-js vjs-default-skin vjs-big-play-centered w-full rounded-lg"
controls
preload="auto"
:poster="posterUrl"
data-setup="{}"
>
<source :src="videoUrl" type="video/mp4" />
<p class="vjs-no-js">
Pour voir cette vidéo, activez JavaScript et considérez passer à un navigateur web qui
<a href="https://videojs.com/html5-video-support/" target="_blank">supporte la vidéo HTML5</a>.
</p>
</video>
<div data-vjs-player>
<video
ref="videoPlayer"
class="video-js vjs-default-skin vjs-big-play-centered w-full rounded-lg"
controls
preload="auto"
:poster="posterUrl"
playsinline
>
<source :src="videoUrl" />
<p class="vjs-no-js">
Pour voir cette vidéo, activez JavaScript et considérez passer à un navigateur web qui
<a href="https://videojs.com/html5-video-support/" target="_blank">supporte la vidéo HTML5</a>.
</p>
</video>
</div>
</div>
<!-- Video Stats -->
<div class="mt-4 flex items-center justify-between text-sm text-gray-600">
<div class="flex items-center space-x-4">
<span class="flex items-center">
<Eye class="w-4 h-4 mr-1" />
{{ viewsCount }} vue{{ viewsCount > 1 ? 's' : '' }}
</span>
<div class="flex items-center space-x-3">
<span class="flex items-center" title="Vues uniques">
<Eye class="w-4 h-4 mr-1" />
{{ viewsCount }}
</span>
<span class="flex items-center" title="Replays">
<RotateCcw class="w-4 h-4 mr-1" />
{{ replaysCount }}
</span>
</div>
<span class="flex items-center">
<Clock class="w-4 h-4 mr-1" />
{{ formatDuration(duration) }}
@@ -57,7 +65,7 @@
import { ref, onMounted, onBeforeUnmount, watch, computed } from 'vue'
import videojs from 'video.js'
import 'video.js/dist/video-js.css'
import { Eye, Clock, Heart, MessageSquare } from 'lucide-vue-next'
import { Eye, Clock, Heart, MessageSquare, RotateCcw } from 'lucide-vue-next'
import { getMediaUrl } from '@/utils/axios'
const props = defineProps({
@@ -85,6 +93,10 @@ const props = defineProps({
type: Number,
default: 0
},
replaysCount: {
type: Number,
default: 0
},
likesCount: {
type: Number,
default: 0
@@ -103,6 +115,7 @@ const emit = defineEmits(['like', 'toggle-comments'])
const videoPlayer = ref(null)
const player = ref(null)
const currentVideoSrc = ref(null) // Track la source actuelle pour éviter les rechargements inutiles
// Computed properties pour les URLs
const videoUrl = computed(() => getMediaUrl(props.src))
@@ -124,16 +137,62 @@ function toggleComments() {
emit('toggle-comments')
}
// Fonction pour gérer les raccourcis clavier manuellement
function handleKeydown(e) {
if (!player.value) return;
// Ignorer si l'utilisateur tape dans un input
if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) return;
switch(e.key) {
case 'ArrowLeft':
e.preventDefault();
player.value.currentTime(Math.max(0, player.value.currentTime() - 10));
break;
case 'ArrowRight':
e.preventDefault();
player.value.currentTime(Math.min(player.value.duration(), player.value.currentTime() + 10));
break;
case ' ':
case 'Space': // Espace pour pause/play
e.preventDefault();
if (player.value.paused()) {
player.value.play();
} else {
player.value.pause();
}
break;
}
}
onMounted(() => {
if (videoPlayer.value) {
player.value = videojs(videoPlayer.value, {
// Options de base pour Video.js
const options = {
controls: true,
fluid: true,
responsive: true,
playbackRates: [0.5, 0.75, 1, 1.25, 1.5, 2],
// Désactiver les hotkeys natifs qui causent l'erreur passive listener
userActions: {
hotkeys: false
},
html5: {
vhs: {
overrideNative: true
},
nativeAudioTracks: false,
nativeVideoTracks: false
},
controlBar: {
skipButtons: {
forward: 10,
backward: 10
},
children: [
'playToggle',
'skipBackward',
'skipForward',
'volumePanel',
'currentTimeDisplay',
'timeDivider',
@@ -143,12 +202,23 @@ onMounted(() => {
'fullscreenToggle'
]
}
})
};
player.value = videojs(videoPlayer.value, options);
// Définir la source initiale après l'initialisation
if (videoUrl.value) {
player.value.src({ src: videoUrl.value, type: 'video/mp4' })
currentVideoSrc.value = videoUrl.value
}
// Error handling
player.value.on('error', (error) => {
console.error('Video.js error:', error)
})
// Ajouter l'écouteur d'événements clavier global
document.addEventListener('keydown', handleKeydown)
}
})
@@ -156,20 +226,39 @@ onBeforeUnmount(() => {
if (player.value) {
player.value.dispose()
}
// Retirer l'écouteur
document.removeEventListener('keydown', handleKeydown)
})
// Watch for src changes to reload video
watch(() => props.src, () => {
if (player.value && videoUrl.value) {
player.value.src({ src: videoUrl.value, type: 'video/mp4' })
// Watch for src changes to reload video - amélioré pour éviter les rechargements inutiles
watch(() => videoUrl.value, (newUrl, oldUrl) => {
// Ne recharger que si l'URL a vraiment changé et que le player est prêt
if (player.value && newUrl && newUrl !== currentVideoSrc.value) {
const wasPlaying = !player.value.paused()
const currentTime = player.value.currentTime()
player.value.src({ src: newUrl, type: 'video/mp4' })
player.value.load()
currentVideoSrc.value = newUrl
// Restaurer la position si possible (optionnel)
player.value.ready(() => {
if (currentTime > 0 && currentTime < player.value.duration()) {
player.value.currentTime(currentTime)
}
if (wasPlaying) {
player.value.play().catch(() => {})
}
})
}
})
}, { immediate: false })
</script>
<style scoped>
.video-js {
aspect-ratio: 16/9;
/* Fix pour l'erreur "passive event listener" sur certains navigateurs */
touch-action: manipulation;
}
.video-js .vjs-big-play-button {

View File

@@ -122,12 +122,24 @@
</div>
</transition>
</div>
<!-- Mobile menu button -->
<div class="flex items-center sm:hidden ml-4">
<button
@click="isMobileMenuOpen = !isMobileMenuOpen"
class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary-500"
>
<span class="sr-only">Open main menu</span>
<Menu v-if="!isMobileMenuOpen" class="block h-6 w-6" aria-hidden="true" />
<X v-else class="block h-6 w-6" aria-hidden="true" />
</button>
</div>
</div>
</div>
</div>
<!-- Mobile menu -->
<div class="sm:hidden">
<div class="sm:hidden border-t border-gray-200" v-show="isMobileMenuOpen">
<div class="pt-2 pb-3 space-y-1">
<router-link
v-for="item in navigation"
@@ -229,7 +241,8 @@ import {
Bell,
User,
ChevronDown,
X
X,
Menu
} from 'lucide-vue-next'
import UserAvatar from '@/components/UserAvatar.vue'
import TicketFloatingButton from '@/components/TicketFloatingButton.vue'
@@ -247,6 +260,7 @@ const navigation = [
const showUserMenu = ref(false)
const showNotifications = ref(false)
const isMobileMenuOpen = ref(false)
const user = computed(() => authStore.user)
const notifications = computed(() => authStore.notifications)

View File

@@ -18,30 +18,30 @@
<div v-else>
<!-- Header -->
<div class="mb-8">
<div class="flex items-center justify-between mb-4">
<div class="flex flex-col sm:flex-row items-start sm:items-center justify-between mb-4 space-y-4 sm:space-y-0">
<router-link to="/albums" class="text-primary-600 hover:text-primary-700 flex items-center">
<ArrowLeft class="w-4 h-4 mr-2" />
Retour aux albums
</router-link>
<div v-if="canEdit" class="flex space-x-2">
<div v-if="canEdit" class="flex flex-wrap gap-2 w-full sm:w-auto">
<button
@click="showEditModal = true"
class="btn-secondary"
class="flex-1 sm:flex-none btn-secondary justify-center"
>
<Edit class="w-4 h-4 mr-2" />
Modifier
</button>
<button
@click="showUploadModal = true"
class="btn-primary"
class="flex-1 sm:flex-none btn-primary justify-center"
>
<Upload class="w-4 h-4 mr-2" />
Ajouter des médias
Ajouter
</button>
<button
@click="deleteAlbum"
class="btn-secondary text-accent-600 hover:text-accent-700 hover:border-accent-300"
class="flex-1 sm:flex-none btn-secondary text-accent-600 hover:text-accent-700 hover:border-accent-300 justify-center"
>
<Trash2 class="w-4 h-4 mr-2" />
Supprimer
@@ -49,9 +49,9 @@
</div>
</div>
<div class="flex items-start space-x-6">
<div class="flex flex-col md:flex-row items-start space-y-6 md:space-y-0 md:space-x-6">
<!-- Cover Image -->
<div class="w-64 h-48 bg-gradient-to-br from-primary-400 to-primary-600 rounded-xl flex items-center justify-center">
<div class="w-full md:w-64 h-48 bg-gradient-to-br from-primary-400 to-primary-600 rounded-xl flex items-center justify-center flex-shrink-0">
<Image v-if="!album.cover_image" class="w-16 h-16 text-white" />
<img
v-else

View File

@@ -1,14 +1,14 @@
<template>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Header -->
<div class="flex justify-between items-center mb-8">
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 sm:mb-8 space-y-4 sm:space-y-0">
<div>
<h1 class="text-3xl font-bold text-gray-900">Albums photos</h1>
<p class="text-gray-600 mt-1">Partagez vos souvenirs en photos et vidéos</p>
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900">Albums photos</h1>
<p class="text-sm sm:text-base text-gray-600 mt-1">Partagez vos souvenirs en photos et vidéos</p>
</div>
<button
@click="showCreateModal = true"
class="btn-primary"
class="w-full sm:w-auto btn-primary justify-center"
>
<Plus class="w-4 h-4 mr-2" />
Nouvel album

View File

@@ -18,23 +18,23 @@
<div v-else>
<!-- Header -->
<div class="mb-8">
<div class="flex items-center justify-between mb-4">
<div class="flex flex-col sm:flex-row items-start sm:items-center justify-between mb-4 space-y-4 sm:space-y-0">
<router-link to="/events" class="text-primary-600 hover:text-primary-700 flex items-center">
<ArrowLeft class="w-4 h-4 mr-2" />
Retour aux événements
</router-link>
<div v-if="canEdit" class="flex space-x-2">
<div v-if="canEdit" class="flex w-full sm:w-auto space-x-2">
<button
@click="showEditModal = true"
class="btn-secondary"
class="flex-1 sm:flex-none btn-secondary justify-center"
>
<Edit class="w-4 h-4 mr-2" />
Modifier
</button>
<button
@click="deleteEvent"
class="btn-secondary text-accent-600 hover:text-accent-700 hover:border-accent-300"
class="flex-1 sm:flex-none btn-secondary text-accent-600 hover:text-accent-700 hover:border-accent-300 justify-center"
>
<Trash2 class="w-4 h-4 mr-2" />
Supprimer
@@ -42,9 +42,9 @@
</div>
</div>
<div class="flex items-start space-x-6">
<div class="flex flex-col md:flex-row items-start space-y-6 md:space-y-0 md:space-x-6">
<!-- Cover Image -->
<div class="w-64 h-48 bg-gradient-to-br from-primary-400 to-primary-600 rounded-xl flex items-center justify-center">
<div class="w-full md:w-64 h-48 bg-gradient-to-br from-primary-400 to-primary-600 rounded-xl flex items-center justify-center flex-shrink-0">
<img
v-if="!event.cover_image && event.creator_avatar"
:src="getMediaUrl(event.creator_avatar)"
@@ -122,25 +122,6 @@
<p class="text-gray-700 whitespace-pre-wrap">{{ event.description }}</p>
</div>
<!-- Map Section -->
<div v-if="event.latitude && event.longitude" class="card p-6 mb-8">
<h2 class="text-xl font-semibold text-gray-900 mb-4">Localisation</h2>
<div class="bg-gray-100 rounded-lg p-6 h-48 flex items-center justify-center">
<div class="text-center text-gray-600">
<MapPin class="w-12 h-12 mx-auto mb-2 text-primary-600" />
<p class="text-sm">Carte interactive</p>
<p class="text-xs mt-1">{{ event.latitude }}, {{ event.longitude }}</p>
<a
:href="`https://www.openstreetmap.org/?mlat=${event.latitude}&mlon=${event.longitude}&zoom=15`"
target="_blank"
class="text-primary-600 hover:underline text-sm mt-2 inline-block"
>
Voir sur OpenStreetMap
</a>
</div>
</div>
</div>
<!-- My Participation -->
<div class="card p-6 mb-8">
<h2 class="text-xl font-semibold text-gray-900 mb-4">Ma participation</h2>

View File

@@ -1,14 +1,14 @@
<template>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Header -->
<div class="flex justify-between items-center mb-8">
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 sm:mb-8 space-y-4 sm:space-y-0">
<div>
<h1 class="text-3xl font-bold text-gray-900">Événements</h1>
<p class="text-gray-600 mt-1">Organisez et participez aux événements du groupe</p>
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900">Événements</h1>
<p class="text-sm sm:text-base text-gray-600 mt-1">Organisez et participez aux événements du groupe</p>
</div>
<button
@click="showCreateModal = true"
class="btn-primary"
class="w-full sm:w-auto btn-primary justify-center"
>
<Plus class="w-4 h-4 mr-2" />
Nouvel événement
@@ -214,51 +214,6 @@
>
</div>
<!-- Map Section -->
<div>
<label class="label">Coordonnées géographiques (optionnel)</label>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="text-sm text-gray-600">Latitude</label>
<input
v-model="newEvent.latitude"
type="number"
step="0.000001"
class="input"
placeholder="48.8566"
>
</div>
<div>
<label class="text-sm text-gray-600">Longitude</label>
<input
v-model="newEvent.longitude"
type="number"
step="0.000001"
class="input"
placeholder="2.3522"
>
</div>
</div>
<!-- Map Preview -->
<div v-if="newEvent.latitude && newEvent.longitude" class="mt-3">
<div class="bg-gray-100 rounded-lg p-4 h-32 flex items-center justify-center">
<div class="text-center text-gray-600">
<MapPin class="w-8 h-8 mx-auto mb-2 text-primary-600" />
<p class="text-sm">Localisation sélectionnée</p>
<p class="text-xs mt-1">{{ newEvent.latitude }}, {{ newEvent.longitude }}</p>
<a
:href="`https://www.openstreetmap.org/?mlat=${newEvent.latitude}&mlon=${newEvent.longitude}&zoom=15`"
target="_blank"
class="text-primary-600 hover:underline text-xs mt-2 inline-block"
>
Voir sur OpenStreetMap
</a>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="label">Date et heure</label>
@@ -339,8 +294,6 @@ const newEvent = ref({
description: '',
date: '',
location: '',
latitude: null,
longitude: null,
end_date: null
})
@@ -453,8 +406,6 @@ async function createEvent() {
description: newEvent.value.description,
date: new Date(newEvent.value.date).toISOString(),
location: newEvent.value.location,
latitude: newEvent.value.latitude,
longitude: newEvent.value.longitude,
end_date: newEvent.value.end_date ? new Date(newEvent.value.end_date).toISOString() : null
}
@@ -478,8 +429,6 @@ function resetForm() {
description: '',
date: '',
location: '',
latitude: null,
longitude: null,
end_date: null
}
}

View File

@@ -1,5 +1,5 @@
<template>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 sm:py-8">
<!-- Welcome Section -->
<div class="mb-8">
<h1 class="text-3xl font-bold text-gray-900 mb-2">
@@ -9,8 +9,8 @@
</div>
<!-- Quick Stats -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
<div class="card p-6">
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
<div class="card p-4 sm:p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">Prochain événement</p>
@@ -23,7 +23,7 @@
</div>
</div>
<div class="card p-6">
<div class="card p-4 sm:p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">Taux de présence</p>
@@ -33,7 +33,7 @@
</div>
</div>
<div class="card p-6">
<div class="card p-4 sm:p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">Nouveaux posts</p>
@@ -44,7 +44,7 @@
</div>
</div>
<div class="card p-6">
<div class="card p-4 sm:p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">Membres actifs</p>
@@ -59,17 +59,17 @@
<!-- Recent Posts -->
<div class="lg:col-span-2">
<div class="card">
<div class="p-6 border-b border-gray-100">
<div class="p-4 sm:p-6 border-b border-gray-100">
<h2 class="text-lg font-semibold text-gray-900">Publications récentes</h2>
</div>
<div class="divide-y divide-gray-100">
<div v-if="posts.length === 0" class="p-6 text-center text-gray-500">
<div v-if="posts.length === 0" class="p-4 sm:p-6 text-center text-gray-500">
Aucune publication récente
</div>
<div
v-for="post in posts"
:key="post.id"
class="p-6 hover:bg-gray-50 transition-colors"
class="p-4 sm:p-6 hover:bg-gray-50 transition-colors"
>
<div class="flex items-start space-x-3">
<img
@@ -133,11 +133,11 @@
<!-- Upcoming Events -->
<div>
<div class="card">
<div class="p-6 border-b border-gray-100">
<div class="p-4 sm:p-6 border-b border-gray-100">
<h2 class="text-lg font-semibold text-gray-900">Événements à venir</h2>
</div>
<div class="divide-y divide-gray-100">
<div v-if="upcomingEvents.length === 0" class="p-6 text-center text-gray-500">
<div v-if="upcomingEvents.length === 0" class="p-4 sm:p-6 text-center text-gray-500">
Aucun événement prévu
</div>
<router-link
@@ -171,11 +171,11 @@
<!-- Recent Vlogs -->
<div class="card mt-6">
<div class="p-6 border-b border-gray-100">
<div class="p-4 sm:p-6 border-b border-gray-100">
<h2 class="text-lg font-semibold text-gray-900">Derniers vlogs</h2>
</div>
<div class="divide-y divide-gray-100">
<div v-if="recentVlogs.length === 0" class="p-6 text-center text-gray-500">
<div v-if="recentVlogs.length === 0" class="p-4 sm:p-6 text-center text-gray-500">
Aucun vlog récent
</div>
<router-link

View File

@@ -1,14 +1,14 @@
<template>
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Header -->
<div class="flex justify-between items-center mb-8">
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 sm:mb-8 space-y-4 sm:space-y-0">
<div>
<h1 class="text-3xl font-bold text-gray-900">Publications</h1>
<p class="text-gray-600 mt-1">Partagez vos moments avec le groupe</p>
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900">Publications</h1>
<p class="text-sm sm:text-base text-gray-600 mt-1">Partagez vos moments avec le groupe</p>
</div>
<button
@click="showCreateModal = true"
class="btn-primary"
class="w-full sm:w-auto btn-primary justify-center"
>
<Plus class="w-4 h-4 mr-2" />
Nouvelle publication
@@ -16,7 +16,7 @@
</div>
<!-- Create Post Form -->
<div class="card p-6 mb-8">
<div class="card p-4 sm:p-6 mb-8">
<div class="flex items-start space-x-3">
<img
v-if="user?.avatar_url"
@@ -99,7 +99,7 @@
<div
v-for="post in posts"
:key="post.id"
class="card p-6"
class="card p-4 sm:p-6"
>
<!-- Post Header -->
<div class="flex items-start space-x-3 mb-4">

View File

@@ -18,23 +18,23 @@
<div v-else>
<!-- Header -->
<div class="mb-8">
<div class="flex items-center justify-between mb-4">
<div class="flex flex-col sm:flex-row items-start sm:items-center justify-between mb-4 space-y-4 sm:space-y-0">
<router-link to="/vlogs" class="text-primary-600 hover:text-primary-700 flex items-center">
<ArrowLeft class="w-4 h-4 mr-2" />
Retour aux vlogs
</router-link>
<div v-if="canEdit" class="flex space-x-2">
<div v-if="canEdit" class="flex w-full sm:w-auto space-x-2">
<button
@click="showEditModal = true"
class="btn-secondary"
class="flex-1 sm:flex-none btn-secondary justify-center"
>
<Edit class="w-4 h-4 mr-2" />
Modifier
</button>
<button
@click="deleteVlog"
class="btn-secondary text-accent-600 hover:text-accent-700 hover:border-accent-300"
class="flex-1 sm:flex-none btn-secondary text-accent-600 hover:text-accent-700 hover:border-accent-300 justify-center"
>
<Trash2 class="w-4 h-4 mr-2" />
Supprimer
@@ -42,9 +42,9 @@
</div>
</div>
<h1 class="text-4xl font-bold text-gray-900 mb-4">{{ vlog.title }}</h1>
<h1 class="text-2xl sm:text-4xl font-bold text-gray-900 mb-4">{{ vlog.title }}</h1>
<div class="flex items-center space-x-6 text-gray-600 mb-6">
<div class="flex flex-wrap items-center gap-4 text-sm sm:text-base text-gray-600 mb-6">
<div class="flex items-center">
<img
v-if="vlog.author_avatar"
@@ -52,10 +52,10 @@
:alt="vlog.author_name"
class="w-8 h-8 rounded-full object-cover mr-3"
>
<div v-else class="w-8 h-8 rounded-full bg-primary-100 flex items-center justify-center mr-3">
<div v-else class="w-8 h-8 rounded-full bg-primary-100 flex items-center justify-center mr-2">
<User class="w-4 h-4 text-primary-600" />
</div>
<span>Par {{ vlog.author_name }}</span>
<span>{{ vlog.author_name }}</span>
</div>
<div class="flex items-center">
@@ -67,6 +67,11 @@
<Eye class="w-4 h-4 mr-2" />
<span>{{ vlog.views_count }} vue{{ vlog.views_count > 1 ? 's' : '' }}</span>
</div>
<div class="flex items-center">
<RotateCcw class="w-4 h-4 mr-2" />
<span>{{ vlog.replays_count }} replay{{ vlog.replays_count > 1 ? 's' : '' }}</span>
</div>
<div v-if="vlog.duration" class="flex items-center">
<Clock class="w-4 h-4 mr-2" />
@@ -76,7 +81,7 @@
</div>
<!-- Video Player -->
<div class="card p-6 mb-8">
<div class="card p-0 sm:p-6 mb-8 overflow-hidden sm:overflow-visible">
<VideoPlayer
:src="vlog.video_url"
:poster="vlog.thumbnail_url"
@@ -84,6 +89,7 @@
:description="vlog.description"
:duration="vlog.duration"
:views-count="vlog.views_count"
:replays-count="vlog.replays_count"
:likes-count="vlog.likes_count"
:comments-count="vlog.comments?.length || 0"
:is-liked="vlog.is_liked"
@@ -93,13 +99,13 @@
</div>
<!-- Description -->
<div v-if="vlog.description" class="card p-6 mb-8">
<div v-if="vlog.description" class="card p-4 sm:p-6 mb-8">
<h2 class="text-xl font-semibold text-gray-900 mb-4">Description</h2>
<p class="text-gray-700 whitespace-pre-wrap">{{ vlog.description }}</p>
</div>
<!-- Comments Section -->
<div class="card p-6 mb-8">
<div class="card p-4 sm:p-6 mb-8">
<VlogComments
:vlog-id="vlog.id"
:comments="vlog.comments || []"
@@ -233,7 +239,8 @@ import {
Edit,
Trash2,
Film,
Play
Play,
RotateCcw
} from 'lucide-vue-next'
import VideoPlayer from '@/components/VideoPlayer.vue'
import VlogComments from '@/components/VlogComments.vue'
@@ -279,8 +286,9 @@ function formatDuration(seconds) {
async function toggleLike() {
try {
const response = await axios.post(`/api/vlogs/${vlog.value.id}/like`)
// Refresh vlog data to get updated like count
await fetchVlog()
// Update local state without refreshing full vlog data (avoiding view increment)
vlog.value.likes_count = response.data.likes_count
vlog.value.is_liked = !vlog.value.is_liked
toast.success(response.data.message)
} catch (error) {
toast.error('Erreur lors de la mise à jour du like')

View File

@@ -1,14 +1,14 @@
<template>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Header -->
<div class="flex justify-between items-center mb-8">
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 sm:mb-8 space-y-4 sm:space-y-0">
<div>
<h1 class="text-3xl font-bold text-gray-900">Vlogs</h1>
<p class="text-gray-600 mt-1">Partagez vos moments en vidéo avec le groupe</p>
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900">Vlogs</h1>
<p class="text-sm sm:text-base text-gray-600 mt-1">Partagez vos moments en vidéo avec le groupe</p>
</div>
<button
@click="showCreateModal = true"
class="btn-primary"
class="w-full sm:w-auto btn-primary justify-center"
>
<Plus class="w-4 h-4 mr-2" />
Nouveau vlog