fix(webui): sp6 third review round, a held message line stays steady between polls, only a pane's latest render decides the line, the held preview cuts on whole characters (#1873, lane sp6-threads)

This commit was merged in pull request #1970.
This commit is contained in:
jason.woltje committed 2026-11-15 05:02:28 -06:00
1 parent 02df2a3a25
commit 789e2bcdd4
1 file changed
+20 -10
+20 -10
View File
@@ -224,8 +224,16 @@
why = `Nothing is read for this thread while ${name}'s bus is ${how}.`; readable = false; back = true;
}
else if (i?.threads && !thread) line = `This thread is not in ${name}'s thread list.`;
setText(e.line, line); e.line.hidden = !line;
// A held message the last render had no composer for stays in the line between polls, so the
// status line neither flickers nor is announced again on every read.
const withHeld = () => {
const cps = [...p.draft.replace(/\s+/g, ' ').trim()];
return `${line ? `${line} ` : ''}Your unsent message is kept until this thread can send: “${cps.length > 200 ? `${cps.slice(0, 200).join('')}…` : cps.join('')}”`;
};
const shown = readable && p.held && p.draft ? withHeld() : line;
setText(e.line, shown); e.line.hidden = !shown;
if (!readable) {
p.held = false;
// An unsent message outlives the chat view: it is held on the pane and put back when the
// thread reads again.
const typed = p.chat && e.body.querySelector('form textarea')?.value;
@@ -236,19 +244,21 @@
return;
}
if (!p.chat) { p.chat = window.MosaicChat.pane(p.org); p.first = true; }
const first = p.first, chat = p.chat; p.first = false;
const first = p.first, chat = p.chat, seq = ++p.seq; p.first = false;
void chat.render({ path: `/chat/threads/${p.id}`, container: e.body, canSend: !!list && i?.chatSend === true, navigated: first }).then(() => {
if (!p.draft || p.chat !== chat || !p.el.isConnected) return;
const field = e.body.querySelector('form textarea');
if (!field) {
// Only the latest render of this pane decides: an earlier one that a newer render cut short
// has no composer yet and must not claim the thread cannot send.
if (seq !== p.seq || p.chat !== chat || !p.el.isConnected) return;
const field = p.draft ? e.body.querySelector('form textarea') : null;
if (p.draft && !field) {
// The thread reads but offers no composer: the held message stays visible here, never silently held.
const held = p.draft.length > 200 ? `${p.draft.slice(0, 200)}…` : p.draft;
setText(e.line, `${line ? `${line} ` : ''}Your unsent message is kept until this thread can send: “${held}”`); e.line.hidden = false;
p.held = true; const text = withHeld(); setText(e.line, text); e.line.hidden = false;
return;
}
// Put the held message back only into an empty composer; text typed meanwhile is never overwritten.
if (!field.value) { field.value = p.draft; field.dispatchEvent(new Event('input', { bubbles: true })); }
if (field.value === p.draft) p.draft = '';
if (field && !field.value) { field.value = p.draft; field.dispatchEvent(new Event('input', { bubbles: true })); }
if (field && field.value === p.draft) p.draft = '';
if (p.held && (field || !p.draft)) { p.held = false; setText(e.line, line); e.line.hidden = !line; }
}, () => {});
}
function paintTabs() {
@@ -281,7 +291,7 @@
// ---------- panes ----------
function makePane(key) {
const { org, id } = split(key);
const p = { key, org, id, n: ++paneSeq, el: null, chat: null, first: true, title: null, draft: '' };
const p = { key, org, id, n: ++paneSeq, el: null, chat: null, first: true, title: null, draft: '', held: false, seq: 0 };
const box = mounted.root.querySelector('[data-assist-panes]');
box.insertAdjacentHTML('beforeend', paneShell(p));
p.el = box.lastElementChild;