"use client"; import { useEffect, useState, useRef } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { RefreshCw, MessageSquare, Loader2 } from "lucide-react"; import { useRouter } from "next/navigation"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { signIn, useSession } from "next-auth/react"; import { useWidgetNotification } from "@/hooks/use-widget-notification"; import { useUnifiedRefresh } from "@/hooks/use-unified-refresh"; import { REFRESH_INTERVALS } from "@/lib/constants/refresh-intervals"; import { Badge } from "@/components/ui/badge"; interface Message { id: string; text: string; timestamp: string; rawTimestamp: string; roomName: string; roomType: string; sender: { _id: string; username: string; name: string; initials: string; color: string; }; isOwnMessage: boolean; room: { id: string; type: string; name: string; isChannel: boolean; isPrivateGroup: boolean; isDirect: boolean; link: string; }; } export function Parole() { const [messages, setMessages] = useState([]); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [unreadCount, setUnreadCount] = useState(0); const router = useRouter(); const { data: session, status } = useSession(); const { triggerNotification } = useWidgetNotification(); const lastUnreadCountRef = useRef(-1); // Initialize to -1 to detect first load const lastMessageIdsRef = useRef>(new Set()); const isInitializedRef = useRef(false); const fetchMessages = async (forceRefresh = false) => { // Only show loading spinner on initial load, not on auto-refresh if (!messages.length) { setLoading(true); } setRefreshing(true); setError(null); try { const response = await fetch('/api/rocket-chat/messages' + (forceRefresh ? '?refresh=true' : ''), { cache: 'no-store', next: { revalidate: 0 }, credentials: 'include', }); if (!response.ok) { // Check if response is JSON before trying to parse const contentType = response.headers.get('content-type') || ''; if (contentType.includes('application/json')) { const errorData = await response.json(); throw new Error(errorData.error || 'Failed to fetch messages'); } else { // Response is HTML (probably an error page) const errorText = await response.text(); console.error('[Parole Widget] Received HTML instead of JSON', { status: response.status, statusText: response.statusText, preview: errorText.substring(0, 200), }); throw new Error(`Server returned error page (${response.status})`); } } // Check if response is JSON before parsing const contentType = response.headers.get('content-type') || ''; if (!contentType.includes('application/json')) { const errorText = await response.text(); console.error('[Parole Widget] Expected JSON, got HTML', { contentType, preview: errorText.substring(0, 200), }); throw new Error('Server returned invalid response format'); } const data = await response.json(); if (Array.isArray(data.messages)) { // Utiliser le totalUnreadCount de l'API (plus fiable) const currentUnreadCount = data.totalUnreadCount || 0; const currentMessageIds = new Set(data.messages.map((m: any) => m.id as string)); // Update unread count state for badge display setUnreadCount(currentUnreadCount); // Detect new messages by comparing IDs (more reliable than count) const newMessageIds = new Set( (Array.from(currentMessageIds) as string[]).filter((id: string) => !lastMessageIdsRef.current.has(id)) ); const hasNewMessages = newMessageIds.size > 0; // On initialise au premier chargement if (!isInitializedRef.current) { console.log('[Parole Widget] 💬 Initializing - storing existing message IDs without notifications', { messageCount: data.messages.length, unreadCount: currentUnreadCount, }); lastMessageIdsRef.current = currentMessageIds; lastUnreadCountRef.current = currentUnreadCount; isInitializedRef.current = true; } else { // Update count if it changed if (currentUnreadCount !== lastUnreadCountRef.current) { lastUnreadCountRef.current = currentUnreadCount; } } // Always prepare notification items (messages, max 10) const notificationItems = data.messages .slice(0, 10) .map((msg: any) => ({ id: msg.id, title: msg.sender.name || msg.sender.username, message: msg.text || msg.message || '', link: '/parole', timestamp: new Date(msg.rawTimestamp || msg.timestamp), metadata: { roomName: msg.roomName, roomType: msg.roomType, }, })); // Always trigger notification update to keep count fresh in Redis // This ensures the count doesn't expire even if it hasn't changed await triggerNotification({ source: 'rocketchat', count: currentUnreadCount, items: notificationItems, }); // Dispatch event for Outlook-style notifications (for new messages detected by ID) if (hasNewMessages) { console.log('[Parole Widget] 💬 Dispatching new messages event', { newMessagesCount: newMessageIds.size, newMessageIds: Array.from(newMessageIds), previousCount: lastUnreadCountRef.current, currentCount: currentUnreadCount, previousMessageIds: Array.from(lastMessageIdsRef.current), }); window.dispatchEvent(new CustomEvent('new-messages-detected', { detail: { messages: data.messages, previousCount: lastUnreadCountRef.current, currentCount: currentUnreadCount, } })); } // Always update lastMessageIdsRef to track current state lastMessageIdsRef.current = currentMessageIds; setMessages(data.messages); } else { console.warn('Unexpected data format:', data); setMessages([]); } setError(null); } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Failed to fetch messages'; console.error('[Parole Widget] Error fetching messages', { error: errorMessage, forceRefresh, err, }); setError(errorMessage); } finally { setLoading(false); setRefreshing(false); } }; // Initial fetch on mount useEffect(() => { if (status === 'authenticated') { fetchMessages(false); // Use cache on initial load } }, [status]); // Integrate unified refresh for automatic polling // Use forceRefresh=true to ensure we get the latest messages immediately const { refresh } = useUnifiedRefresh({ resource: 'parole', interval: REFRESH_INTERVALS.PAROLE, // 30 seconds enabled: status === 'authenticated', onRefresh: async () => { // Use forceRefresh to bypass cache and get latest messages immediately await fetchMessages(true); // Force refresh to get new messages immediately }, priority: 'high', }); // Manual refresh handler (bypasses cache) const handleManualRefresh = async (e?: React.MouseEvent) => { if (e) { e.stopPropagation(); } await fetchMessages(true); // Force refresh, bypass cache }; if (status === 'loading') { return ( Parole

Loading...

); } if (status === 'unauthenticated' || (error && error.includes('Session expired'))) { return ( Parole

Please sign in to view messages

); } return ( router.push('/parole')} > Parole {unreadCount > 0 && ( {unreadCount} )} {loading && messages.length === 0 ? (

Chargement des messages...

) : error ? (

{error}

) : (
{messages.length === 0 ? (

Aucun message

) : ( messages.map((message) => (
{message.sender.initials}

{message.sender.name}

{message.timestamp}

{message.text}

{message.roomName && (
{message.room.isChannel ? '#' : message.room.isPrivateGroup ? '🔒' : '💬'} {message.roomName}
)}
)) )}
)}
); }