From df94c2d11de3ed116bf04b92e31c9f6028f3cb89 Mon Sep 17 00:00:00 2001 From: sneak Date: Wed, 21 Oct 2026 03:59:25 +0000 Subject: [PATCH] Keep the web client's saved channels and show who changed a mode (closes #176) The web client saved its channel tabs to localStorage whenever the tabs changed, including on page load, when only the Server tab exists. That wrote an empty list over the saved one before login read it, so a new session never rejoined anything. The save now waits until the user is logged in, and login reads the saved list before it marks the user logged in. A MODE message from the server fell through to the Server tab as its bare body. It now shows in the channel's tab as "alice sets mode +v bob on #chan". Model: opus-5-5 --- web/src/app.jsx | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/web/src/app.jsx b/web/src/app.jsx index 932ee72..c3ad50f 100644 --- a/web/src/app.jsx +++ b/web/src/app.jsx @@ -300,11 +300,14 @@ function App() { nickRef.current = nick; }, [nick]); - // Persist joined channels. + // Persist joined channels, once logged in: before then only the + // Server tab exists, and writing would erase the saved channels + // that onLogin rejoins. useEffect(() => { + if (!loggedIn) return; const channels = tabs.filter((t) => t.type === "channel").map((t) => t.name); localStorage.setItem("neoirc_channels", JSON.stringify(channels)); - }, [tabs]); + }, [loggedIn, tabs]); // Clear unread on tab switch. useEffect(() => { @@ -463,6 +466,15 @@ function App() { break; } + case "MODE": { + // The body is the change, with its value where it has one: + // ["+o bob"], ["+m"], ["+k", "secretpass"]. + const modes = Array.isArray(msg.body) ? msg.body.join(" ") : ""; + const text = `${msg.from} sets mode ${modes} on ${msg.to}`; + if (msg.to) addMessage(msg.to, { ...base, text, system: true }); + + break; + } case 353: { // RPL_NAMREPLY — each name is a hostmask (nick!user@host), // prefixed with @ for an operator or + for a voiced member. @@ -759,6 +771,11 @@ function App() { const onLogin = useCallback( async (userNick, isResumed) => { + // Read before setLoggedIn(true): from then on the effect under + // "Persist joined channels" writes the open channel tabs over it. + const saved = JSON.parse( + localStorage.getItem("neoirc_channels") || "[]", + ); setNick(userNick); setLoggedIn(true); addSystemMessage("Server", `Connected as ${userNick}`); @@ -783,9 +800,6 @@ function App() { } // Fresh session — join any previously saved channels. - const saved = JSON.parse( - localStorage.getItem("neoirc_channels") || "[]", - ); for (const ch of saved) { try { await api("/messages", { -- 2.54.0