diff --git a/web/src/app.jsx b/web/src/app.jsx index c3ad50f..11607d8 100644 --- a/web/src/app.jsx +++ b/web/src/app.jsx @@ -355,6 +355,14 @@ function App() { .catch(() => {}); }, []); + // removeChannelTab closes a channel's tab and shows the Server tab. + const removeChannelTab = useCallback((name) => { + setTabs((prev) => + prev.filter((t) => !(t.type === "channel" && t.name === name)), + ); + setActiveTab(0); + }, []); + const processMessage = useCallback( (msg) => { const body = Array.isArray(msg.body) ? msg.body.join("\n") : ""; @@ -424,6 +432,13 @@ function App() { break; } case "PART": { + // The user's own PART, sent from this client or another, + // closes the tab and adds no message or unread count. + if (msg.from === nickRef.current) { + removeChannelTab(msg.to); + + break; + } const reason = body ? " (" + body + ")" : ""; const text = `${msg.from} has parted ${msg.to}${reason}`; if (msg.to) addMessage(msg.to, { ...base, text, system: true }); @@ -431,6 +446,20 @@ function App() { break; } + case "KICK": { + // The kicked nick is in params; the body is the reason. + const kicked = Array.isArray(msg.params) ? msg.params[0] : ""; + const reason = body ? " (" + body + ")" : ""; + 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) { + removeChannelTab(msg.to); + } else if (msg.to && msg.to.startsWith("#")) { + refreshMembers(msg.to); + } + + break; + } case "QUIT": { const reason = body ? " (" + body + ")" : ""; const text = `${msg.from} has quit${reason}`; @@ -656,7 +685,7 @@ function App() { } } }, - [addMessage, refreshMembers], + [addMessage, refreshMembers, removeChannelTab], ); // Long-poll loop. @@ -865,10 +894,7 @@ function App() { } catch (e) { // Ignore. } - setTabs((prev) => - prev.filter((t) => !(t.type === "channel" && t.name === name)), - ); - setActiveTab(0); + removeChannelTab(name); }; const closeTab = (idx) => {