#14 draft: guarantee the save at leave-time (fixes switch-chats-and-return loss)

Per-keystroke 'input' saves can be missed on mobile (predictive text or a fast
tap-away fires no final input event), which lost the whole draft when you switched
chats and came back. Added persistCurrentDraft() — snapshots the composer value
into the current chat's draft key the instant you leave it (selectChat before the
swap, and showWelcome/back). Restore on open was already correct. Skipped while
editing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 22:39:16 +05:30
parent 6ee424db16
commit 25b0b39c49
+7
View File
@@ -3420,6 +3420,11 @@ let _openUnread=0; // set by selectChat (the unread count before it's reset) so
function draftKey(kind,id){ return 'draft_'+((ME&&ME.id)||'')+'_'+kind+':'+id; } 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 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(_){} } function setDraft(kind,id,val){ try{ if(val&&val.trim()) localStorage.setItem(draftKey(kind,id), val); else localStorage.removeItem(draftKey(kind,id)); }catch(_){} }
// #14: GUARANTEED save of the currently-open chat's draft — called the instant we leave it (switching chats
// OR going back to the list). The per-keystroke 'input' save can be missed on mobile (predictive text / a
// fast tap-away fires no final input event), which lost the whole draft on switch-and-return. Snapshotting
// the composer value at leave-time makes it reliable regardless. Skipped while editing (that's not a draft).
function persistCurrentDraft(){ try{ if(selected && !editTarget){ const inp=document.getElementById('msgInput'); if(inp) setDraft(selected.kind, selected.id, inp.value); } }catch(_){} }
// Compositor-only open slide (workflow-verified). armOpenSlide() runs AFTER openConvo's synchronous // Compositor-only open slide (workflow-verified). armOpenSlide() runs AFTER openConvo's synchronous
// render: it pins the pane off-screen (chat-opening), lets a frame paint it, then adds .chat-anim on a // render: it pins the pane off-screen (chat-opening), lets a frame paint it, then adds .chat-anim on a
// double-rAF so the compositor runs the 300ms transform with the main thread idle. All scrollTop/innerHTML // double-rAF so the compositor runs the 300ms transform with the main thread idle. All scrollTop/innerHTML
@@ -3810,6 +3815,7 @@ function openPollModal(gid){
} }
async function selectChat(kind,id){ async function selectChat(kind,id){
ensureNotifyPermission(); ensureNotifyPermission();
persistCurrentDraft(); // #14: snapshot the outgoing chat's draft BEFORE we swap (reliable across the switch)
stopTyping(); // leaving the previous conversation → tell that peer we stopped stopTyping(); // leaving the previous conversation → tell that peer we stopped
if(_selMode) exitSelect(); // #1: leave forward-selection when changing chats if(_selMode) exitSelect(); // #1: leave forward-selection when changing chats
selected={kind,id}; selected={kind,id};
@@ -6045,6 +6051,7 @@ function switchTab(tab){
if(tab==='meeting' && meetState==='idle'){ renderMeetingLobby(); } if(tab==='meeting' && meetState==='idle'){ renderMeetingLobby(); }
} }
function showWelcome(skipAnim){ function showWelcome(skipAnim){
persistCurrentDraft(); // #14: save the open chat's draft before we leave it for the list (back button)
const finish=()=>{ selected=null; document.body.classList.remove('chat-open','chat-closing','chat-dragging'); renderChats(searchVal()); renderChatPanel(); updateRailUnread(); }; const finish=()=>{ selected=null; document.body.classList.remove('chat-open','chat-closing','chat-dragging'); renderChats(searchVal()); renderChatPanel(); updateRailUnread(); };
// Mobile: slide the outgoing conversation off to the right before swapping back to the chat list, so // Mobile: slide the outgoing conversation off to the right before swapping back to the chat list, so
// "back" has a clear push/pop direction (like Teams/WhatsApp). Desktop just closes instantly. // "back" has a clear push/pop direction (like Teams/WhatsApp). Desktop just closes instantly.