support multiple connections to the worker
Also removes the IRCClient message-passing-style API since the service worker will mutualize connections and thus interpret stuff like "connect" differently. This is getting messy, there is a real need to refactor and extend the "IRC client state" so that 1. There is less state in the shoutbox page, instead more should come from the IRC client state (which updates pretty frequently) 2. The protocol is cleaner Because currently if you disconnect from one page the others don't necessarily show the proper tabs, etc.
This commit is contained in:
parent
e127e92bd3
commit
ece78efd9c
|
@ -19,6 +19,8 @@ body {
|
|||
--shoutbox-message-author-color: #777777;
|
||||
--shoutbox-message-bg1: #ffffff;
|
||||
--shoutbox-message-bg2: #f8f8f8;
|
||||
--shoutbox-error-bg: #dd5648;
|
||||
--shoutbox-error-fg: white;
|
||||
}
|
||||
|
||||
/* Shoutbox-only style */
|
||||
|
@ -107,6 +109,13 @@ body {
|
|||
flex-flow: column;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
#v5shoutbox .channels .login-form .error {
|
||||
display: none;
|
||||
background: var(--shoutbox-error-bg);
|
||||
color: var(--shoutbox-error-fg);
|
||||
padding: 6px 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#v5shoutbox .channel {
|
||||
overflow-x: auto;
|
||||
|
|
|
@ -41,10 +41,7 @@ class IRCClientState {
|
|||
this.state = IRCState.DISCONNECTED;
|
||||
this.channels = [];
|
||||
}
|
||||
else {
|
||||
this.state = protocol_state_object.state;
|
||||
this.channels = protocol_state_object.channels;
|
||||
}
|
||||
else Object.assign(this, protocol_state_object);
|
||||
}
|
||||
|
||||
/* Connection status */
|
||||
|
@ -52,13 +49,13 @@ class IRCClientState {
|
|||
return this.state !== IRCState.DISCONNECTED;
|
||||
}
|
||||
isRunning() {
|
||||
return this.state === IRCState.READY;
|
||||
return this.state === IRCState.RUNNING;
|
||||
}
|
||||
stateString() {
|
||||
switch(this.state) {
|
||||
case IRCState.DISCONNECTED: return "Disconnected";
|
||||
case IRCState.CONNECTING: return "Connecting...";
|
||||
case IRCState.READY: return "Connected";
|
||||
case IRCState.RUNNING: return "Connected";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,16 +94,26 @@ class SharedChat {
|
|||
this.onMessage(e.data);
|
||||
});
|
||||
|
||||
return navigator.serviceWorker.register(
|
||||
/* Register the service worker */
|
||||
const reg = await navigator.serviceWorker.register(
|
||||
"v5shoutbox_worker.js", { scope: "./" })
|
||||
.then((registration) => {
|
||||
console.log(registration);
|
||||
return true;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(`Service worker registration failed with ${error}`);
|
||||
return false;
|
||||
return null;
|
||||
});
|
||||
if(reg === null)
|
||||
return false;
|
||||
|
||||
/* Once registered, wait for it to be ready, and then check whether it's
|
||||
controlling us; if not, fall back to the local mode */
|
||||
console.log("Service worker registration succeeded:", reg);
|
||||
await navigator.serviceWorker.ready;
|
||||
|
||||
if(navigator.serviceWorker.controller === null) {
|
||||
console.warn("No controller (maybe Shift-refresh was used?)");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/* Handler called when the page is taken over by a service worker. Usually
|
||||
|
@ -140,30 +147,29 @@ class SharedChat {
|
|||
}
|
||||
}
|
||||
|
||||
/** API for sending messages to the IRC client **/
|
||||
/** API for using the IRC client **/
|
||||
|
||||
/* Send a message to the IRC client */
|
||||
remoteCall(e) {
|
||||
connect(login, password) {
|
||||
if(this.irc == null)
|
||||
navigator.serviceWorker.controller.postMessage({
|
||||
type: "remote_call",
|
||||
rpc: e,
|
||||
type: "connect",
|
||||
login: login,
|
||||
password: password,
|
||||
});
|
||||
else
|
||||
this.irc.remoteCall(e);
|
||||
this.irc.connect(login, password);
|
||||
}
|
||||
|
||||
connect(login, password) { this.remoteCall({
|
||||
type: "connect",
|
||||
login: login,
|
||||
password: password,
|
||||
})}
|
||||
|
||||
post(channel, message) { this.remoteCall({
|
||||
type: "post",
|
||||
channel: channel,
|
||||
message: message,
|
||||
})}
|
||||
post(channel, message) {
|
||||
if(this.irc == null)
|
||||
navigator.serviceWorker.controller.postMessage({
|
||||
type: "post",
|
||||
channel: channel,
|
||||
message: message,
|
||||
});
|
||||
else
|
||||
this.irc.postMessage(channel, message);
|
||||
}
|
||||
|
||||
/** Handlers for getting messages from the IRC client **/
|
||||
|
||||
|
@ -203,6 +209,7 @@ let shoutbox = new function() {
|
|||
this.eLoginForm = root.querySelector(".login-form");
|
||||
this.eLogin = root.querySelector(".login");
|
||||
this.ePassword = root.querySelector(".password");
|
||||
this.eAuthenticationError = root.querySelector(".error");
|
||||
/* Elements of the shoutbox form */
|
||||
this.eShoutboxForm = root.querySelector(".shoutbox-form");
|
||||
this.eMessage = root.querySelector(".message-input");
|
||||
|
@ -258,8 +265,22 @@ let shoutbox = new function() {
|
|||
console.log(e);
|
||||
this.refreshView();
|
||||
}
|
||||
else if(e.type == "welcome") {
|
||||
for(const [channel, info] of this.clientState.channels) {
|
||||
this.createChannel(channel);
|
||||
/* If we are connected, select a channel */
|
||||
if(!this.isOnRemoteChannel())
|
||||
this.selectChannel(channel);
|
||||
}
|
||||
this.refreshView();
|
||||
}
|
||||
else if(e.type == "authenticated") {
|
||||
this.ePassword.value = "";
|
||||
this.eAuthenticationError.style.display = "none";
|
||||
}
|
||||
else if(e.type == "authentication_failed") {
|
||||
this.ePassword.value = "";
|
||||
this.eAuthenticationError.style.display = "block";
|
||||
}
|
||||
else if(e.type == "new_message") {
|
||||
this.onNewMessageCallback(e.channel, e.date, e.author, e.message);
|
||||
|
|
|
@ -88,8 +88,8 @@ const IRCState = Object.freeze({
|
|||
DISCONNECTED: 0,
|
||||
/* We just opened a socket to the IRC server and have yet to register. */
|
||||
CONNECTING: 1,
|
||||
/* We are authenticated and ready to use the server. */
|
||||
READY: 2,
|
||||
/* We are authenticated and using the server. */
|
||||
RUNNING: 2,
|
||||
});
|
||||
|
||||
/* https://stackoverflow.com/questions/30106476 */
|
||||
|
@ -143,6 +143,9 @@ class IRCClient {
|
|||
onAuthenticated() {
|
||||
this.sendMessage({ type: "authenticated" });
|
||||
}
|
||||
onAuthenticationFailed() {
|
||||
this.sendMessage({ type: "authentication_failed" });
|
||||
}
|
||||
onNewMessage(channel, date, author, message) {
|
||||
this.sendMessage({
|
||||
type: "new_message",
|
||||
|
@ -156,15 +159,6 @@ class IRCClient {
|
|||
this.sendMessage({ type: "channel_changed", channel: channel });
|
||||
}
|
||||
|
||||
/*** Message-based actions ***/
|
||||
|
||||
remoteCall(e) {
|
||||
if(e.type == "connect")
|
||||
this.connect(e.login, e.password);
|
||||
else if(e.type == "post")
|
||||
this.postMessage(e.channel, e.message);
|
||||
}
|
||||
|
||||
/*** Connection management ***/
|
||||
|
||||
setState(state) {
|
||||
|
@ -284,11 +278,25 @@ class IRCClient {
|
|||
this.#ws.send("AUTH " + this.username + ":" + this.#password);
|
||||
}
|
||||
|
||||
if(msg.command == 433 || msg.command == 451) {
|
||||
/* These are messy situations to be in, if this happens just disconnect
|
||||
so we can at least start from a blank state */
|
||||
this.log("[v5shoutbox] Nickname already in use / Not registered!");
|
||||
this.#ws.close();
|
||||
this.setState(IRCState.DISCONNECTED);
|
||||
}
|
||||
if(msg.command == 904) {
|
||||
this.log("[v5shoutbox] Authentication failed!");
|
||||
this.#ws.close();
|
||||
this.setState(IRCState.DISCONNECTED);
|
||||
this.onAuthenticationFailed();
|
||||
}
|
||||
|
||||
if(msg.command === 900) {
|
||||
this.log("[v5shoutbox] Authenticated.");
|
||||
if(this.caps.includes("sasl"))
|
||||
this.#sendRaw("CAP END");
|
||||
this.setState(IRCState.READY);
|
||||
this.setState(IRCState.RUNNING);
|
||||
this.#password = undefined;
|
||||
this.onAuthenticated();
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ self.addEventListener("activate", (e) => {
|
|||
log("activating!");
|
||||
irc = new IRCClient("wss://irc.planet-casio.com:443", undefined);
|
||||
irc.onMessage = (msg) => {
|
||||
console.log("message from irc server", msg);
|
||||
log(msg.type == "log" ? msg.text : msg);
|
||||
clients.forEach(c => c.postMessage(msg));
|
||||
};
|
||||
|
||||
|
@ -39,17 +39,27 @@ self.addEventListener("activate", (e) => {
|
|||
});
|
||||
|
||||
self.addEventListener("message", (e) => {
|
||||
log(e);
|
||||
if(e.data.type == "remote_call")
|
||||
irc.remoteCall(e.data.rpc);
|
||||
if(e.data.type == "connect") {
|
||||
log("<connect call, not shown>");
|
||||
irc.connect(e.data.login, e.data.password);
|
||||
}
|
||||
else if(e.data.type == "post") {
|
||||
log(e);
|
||||
irc.postMessage(e.data.channel, e.data.message);
|
||||
}
|
||||
else if(e.data.type == "handshake") {
|
||||
clients.push(e.source);
|
||||
console.log(`New client: ${e.source.id} (total ${clients.length})`);
|
||||
console.log("All clients:", clients);
|
||||
log(`New client: ${e.source.id} (total ${clients.length})`);
|
||||
log("All clients:", clients);
|
||||
e.source.postMessage({
|
||||
type: "welcome",
|
||||
// TODO: This is copied from IRC.sendMessage
|
||||
state: { state: irc.state, channels: irc.channels }
|
||||
});
|
||||
}
|
||||
else if(e.data.type == "leave") {
|
||||
clients = clients.filter(c => c.id != e.source.id);
|
||||
console.log(`Client left: ${e.source.id} (remains ${clients.length})`);
|
||||
console.log("All clients:", clients);
|
||||
log(`Client left: ${e.source.id} (remains ${clients.length})`);
|
||||
log("All clients:", clients);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<div class="channels">
|
||||
<div data-channel="\login">
|
||||
<form class="login-form form">
|
||||
<div class="error">Authentication failed, please try again.</div>
|
||||
<input type="text" class="login" placeholder="Utilisateur" />
|
||||
<input type="password" class="password" placeholder="Mot de passe" />
|
||||
<input type="submit" class="connect bg-ok" value="Connect!" />
|
||||
|
|
Loading…
Reference in New Issue