Give the web Server tab a name no nick can take (closes #200)
check / check (push) Waiting to run
check / check (push) Waiting to run
The web client stored each tab's messages and unread count under the tab's name, and the Server tab was named Server. A user with the nick Server got a direct-message tab with the same name, so the two tabs showed one message list. The Server tab is now stored under "*server*". No nick or channel name can contain *, so that user's tab keeps its own messages. The tab bar still labels the tab Server. Model: opus-5-5
This commit was merged in pull request #201.
This commit is contained in:
+48
-43
@@ -7,6 +7,11 @@ const RECONNECT_DELAY = 3000;
|
|||||||
const MEMBER_REFRESH_INTERVAL = 10000;
|
const MEMBER_REFRESH_INTERVAL = 10000;
|
||||||
const ACTION_PREFIX = "\x01ACTION ";
|
const ACTION_PREFIX = "\x01ACTION ";
|
||||||
const ACTION_SUFFIX = "\x01";
|
const ACTION_SUFFIX = "\x01";
|
||||||
|
// SERVER_TAB is the Server tab's name, under which its messages and
|
||||||
|
// unread count are stored. No nick or channel name can contain *, so a
|
||||||
|
// user with the nick Server gets a direct-message tab of its own. The
|
||||||
|
// tab bar labels this tab Server.
|
||||||
|
const SERVER_TAB = "*server*";
|
||||||
|
|
||||||
// Hashcash proof-of-work helpers using Web Crypto API.
|
// Hashcash proof-of-work helpers using Web Crypto API.
|
||||||
|
|
||||||
@@ -270,9 +275,9 @@ function UserList({ members, onNickClick }) {
|
|||||||
function App() {
|
function App() {
|
||||||
const [loggedIn, setLoggedIn] = useState(false);
|
const [loggedIn, setLoggedIn] = useState(false);
|
||||||
const [nick, setNick] = useState("");
|
const [nick, setNick] = useState("");
|
||||||
const [tabs, setTabs] = useState([{ type: "server", name: "Server" }]);
|
const [tabs, setTabs] = useState([{ type: "server", name: SERVER_TAB }]);
|
||||||
const [activeTab, setActiveTab] = useState(0);
|
const [activeTab, setActiveTab] = useState(0);
|
||||||
const [messages, setMessages] = useState({ Server: [] });
|
const [messages, setMessages] = useState({ [SERVER_TAB]: [] });
|
||||||
const [members, setMembers] = useState({});
|
const [members, setMembers] = useState({});
|
||||||
const [topics, setTopics] = useState({});
|
const [topics, setTopics] = useState({});
|
||||||
const [unread, setUnread] = useState({});
|
const [unread, setUnread] = useState({});
|
||||||
@@ -468,7 +473,7 @@ function App() {
|
|||||||
// The channel's tab closes, so the Server tab gets the line
|
// The channel's tab closes, so the Server tab gets the line
|
||||||
// too. It needs an id of its own: addMessage drops a message
|
// too. It needs an id of its own: addMessage drops a message
|
||||||
// whose id it has already seen.
|
// whose id it has already seen.
|
||||||
addMessage("Server", {
|
addMessage(SERVER_TAB, {
|
||||||
...base,
|
...base,
|
||||||
id: msg.id + "-server",
|
id: msg.id + "-server",
|
||||||
text,
|
text,
|
||||||
@@ -568,7 +573,7 @@ function App() {
|
|||||||
const chName = msg.params[0];
|
const chName = msg.params[0];
|
||||||
const count = msg.params[1];
|
const count = msg.params[1];
|
||||||
const chTopic = body || "";
|
const chTopic = body || "";
|
||||||
addMessage("Server", {
|
addMessage(SERVER_TAB, {
|
||||||
...base,
|
...base,
|
||||||
text: `${chName} (${count} users): ${chTopic.trim()}`,
|
text: `${chName} (${count} users): ${chTopic.trim()}`,
|
||||||
system: true,
|
system: true,
|
||||||
@@ -578,7 +583,7 @@ function App() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 323:
|
case 323:
|
||||||
addMessage("Server", {
|
addMessage(SERVER_TAB, {
|
||||||
...base,
|
...base,
|
||||||
text: body || "End of channel list",
|
text: body || "End of channel list",
|
||||||
system: true,
|
system: true,
|
||||||
@@ -591,7 +596,7 @@ function App() {
|
|||||||
const whoCh = msg.params[0];
|
const whoCh = msg.params[0];
|
||||||
const whoNick = msg.params[4];
|
const whoNick = msg.params[4];
|
||||||
const whoFlags = msg.params.length > 5 ? msg.params[5] : "";
|
const whoFlags = msg.params.length > 5 ? msg.params[5] : "";
|
||||||
addMessage("Server", {
|
addMessage(SERVER_TAB, {
|
||||||
...base,
|
...base,
|
||||||
text: `${whoCh} ${whoNick} ${whoFlags}`,
|
text: `${whoCh} ${whoNick} ${whoFlags}`,
|
||||||
system: true,
|
system: true,
|
||||||
@@ -601,7 +606,7 @@ function App() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 315:
|
case 315:
|
||||||
addMessage("Server", {
|
addMessage(SERVER_TAB, {
|
||||||
...base,
|
...base,
|
||||||
text: body || "End of /WHO list",
|
text: body || "End of /WHO list",
|
||||||
system: true,
|
system: true,
|
||||||
@@ -612,7 +617,7 @@ function App() {
|
|||||||
// RPL_WHOISUSER — params nick, user, host, *; body realname.
|
// RPL_WHOISUSER — params nick, user, host, *; body realname.
|
||||||
if (Array.isArray(msg.params) && msg.params.length >= 3) {
|
if (Array.isArray(msg.params) && msg.params.length >= 3) {
|
||||||
const [wiNick, wiUser, wiHost] = msg.params;
|
const [wiNick, wiUser, wiHost] = msg.params;
|
||||||
addMessage("Server", {
|
addMessage(SERVER_TAB, {
|
||||||
...base,
|
...base,
|
||||||
text: `${wiNick} is ${wiUser}@${wiHost} (${body})`,
|
text: `${wiNick} is ${wiUser}@${wiHost} (${body})`,
|
||||||
system: true,
|
system: true,
|
||||||
@@ -626,7 +631,7 @@ function App() {
|
|||||||
if (Array.isArray(msg.params) && msg.params.length >= 2) {
|
if (Array.isArray(msg.params) && msg.params.length >= 2) {
|
||||||
const wiNick = msg.params[0];
|
const wiNick = msg.params[0];
|
||||||
const wiServer = msg.params[1];
|
const wiServer = msg.params[1];
|
||||||
addMessage("Server", {
|
addMessage(SERVER_TAB, {
|
||||||
...base,
|
...base,
|
||||||
text: `${wiNick} on ${wiServer}`,
|
text: `${wiNick} on ${wiServer}`,
|
||||||
system: true,
|
system: true,
|
||||||
@@ -638,7 +643,7 @@ function App() {
|
|||||||
case 313: {
|
case 313: {
|
||||||
// RPL_WHOISOPERATOR — nick; body "is an IRC operator".
|
// RPL_WHOISOPERATOR — nick; body "is an IRC operator".
|
||||||
if (Array.isArray(msg.params) && msg.params.length >= 1) {
|
if (Array.isArray(msg.params) && msg.params.length >= 1) {
|
||||||
addMessage("Server", {
|
addMessage(SERVER_TAB, {
|
||||||
...base,
|
...base,
|
||||||
text: `${msg.params[0]} ${body}`,
|
text: `${msg.params[0]} ${body}`,
|
||||||
system: true,
|
system: true,
|
||||||
@@ -652,7 +657,7 @@ function App() {
|
|||||||
if (Array.isArray(msg.params) && msg.params.length >= 3) {
|
if (Array.isArray(msg.params) && msg.params.length >= 3) {
|
||||||
const [wiNick, idle, signon] = msg.params;
|
const [wiNick, idle, signon] = msg.params;
|
||||||
const signedOn = new Date(Number(signon) * 1000).toLocaleString();
|
const signedOn = new Date(Number(signon) * 1000).toLocaleString();
|
||||||
addMessage("Server", {
|
addMessage(SERVER_TAB, {
|
||||||
...base,
|
...base,
|
||||||
text: `${wiNick} has been idle ${idle}s, signed on ${signedOn}`,
|
text: `${wiNick} has been idle ${idle}s, signed on ${signedOn}`,
|
||||||
system: true,
|
system: true,
|
||||||
@@ -665,7 +670,7 @@ function App() {
|
|||||||
// RPL_WHOISCHANNELS — nick, channels.
|
// RPL_WHOISCHANNELS — nick, channels.
|
||||||
if (Array.isArray(msg.params) && msg.params.length >= 1) {
|
if (Array.isArray(msg.params) && msg.params.length >= 1) {
|
||||||
const wiNick = msg.params[0];
|
const wiNick = msg.params[0];
|
||||||
addMessage("Server", {
|
addMessage(SERVER_TAB, {
|
||||||
...base,
|
...base,
|
||||||
text: `${wiNick} is on: ${body}`,
|
text: `${wiNick} is on: ${body}`,
|
||||||
system: true,
|
system: true,
|
||||||
@@ -675,7 +680,7 @@ function App() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 318:
|
case 318:
|
||||||
addMessage("Server", {
|
addMessage(SERVER_TAB, {
|
||||||
...base,
|
...base,
|
||||||
text: body || "End of /WHOIS list",
|
text: body || "End of /WHOIS list",
|
||||||
system: true,
|
system: true,
|
||||||
@@ -685,7 +690,7 @@ function App() {
|
|||||||
case 324: {
|
case 324: {
|
||||||
// RPL_CHANNELMODEIS — channel, mode string.
|
// RPL_CHANNELMODEIS — channel, mode string.
|
||||||
if (Array.isArray(msg.params) && msg.params.length >= 2) {
|
if (Array.isArray(msg.params) && msg.params.length >= 2) {
|
||||||
addMessage("Server", {
|
addMessage(SERVER_TAB, {
|
||||||
...base,
|
...base,
|
||||||
text: `${msg.params[0]} modes: ${msg.params[1]}`,
|
text: `${msg.params[0]} modes: ${msg.params[1]}`,
|
||||||
system: true,
|
system: true,
|
||||||
@@ -697,12 +702,12 @@ function App() {
|
|||||||
case 375:
|
case 375:
|
||||||
case 372:
|
case 372:
|
||||||
case 376:
|
case 376:
|
||||||
addMessage("Server", { ...base, text: body, system: true });
|
addMessage(SERVER_TAB, { ...base, text: body, system: true });
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (body) {
|
if (body) {
|
||||||
addMessage("Server", { ...base, text: body, system: true });
|
addMessage(SERVER_TAB, { ...base, text: body, system: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -828,7 +833,7 @@ function App() {
|
|||||||
);
|
);
|
||||||
setNick(userNick);
|
setNick(userNick);
|
||||||
setLoggedIn(true);
|
setLoggedIn(true);
|
||||||
addSystemMessage("Server", `Connected as ${userNick}`);
|
addSystemMessage(SERVER_TAB, `Connected as ${userNick}`);
|
||||||
|
|
||||||
if (isResumed) {
|
if (isResumed) {
|
||||||
// Request MOTD on resumed sessions (new sessions
|
// Request MOTD on resumed sessions (new sessions
|
||||||
@@ -912,7 +917,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addSystemMessage(
|
addSystemMessage(
|
||||||
"Server",
|
SERVER_TAB,
|
||||||
`Failed to join ${name}: ${err.data?.error || "error"}`,
|
`Failed to join ${name}: ${err.data?.error || "error"}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -963,7 +968,7 @@ function App() {
|
|||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case "/join": {
|
case "/join": {
|
||||||
if (parts[1]) joinChannel(parts[1]);
|
if (parts[1]) joinChannel(parts[1]);
|
||||||
else addSystemMessage("Server", "Usage: /join #channel");
|
else addSystemMessage(SERVER_TAB, "Usage: /join #channel");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -972,7 +977,7 @@ function App() {
|
|||||||
const reason = parts.slice(1).join(" ") || undefined;
|
const reason = parts.slice(1).join(" ") || undefined;
|
||||||
partChannel(tab.name, reason);
|
partChannel(tab.name, reason);
|
||||||
} else {
|
} else {
|
||||||
addSystemMessage("Server", "You are not in a channel");
|
addSystemMessage(SERVER_TAB, "You are not in a channel");
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -993,19 +998,19 @@ function App() {
|
|||||||
openDM(target);
|
openDM(target);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addSystemMessage(
|
addSystemMessage(
|
||||||
"Server",
|
SERVER_TAB,
|
||||||
`Message failed: ${err.data?.error || "error"}`,
|
`Message failed: ${err.data?.error || "error"}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addSystemMessage("Server", "Usage: /msg <nick> <message>");
|
addSystemMessage(SERVER_TAB, "Usage: /msg <nick> <message>");
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "/me": {
|
case "/me": {
|
||||||
if (tab.type === "server") {
|
if (tab.type === "server") {
|
||||||
addSystemMessage("Server", "Cannot use /me in server window");
|
addSystemMessage(SERVER_TAB, "Cannot use /me in server window");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1027,7 +1032,7 @@ function App() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addSystemMessage("Server", "Usage: /me <action>");
|
addSystemMessage(SERVER_TAB, "Usage: /me <action>");
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -1041,19 +1046,19 @@ function App() {
|
|||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addSystemMessage(
|
addSystemMessage(
|
||||||
"Server",
|
SERVER_TAB,
|
||||||
`Nick change failed: ${err.data?.error || "error"}`,
|
`Nick change failed: ${err.data?.error || "error"}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addSystemMessage("Server", "Usage: /nick <newnick>");
|
addSystemMessage(SERVER_TAB, "Usage: /nick <newnick>");
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "/topic": {
|
case "/topic": {
|
||||||
if (tab.type !== "channel") {
|
if (tab.type !== "channel") {
|
||||||
addSystemMessage("Server", "You are not in a channel");
|
addSystemMessage(SERVER_TAB, "You are not in a channel");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1070,13 +1075,13 @@ function App() {
|
|||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addSystemMessage(
|
addSystemMessage(
|
||||||
"Server",
|
SERVER_TAB,
|
||||||
`Topic change failed: ${err.data?.error || "error"}`,
|
`Topic change failed: ${err.data?.error || "error"}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addSystemMessage(
|
addSystemMessage(
|
||||||
"Server",
|
SERVER_TAB,
|
||||||
`Current topic for ${tab.name}: ${topics[tab.name] || "(none)"}`,
|
`Current topic for ${tab.name}: ${topics[tab.name] || "(none)"}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1085,7 +1090,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
case "/mode": {
|
case "/mode": {
|
||||||
if (tab.type !== "channel") {
|
if (tab.type !== "channel") {
|
||||||
addSystemMessage("Server", "You are not in a channel");
|
addSystemMessage(SERVER_TAB, "You are not in a channel");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1103,7 +1108,7 @@ function App() {
|
|||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addSystemMessage(
|
addSystemMessage(
|
||||||
"Server",
|
SERVER_TAB,
|
||||||
`MODE failed: ${err.data?.error || "error"}`,
|
`MODE failed: ${err.data?.error || "error"}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1134,7 +1139,7 @@ function App() {
|
|||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addSystemMessage(
|
addSystemMessage(
|
||||||
"Server",
|
SERVER_TAB,
|
||||||
`Failed to request MOTD: ${err.data?.error || "error"}`,
|
`Failed to request MOTD: ${err.data?.error || "error"}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1158,13 +1163,13 @@ function App() {
|
|||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addSystemMessage(
|
addSystemMessage(
|
||||||
"Server",
|
SERVER_TAB,
|
||||||
`Message failed: ${err.data?.error || "error"}`,
|
`Message failed: ${err.data?.error || "error"}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addSystemMessage("Server", "Usage: /query <nick> [message]");
|
addSystemMessage(SERVER_TAB, "Usage: /query <nick> [message]");
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -1177,7 +1182,7 @@ function App() {
|
|||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addSystemMessage(
|
addSystemMessage(
|
||||||
"Server",
|
SERVER_TAB,
|
||||||
`Failed to list channels: ${err.data?.error || "error"}`,
|
`Failed to list channels: ${err.data?.error || "error"}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1194,12 +1199,12 @@ function App() {
|
|||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addSystemMessage(
|
addSystemMessage(
|
||||||
"Server",
|
SERVER_TAB,
|
||||||
`WHO failed: ${err.data?.error || "error"}`,
|
`WHO failed: ${err.data?.error || "error"}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addSystemMessage("Server", "Usage: /who #channel");
|
addSystemMessage(SERVER_TAB, "Usage: /who #channel");
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -1213,12 +1218,12 @@ function App() {
|
|||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addSystemMessage(
|
addSystemMessage(
|
||||||
"Server",
|
SERVER_TAB,
|
||||||
`WHOIS failed: ${err.data?.error || "error"}`,
|
`WHOIS failed: ${err.data?.error || "error"}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addSystemMessage("Server", "Usage: /whois <nick>");
|
addSystemMessage(SERVER_TAB, "Usage: /whois <nick>");
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -1249,13 +1254,13 @@ function App() {
|
|||||||
" /help — Show this help",
|
" /help — Show this help",
|
||||||
];
|
];
|
||||||
for (const line of helpLines) {
|
for (const line of helpLines) {
|
||||||
addSystemMessage("Server", line);
|
addSystemMessage(SERVER_TAB, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
addSystemMessage("Server", `Unknown command: ${cmd}`);
|
addSystemMessage(SERVER_TAB, `Unknown command: ${cmd}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1284,7 +1289,7 @@ function App() {
|
|||||||
|
|
||||||
if (tab.type === "server") {
|
if (tab.type === "server") {
|
||||||
addSystemMessage(
|
addSystemMessage(
|
||||||
"Server",
|
SERVER_TAB,
|
||||||
"Cannot send messages to the server window. Use /join #channel first.",
|
"Cannot send messages to the server window. Use /join #channel first.",
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1355,7 +1360,7 @@ function App() {
|
|||||||
key={tab.name}
|
key={tab.name}
|
||||||
>
|
>
|
||||||
<span class="tab-label">
|
<span class="tab-label">
|
||||||
{tab.type === "dm" ? tab.name : tab.name}
|
{tab.type === "server" ? "Server" : tab.name}
|
||||||
</span>
|
</span>
|
||||||
{unread[tab.name] > 0 && i !== activeTab && (
|
{unread[tab.name] > 0 && i !== activeTab && (
|
||||||
<span class="unread-count">({unread[tab.name]})</span>
|
<span class="unread-count">({unread[tab.name]})</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user