From 2331c15aadb0ed8ca33f49d168346249d8b85da5 Mon Sep 17 00:00:00 2001 From: clawbot <35+clawbot@noreply.example.org> Date: Tue, 27 Oct 2026 05:52:48 +0100 Subject: [PATCH] Keep the web tab the user reads, and show a kick in the Server tab (closes #195) removeChannelTab made the Server tab active whenever it closed a channel's tab. When a part or kick came from another client, the user lost the channel they were reading. It now finds the tab the user was reading by its name among the remaining tabs and keeps it active. Only when that was the closed tab does the Server tab show. When the kicked nick is the user's own, the kick line also goes into the Server tab, which counts it as unread. That copy carries the event's id with "-server" added, since addMessage drops an id it has already seen. Model: opus-5-5 --- web/src/app.jsx | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/web/src/app.jsx b/web/src/app.jsx index cd18ff1..364f11e 100644 --- a/web/src/app.jsx +++ b/web/src/app.jsx @@ -355,12 +355,24 @@ function App() { .catch(() => {}); }, []); - // removeChannelTab closes a channel's tab and shows the Server tab. + // removeChannelTab closes a channel's tab. The tab the user was + // reading stays active, found by its name; when it was the closed tab, + // the Server tab shows. const removeChannelTab = useCallback((name) => { - setTabs((prev) => - prev.filter((t) => !(t.type === "channel" && t.name === name)), - ); - setActiveTab(0); + setTabs((prev) => { + const remaining = prev.filter( + (t) => !(t.type === "channel" && t.name === name), + ); + // Preact runs this update at once, on the newest active tab. + setActiveTab((active) => { + const reading = prev[active]?.name; + const idx = remaining.findIndex((t) => t.name === reading); + + return idx >= 0 ? idx : 0; + }); + + return remaining; + }); }, []); const processMessage = useCallback( @@ -453,6 +465,15 @@ function App() { const text = `${kicked} was kicked from ${msg.to} by ${msg.from}${reason}`; if (msg.to) addMessage(msg.to, { ...base, text, system: true }); if (kicked === nickRef.current) { + // The channel's tab closes, so the Server tab gets the line + // too. It needs an id of its own: addMessage drops a message + // whose id it has already seen. + addMessage("Server", { + ...base, + id: msg.id + "-server", + text, + system: true, + }); removeChannelTab(msg.to); } else if (msg.to && msg.to.startsWith("#")) { refreshMembers(msg.to);