feat(chat): per-chat drafts + fix emoji picker closing on tab switch

- Unsent text is saved per conversation (survives switching chats and reloads) and restored
  when you reopen the chat; cleared on send. (#1)
- Emoji picker: stopPropagation on tab/grid clicks so switching category no longer closes it
  (the re-render was detaching the clicked node -> outside-click handler fired). (#6)
build batch23.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 11:55:14 +05:30
parent 2b27889e07
commit a33ed27ffe
+12 -5
View File
@@ -731,7 +731,7 @@
</head>
<body>
<script src="/icons.js?v=4"></script>
<script>window.__BUILD='2026-07-01-batch22';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);</script>
<script>window.__BUILD='2026-07-02-batch23';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);</script>
<div class="loading" id="loading">Loading…</div>
<header>
@@ -1316,10 +1316,12 @@ function positionEmojiPop(pop, anchorEl){
function closeEmoji(){ const pop=document.getElementById('emojiPop'); if(pop) pop.style.display='none'; emojiOpen=false; }
function renderEmojiPop(pop){
pop.innerHTML='<div class="emoji-tabs">'+EMOJI_CATS.map((c,i)=>'<button type="button" data-i="'+i+'" class="'+(i===emojiCat?'active':'')+'">'+c.icon+'</button>').join('')+'</div><div class="emoji-grid" id="emojiGrid"></div>';
pop.querySelectorAll('.emoji-tabs button').forEach(b=>b.onclick=()=>{ emojiCat=+b.dataset.i; renderEmojiPop(pop); });
// stopPropagation: switching tabs re-renders the pop (detaching the clicked button), which would
// make the outside-click handler think the click was outside and close the picker.
pop.querySelectorAll('.emoji-tabs button').forEach(b=>b.onclick=(ev)=>{ ev.stopPropagation(); emojiCat=+b.dataset.i; renderEmojiPop(pop); });
const grid=pop.querySelector('#emojiGrid');
grid.innerHTML=EMOJI_CATS[emojiCat].list.trim().split(/\s+/).map(e=>'<button type="button">'+e+'</button>').join('');
grid.querySelectorAll('button').forEach(b=>b.onclick=()=>onEmojiPick(b.textContent));
grid.querySelectorAll('button').forEach(b=>b.onclick=(ev)=>{ ev.stopPropagation(); onEmojiPick(b.textContent); });
}
function onEmojiPick(e){ if(emojiMode && emojiMode.react){ reactToMessage(emojiMode.react, e); closeEmoji(); } else { insertEmoji(e); } }
function openEmojiForReact(messageId, anchorEl){ openEmoji({ react: messageId }, anchorEl); }
@@ -1534,6 +1536,10 @@ function appendBubble(m){
box.scrollTop=box.scrollHeight;
}
let _openUnread=0; // set by selectChat (the unread count before it's reset) so we can open at the first unread
// Per-conversation drafts: unsent text is kept (across chat switches AND reloads) until sent.
function draftKey(kind,id){ return 'draft_'+((ME&&ME.id)||'')+'_'+kind+':'+id; }
function getDraft(kind,id){ try{ return localStorage.getItem(draftKey(kind,id))||''; }catch(_){ return ''; } }
function setDraft(kind,id,val){ try{ if(val&&val.trim()) localStorage.setItem(draftKey(kind,id), val); else localStorage.removeItem(draftKey(kind,id)); }catch(_){} }
async function openConvo(kind,id){
const it=rowFor(kind,id)||{kind,id,name:'Conversation'};
const _unreadN=_openUnread; _openUnread=0; // consume the captured unread count (#3)
@@ -1555,8 +1561,9 @@ async function openConvo(kind,id){
const pb=document.getElementById('pollBtn'); if(pb) pb.onclick=()=>openPollModal(id);
const inpEl=document.getElementById('msgInput');
if(inpEl){
const _d=getDraft(kind,id); if(_d){ inpEl.value=_d; autoGrow(inpEl); } // restore unsent draft
inpEl.addEventListener('paste', onPaste);
inpEl.addEventListener('input', ()=>autoGrow(inpEl));
inpEl.addEventListener('input', ()=>{ autoGrow(inpEl); setDraft(kind,id,inpEl.value); }); // keep the draft in sync
inpEl.addEventListener('keydown', (e)=>{ if(e.key==='Enter' && !e.shiftKey){ if(mentionItems && mentionItems.length) return; e.preventDefault(); sendMessage(); } }); // Enter sends, Shift+Enter = newline
}
const ci=document.getElementById('convoInfo'); if(ci) ci.onclick=()=>openGroupInfo(id);
@@ -1774,7 +1781,7 @@ async function selectChat(kind,id){
async function sendMessage(){
const inp=document.getElementById('msgInput'); if(!inp) return;
const text=inp.value.trim(); if((!text&&!pendingAttach)||!selected) return;
inp.value=''; inp.style.height='auto';
inp.value=''; inp.style.height='auto'; setDraft(selected.kind, selected.id, ''); // sent → clear the draft
const replyTo=replyTarget?replyTarget.id:null;
const attachmentId=pendingAttach?pendingAttach.id:null;
const payload = selected.kind==='group' ? { group:selected.id, body:text, replyTo, attachmentId, mentions:collectMentions(text) } : { to:selected.id, body:text, replyTo, attachmentId };