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", {