/* Receiving a message is not evidence of seeing it. No receipts are cached on disk. */ document.querySelectorAll('[data-conversation-workspace][data-read-url]').forEach(workspace=>{ const scroller=workspace.querySelector('[data-conversation]'); if(!scroller)return; const acknowledged=new Set(),tracked=new Set(); let running=false,timer=0,controller=null; const cookie=name=>document.cookie.split('; ').find(row=>row.startsWith(`${name}=`))?.split('=').slice(1).join('=')||''; const visibleIds=()=>{ if(document.visibilityState!=='visible' || workspace.dataset.accessDenied)return []; const bounds=scroller.getBoundingClientRect(); const viewport=window.visualViewport; const viewTop=viewport?.offsetTop||0,viewLeft=viewport?.offsetLeft||0; const top=Math.max(bounds.top,viewTop),bottom=Math.min(bounds.bottom,viewTop+(viewport?.height||innerHeight)); const left=Math.max(bounds.left,viewLeft),right=Math.min(bounds.right,viewLeft+(viewport?.width||innerWidth)); return [...scroller.querySelectorAll('[data-message-id]')].filter(message=>{ if(acknowledged.has(message.dataset.messageId) || message.hasAttribute('data-message-deleted'))return false; const box=message.getBoundingClientRect(); const height=Math.min(box.bottom,bottom)-Math.max(box.top,top); if(box.height<=0 || box.width<=0 || heightmessage.dataset.messageId); }; const schedule=()=>{ clearTimeout(timer); timer=setTimeout(send,250); }; const denyAccess=()=>{ workspace.dataset.accessDenied='true'; delete workspace.dataset.pollUrl; controller?.abort();clearTimeout(timer); scroller.querySelectorAll('[data-load-older]').forEach(button=>window.htmx?.trigger(button,'htmx:abort')); scroller.querySelectorAll('[data-message],.date-divider,.conversation-empty,[data-load-older]').forEach(item=>item.remove()); const notice=document.createElement('p'); notice.className='conversation-empty';notice.textContent='دسترسی به این مکاتبات لغو شده است.'; scroller.append(notice); const composer=workspace.querySelector('[data-composer-shell]');if(composer)composer.hidden=true; const jump=workspace.querySelector('[data-jump-latest]');if(jump)jump.hidden=true; }; async function send(){ if(running)return; const ids=visibleIds();if(!ids.length)return; running=true;controller=new AbortController(); try{ const response=await fetch(workspace.dataset.readUrl,{method:'POST',credentials:'same-origin',cache:'no-store',signal:controller.signal, headers:{'Content-Type':'application/json','X-CSRFToken':decodeURIComponent(cookie('csrftoken'))},body:JSON.stringify({message_ids:ids})}); if(response.redirected || [401,403,404].includes(response.status)){workspace.dispatchEvent(new Event('conversation:access-denied'));return} if(!response.ok)throw new Error(`HTTP ${response.status}`); const data=await response.json(); if(workspace.dataset.accessDenied)return; (data.read_ids||[]).forEach(id=>acknowledged.add(id)); if(typeof data.unread_shares==='number')document.querySelectorAll('[data-share-unread-count]').forEach(badge=>{ badge.textContent=String(data.unread_shares);badge.hidden=!data.unread_shares; }); }catch(error){if(error.name!=='AbortError')console.error('Message read acknowledgement failed',error)} finally{running=false;controller=null;if(visibleIds().length)timer=setTimeout(send,3000)} } const observer=new IntersectionObserver(schedule,{root:scroller,threshold:[0,0.1,0.5,1]}); const observeMessages=()=>{ tracked.forEach(message=>{if(!scroller.contains(message)){observer.unobserve(message);tracked.delete(message)}}); scroller.querySelectorAll('[data-message-id]').forEach(message=>{if(!tracked.has(message)){tracked.add(message);observer.observe(message)}}); schedule(); }; new MutationObserver(observeMessages).observe(scroller,{childList:true,subtree:true}); scroller.addEventListener('scroll',schedule,{passive:true}); new ResizeObserver(schedule).observe(scroller); document.addEventListener('click',schedule); document.addEventListener('keydown',schedule); window.addEventListener('resize',schedule); window.visualViewport?.addEventListener('resize',schedule); document.addEventListener('visibilitychange',schedule); window.addEventListener('pageshow',schedule); workspace.addEventListener('conversation:access-denied',denyAccess); window.addEventListener('pagehide',()=>{clearTimeout(timer);controller?.abort()}); observeMessages(); });