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);