Added Chat Page
This commit is contained in:
169
components/MessageNotifications.tsx
Normal file
169
components/MessageNotifications.tsx
Normal file
@@ -0,0 +1,169 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import useSWR from 'swr';
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
name: string | null;
|
||||
}
|
||||
|
||||
interface LastMessage {
|
||||
id: string;
|
||||
content: string | null;
|
||||
senderId: string;
|
||||
senderName: string;
|
||||
createdAt: string;
|
||||
hasFiles: boolean;
|
||||
}
|
||||
|
||||
interface Conversation {
|
||||
id: string;
|
||||
name: string | null;
|
||||
type: string;
|
||||
displayName: string;
|
||||
participants: Array<{ id: string; email: string; name: string | null }>;
|
||||
lastMessage: LastMessage | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
const fetcher = (url: string) => fetch(url).then((res) => res.json());
|
||||
|
||||
export default function MessageNotifications() {
|
||||
const { data: currentUser } = useSWR<User>('/api/auth/me', fetcher);
|
||||
const { data: conversations } = useSWR<Conversation[]>(
|
||||
currentUser ? '/api/conversations' : null,
|
||||
fetcher,
|
||||
{
|
||||
refreshInterval: 3000, // Vérifier toutes les 3 secondes
|
||||
}
|
||||
);
|
||||
|
||||
const lastMessageIdsRef = useRef<Map<string, string>>(new Map());
|
||||
const initializedRef = useRef(false);
|
||||
|
||||
// Fonction pour jouer le son de notification
|
||||
const playNotificationSound = () => {
|
||||
try {
|
||||
const AudioContext = window.AudioContext || (window as any).webkitAudioContext;
|
||||
const audioContext = new AudioContext();
|
||||
const oscillator = audioContext.createOscillator();
|
||||
const gainNode = audioContext.createGain();
|
||||
|
||||
oscillator.connect(gainNode);
|
||||
gainNode.connect(audioContext.destination);
|
||||
|
||||
// Son de notification agréable (deux tons)
|
||||
oscillator.frequency.value = 800;
|
||||
oscillator.type = 'sine';
|
||||
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.2);
|
||||
|
||||
oscillator.start(audioContext.currentTime);
|
||||
oscillator.stop(audioContext.currentTime + 0.2);
|
||||
|
||||
// Deuxième ton après une courte pause
|
||||
setTimeout(() => {
|
||||
const oscillator2 = audioContext.createOscillator();
|
||||
const gainNode2 = audioContext.createGain();
|
||||
|
||||
oscillator2.connect(gainNode2);
|
||||
gainNode2.connect(audioContext.destination);
|
||||
|
||||
oscillator2.frequency.value = 1000;
|
||||
oscillator2.type = 'sine';
|
||||
gainNode2.gain.setValueAtTime(0.3, audioContext.currentTime);
|
||||
gainNode2.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.2);
|
||||
|
||||
oscillator2.start(audioContext.currentTime);
|
||||
oscillator2.stop(audioContext.currentTime + 0.2);
|
||||
}, 150);
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la lecture du son:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Demander la permission pour les notifications
|
||||
useEffect(() => {
|
||||
if ('Notification' in window && Notification.permission === 'default') {
|
||||
Notification.requestPermission().catch(console.error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Initialiser les IDs des messages au premier chargement
|
||||
useEffect(() => {
|
||||
if (conversations && !initializedRef.current) {
|
||||
conversations.forEach((conversation) => {
|
||||
if (conversation.lastMessage) {
|
||||
lastMessageIdsRef.current.set(conversation.id, conversation.lastMessage.id);
|
||||
}
|
||||
});
|
||||
initializedRef.current = true;
|
||||
}
|
||||
}, [conversations]);
|
||||
|
||||
// Détecter les nouveaux messages
|
||||
useEffect(() => {
|
||||
if (!conversations || !currentUser || !initializedRef.current) return;
|
||||
|
||||
conversations.forEach((conversation) => {
|
||||
if (!conversation.lastMessage) return;
|
||||
|
||||
const lastMessageId = lastMessageIdsRef.current.get(conversation.id);
|
||||
const currentMessageId = conversation.lastMessage.id;
|
||||
|
||||
// Nouveau message détecté
|
||||
if (lastMessageId && lastMessageId !== currentMessageId) {
|
||||
// Vérifier si le message n'est pas de l'utilisateur actuel
|
||||
if (conversation.lastMessage.senderId !== currentUser.id) {
|
||||
// Vérifier si on est sur la page de messagerie
|
||||
const isOnMessagingPage = window.location.pathname === '/dashboard/messagerie';
|
||||
|
||||
// Jouer le son seulement si on n'est pas sur la page de messagerie
|
||||
// (sur la page de messagerie, on voit déjà les messages en temps réel)
|
||||
if (!isOnMessagingPage) {
|
||||
playNotificationSound();
|
||||
}
|
||||
|
||||
// Afficher la notification si on n'est pas sur la page de messagerie
|
||||
if (!isOnMessagingPage && 'Notification' in window && Notification.permission === 'granted') {
|
||||
const messageContent = conversation.lastMessage.hasFiles && !conversation.lastMessage.content
|
||||
? '📎 Fichier'
|
||||
: conversation.lastMessage.content || '📎 Fichier';
|
||||
|
||||
const notification = new Notification(
|
||||
conversation.type === 'group' ? conversation.displayName : conversation.lastMessage.senderName,
|
||||
{
|
||||
body: messageContent.length > 100
|
||||
? messageContent.substring(0, 100) + '...'
|
||||
: messageContent,
|
||||
icon: '/logo.svg',
|
||||
badge: '/logo.svg',
|
||||
tag: conversation.id, // Permet de remplacer les notifications de la même conversation
|
||||
requireInteraction: false,
|
||||
}
|
||||
);
|
||||
|
||||
notification.onclick = () => {
|
||||
window.focus();
|
||||
window.location.href = '/dashboard/messagerie';
|
||||
notification.close();
|
||||
};
|
||||
|
||||
// Fermer automatiquement après 5 secondes
|
||||
setTimeout(() => {
|
||||
notification.close();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour l'ID du dernier message
|
||||
lastMessageIdsRef.current.set(conversation.id, currentMessageId);
|
||||
});
|
||||
}, [conversations, currentUser]);
|
||||
|
||||
return null; // Ce composant ne rend rien visuellement
|
||||
}
|
||||
Reference in New Issue
Block a user