// ChatNotifier.jsx — Global poller للشاتات // بيراقب الشاتات بتاعتى كل 8 ثوانى، ولما رسالة جديدة تيجى من طرف تانى: // - يطلع toast لطيف فى الأسفل-اليمين (Messenger-style) // - الضغط عليه → يفتح ChatModal (يطلق custom event للـ LawyersView) // بيعمل polling حتى لو الـ chat modal مفتوحة (مايضرش) لأنه يقارن بـ last_seen. function ChatNotifier() { const [toasts, setToasts] = React.useState([]); // [{ id, chat, msg }] const seenRef = React.useRef({}); // { chatId: lastMessageId } const initializedRef = React.useRef(false); const audioCtxRef = React.useRef(null); // ─── Helper: نغمة صغيرة لما رسالة تيجى ─── const ding = React.useCallback(() => { try { const AC = window.AudioContext || window.webkitAudioContext; if (!AC) return; if (!audioCtxRef.current) audioCtxRef.current = new AC(); const ctx = audioCtxRef.current; const now = ctx.currentTime; const o = ctx.createOscillator(); const g = ctx.createGain(); o.type = 'sine'; o.frequency.setValueAtTime(880, now); o.frequency.exponentialRampToValueAtTime(1320, now + 0.10); g.gain.setValueAtTime(0, now); g.gain.linearRampToValueAtTime(0.16, now + 0.02); g.gain.linearRampToValueAtTime(0, now + 0.22); o.connect(g).connect(ctx.destination); o.start(now); o.stop(now + 0.24); } catch {} }, []); React.useEffect(() => { let cancel = false; const pollOnce = async () => { if (!window.api?.isAuthenticated || !window.api.isAuthenticated()) return; try { // نجيب شاتاتى كـ عميل (لو محامى يفضّل نضيف role lawyer لاحقاً) const chats = await window.api.chats.list('client'); if (cancel) return; const me = window.__sanadCurrentUser?.id; if (!me) return; for (const c of (chats || [])) { // نتجاهل لو الشات مفتوحة دلوقتى فى ChatModal — تعرف عن طريق window flag if (window.__openChatId === c.id) continue; // ما عندناش msg id جايب من الـ summary — بنعتمد على last_message_at const sig = c.last_message_at || ''; const prev = seenRef.current[c.id]; // أول poll — نسجّل بدون toast (ما نزعجش بكل الرسائل القديمة) if (!initializedRef.current) { seenRef.current[c.id] = sig; continue; } if (sig && sig !== prev && c.unread_count > 0) { // جلب آخر رسالة لمعرفة من اللي بعت try { const detail = await window.api.chats.get(c.id); if (cancel) return; const lastFromOther = [...(detail.messages || [])] .reverse() .find(m => m.sender_id !== me); if (lastFromOther) { setToasts(prev => { // ما نظهرش toast لنفس الـ msg id لو موجود if (prev.some(t => t.msgId === lastFromOther.id)) return prev; return [...prev, { id: Date.now() + Math.random(), msgId: lastFromOther.id, chat: c, msg: lastFromOther, }]; }); ding(); } } catch {} } seenRef.current[c.id] = sig; } if (!initializedRef.current) initializedRef.current = true; } catch { // ignore silently } }; pollOnce(); // immediate const t = setInterval(pollOnce, 8000); return () => { cancel = true; clearInterval(t); }; }, [ding]); // ─── Toast dismissal — auto after 10s ─── React.useEffect(() => { if (toasts.length === 0) return; const timers = toasts.map(t => setTimeout(() => setToasts(prev => prev.filter(x => x.id !== t.id)), 10000) ); return () => timers.forEach(clearTimeout); }, [toasts]); const dismissToast = (id) => setToasts(prev => prev.filter(t => t.id !== id)); const openChat = (toast) => { // نبعت custom event لـ LawyersView يفتح ChatModal مع المحامى ده window.dispatchEvent(new CustomEvent('sanad-open-chat', { detail: { lawyerId: toast.chat.lawyer_id, lawyerName: toast.chat.lawyer_display_name, lawyerAvatar: toast.chat.lawyer_avatar_url, }, })); dismissToast(toast.id); }; if (toasts.length === 0) return null; return (