mirror of
https://codeberg.org/emersion/gamja.git
synced 2024-11-14 19:05:01 -05:00
Keep track of client status in Client
This commit is contained in:
parent
0261bc11e7
commit
41cd2153cf
4 changed files with 70 additions and 43 deletions
|
@ -9,7 +9,7 @@ import Composer from "/components/composer.js";
|
||||||
import ScrollManager from "/components/scroll-manager.js";
|
import ScrollManager from "/components/scroll-manager.js";
|
||||||
import { html, Component, createRef } from "/lib/index.js";
|
import { html, Component, createRef } from "/lib/index.js";
|
||||||
import { strip as stripANSI } from "/lib/ansi.js";
|
import { strip as stripANSI } from "/lib/ansi.js";
|
||||||
import { SERVER_BUFFER, BufferType, ReceiptType, Status, Unread } from "/state.js";
|
import { SERVER_BUFFER, BufferType, ReceiptType, NetworkStatus, Unread } from "/state.js";
|
||||||
import commands from "/commands.js";
|
import commands from "/commands.js";
|
||||||
import { setup as setupKeybindings } from "/keybindings.js";
|
import { setup as setupKeybindings } from "/keybindings.js";
|
||||||
|
|
||||||
|
@ -150,7 +150,6 @@ export default class App extends Component {
|
||||||
autoconnect: false,
|
autoconnect: false,
|
||||||
autojoin: [],
|
autojoin: [],
|
||||||
},
|
},
|
||||||
status: Status.DISCONNECTED,
|
|
||||||
networks: new Map(),
|
networks: new Map(),
|
||||||
buffers: new Map(),
|
buffers: new Map(),
|
||||||
activeBuffer: null,
|
activeBuffer: null,
|
||||||
|
@ -451,7 +450,7 @@ export default class App extends Component {
|
||||||
var networks = new Map(state.networks);
|
var networks = new Map(state.networks);
|
||||||
networks.set(netID, {
|
networks.set(netID, {
|
||||||
id: netID,
|
id: netID,
|
||||||
status: Status.CONNECTING,
|
status: NetworkStatus.CONNECTING,
|
||||||
isupport: new Map(),
|
isupport: new Map(),
|
||||||
});
|
});
|
||||||
return { networks };
|
return { networks };
|
||||||
|
@ -469,8 +468,8 @@ export default class App extends Component {
|
||||||
|
|
||||||
this.clients.set(netID, client);
|
this.clients.set(netID, client);
|
||||||
|
|
||||||
client.addEventListener("close", () => {
|
client.addEventListener("status", () => {
|
||||||
this.handleClose(netID);
|
this.handleStatus(netID, client.status);
|
||||||
});
|
});
|
||||||
|
|
||||||
client.addEventListener("message", (event) => {
|
client.addEventListener("message", (event) => {
|
||||||
|
@ -487,18 +486,24 @@ export default class App extends Component {
|
||||||
this.switchBuffer({ network: netID, name: SERVER_BUFFER });
|
this.switchBuffer({ network: netID, name: SERVER_BUFFER });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClose(netID) {
|
handleStatus(netID, status) {
|
||||||
this.setNetworkState(netID, (state) => {
|
this.setNetworkState(netID, (state) => {
|
||||||
if (state.status == Status.DISCONNECTED) {
|
if (status !== Client.Status.DISCONNECTED) {
|
||||||
|
return { status };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.status === Client.Status.DISCONNECTED) {
|
||||||
// User decided to logout
|
// User decided to logout
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Reconnecting to server in " + RECONNECT_DELAY_SEC + " seconds");
|
console.log("Reconnecting to server in " + RECONNECT_DELAY_SEC + " seconds");
|
||||||
clearTimeout(this.reconnectTimeoutID);
|
clearTimeout(this.reconnectTimeoutID);
|
||||||
this.reconnectTimeoutID = setTimeout(() => {
|
this.reconnectTimeoutID = setTimeout(() => {
|
||||||
this.connect(netID, this.state.connectParams);
|
this.connect(netID, this.state.connectParams);
|
||||||
}, RECONNECT_DELAY_SEC * 1000);
|
}, RECONNECT_DELAY_SEC * 1000);
|
||||||
return { status: Status.DISCONNECTED };
|
|
||||||
|
return { status };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,12 +520,10 @@ export default class App extends Component {
|
||||||
|
|
||||||
var client = this.clients.get(netID);
|
var client = this.clients.get(netID);
|
||||||
if (client) {
|
if (client) {
|
||||||
// Prevent auto-reconnect from kicking in
|
client.disconnect();
|
||||||
client.removeEventListener("close", this.handleClose);
|
|
||||||
client.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setNetworkState(netID, { status: Status.DISCONNECTED });
|
this.setNetworkState(netID, { status: NetworkStatus.DISCONNECTED });
|
||||||
}
|
}
|
||||||
|
|
||||||
reconnect(netID) {
|
reconnect(netID) {
|
||||||
|
@ -538,8 +541,6 @@ export default class App extends Component {
|
||||||
var client = this.clients.get(netID);
|
var client = this.clients.get(netID);
|
||||||
switch (msg.command) {
|
switch (msg.command) {
|
||||||
case irc.RPL_WELCOME:
|
case irc.RPL_WELCOME:
|
||||||
this.setNetworkState(netID, { status: Status.REGISTERED });
|
|
||||||
|
|
||||||
if (this.state.connectParams.autojoin.length > 0) {
|
if (this.state.connectParams.autojoin.length > 0) {
|
||||||
client.send({
|
client.send({
|
||||||
command: "JOIN",
|
command: "JOIN",
|
||||||
|
@ -1009,10 +1010,10 @@ export default class App extends Component {
|
||||||
activeNetwork = this.state.networks.get(activeBuffer.network);
|
activeNetwork = this.state.networks.get(activeBuffer.network);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!activeNetwork || (activeNetwork.status != Status.REGISTERED && !activeBuffer)) {
|
if (!activeNetwork || (activeNetwork.status !== NetworkStatus.REGISTERED && !activeBuffer)) {
|
||||||
return html`
|
return html`
|
||||||
<section id="connect">
|
<section id="connect">
|
||||||
<${Connect} error=${this.state.error} params=${this.state.connectParams} disabled=${this.state.status != Status.DISCONNECTED} onSubmit=${this.handleConnectSubmit}/>
|
<${Connect} error=${this.state.error} params=${this.state.connectParams} disabled=${activeNetwork} onSubmit=${this.handleConnectSubmit}/>
|
||||||
</section>
|
</section>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { html, Component } from "/lib/index.js";
|
import { html, Component } from "/lib/index.js";
|
||||||
import linkify from "/lib/linkify.js";
|
import linkify from "/lib/linkify.js";
|
||||||
import { strip as stripANSI } from "/lib/ansi.js";
|
import { strip as stripANSI } from "/lib/ansi.js";
|
||||||
import { BufferType, Status } from "/state.js";
|
import { BufferType, NetworkStatus } from "/state.js";
|
||||||
|
|
||||||
const UserStatus = {
|
const UserStatus = {
|
||||||
HERE: "here",
|
HERE: "here",
|
||||||
|
@ -28,13 +28,16 @@ export default function BufferHeader(props) {
|
||||||
var description = null;
|
var description = null;
|
||||||
if (props.buffer.serverInfo) {
|
if (props.buffer.serverInfo) {
|
||||||
switch (props.network.status) {
|
switch (props.network.status) {
|
||||||
case Status.DISCONNECTED:
|
case NetworkStatus.DISCONNECTED:
|
||||||
description = "Disconnected";
|
description = "Disconnected";
|
||||||
break;
|
break;
|
||||||
case Status.CONNECTING:
|
case NetworkStatus.CONNECTING:
|
||||||
description = "Connecting...";
|
description = "Connecting...";
|
||||||
break;
|
break;
|
||||||
case Status.REGISTERED:
|
case NetworkStatus.REGISTERING:
|
||||||
|
description = "Logging in...";
|
||||||
|
break;
|
||||||
|
case NetworkStatus.REGISTERED:
|
||||||
var serverInfo = props.buffer.serverInfo;
|
var serverInfo = props.buffer.serverInfo;
|
||||||
description = `Connected to ${serverInfo.name}`;
|
description = `Connected to ${serverInfo.name}`;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -13,6 +13,14 @@ const permanentCaps = [
|
||||||
];
|
];
|
||||||
|
|
||||||
export default class Client extends EventTarget {
|
export default class Client extends EventTarget {
|
||||||
|
static Status = {
|
||||||
|
DISCONNECTED: "disconnected",
|
||||||
|
CONNECTING: "connecting",
|
||||||
|
REGISTERING: "registering",
|
||||||
|
REGISTERED: "registered",
|
||||||
|
};
|
||||||
|
|
||||||
|
status = Client.Status.DISCONNECTED;
|
||||||
ws = null;
|
ws = null;
|
||||||
nick = null;
|
nick = null;
|
||||||
params = {
|
params = {
|
||||||
|
@ -23,7 +31,6 @@ export default class Client extends EventTarget {
|
||||||
pass: null,
|
pass: null,
|
||||||
saslPlain: null,
|
saslPlain: null,
|
||||||
};
|
};
|
||||||
registered = false;
|
|
||||||
availableCaps = {};
|
availableCaps = {};
|
||||||
enabledCaps = {};
|
enabledCaps = {};
|
||||||
batches = new Map();
|
batches = new Map();
|
||||||
|
@ -33,12 +40,19 @@ export default class Client extends EventTarget {
|
||||||
|
|
||||||
this.params = Object.assign(this.params, params);
|
this.params = Object.assign(this.params, params);
|
||||||
|
|
||||||
|
this.reconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
reconnect() {
|
||||||
|
this.disconnect();
|
||||||
|
this.setStatus(Client.Status.CONNECTING);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.ws = new WebSocket(params.url);
|
this.ws = new WebSocket(this.params.url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.dispatchEvent(new CustomEvent("error", { detail: "Failed to create connection: " + err }));
|
this.dispatchEvent(new CustomEvent("error", { detail: "Failed to create connection: " + err }));
|
||||||
this.dispatchEvent(new CustomEvent("close"));
|
this.setStatus(Client.Status.DISCONNECTED);
|
||||||
}, 0);
|
}, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -47,7 +61,8 @@ export default class Client extends EventTarget {
|
||||||
|
|
||||||
this.ws.addEventListener("close", () => {
|
this.ws.addEventListener("close", () => {
|
||||||
console.log("Connection closed");
|
console.log("Connection closed");
|
||||||
this.dispatchEvent(new CustomEvent("close"));
|
this.ws = null;
|
||||||
|
this.setStatus(Client.Status.DISCONNECTED);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.ws.addEventListener("error", () => {
|
this.ws.addEventListener("error", () => {
|
||||||
|
@ -55,8 +70,23 @@ export default class Client extends EventTarget {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
if (this.ws) {
|
||||||
|
this.ws.close(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatus(status) {
|
||||||
|
if (this.status === status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.status = status;
|
||||||
|
this.dispatchEvent(new CustomEvent("status"));
|
||||||
|
}
|
||||||
|
|
||||||
handleOpen() {
|
handleOpen() {
|
||||||
console.log("Connection opened");
|
console.log("Connection opened");
|
||||||
|
this.setStatus(Client.Status.REGISTERING);
|
||||||
|
|
||||||
this.nick = this.params.nick;
|
this.nick = this.params.nick;
|
||||||
|
|
||||||
|
@ -88,12 +118,12 @@ export default class Client extends EventTarget {
|
||||||
case irc.RPL_WELCOME:
|
case irc.RPL_WELCOME:
|
||||||
if (this.params.saslPlain && this.availableCaps["sasl"] === undefined) {
|
if (this.params.saslPlain && this.availableCaps["sasl"] === undefined) {
|
||||||
console.error("Server doesn't support SASL PLAIN");
|
console.error("Server doesn't support SASL PLAIN");
|
||||||
this.close();
|
this.disconnect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Registration complete");
|
console.log("Registration complete");
|
||||||
this.registered = true;
|
this.setStatus(Client.Status.REGISTERED);
|
||||||
break;
|
break;
|
||||||
case "CAP":
|
case "CAP":
|
||||||
this.handleCap(msg);
|
this.handleCap(msg);
|
||||||
|
@ -109,7 +139,7 @@ export default class Client extends EventTarget {
|
||||||
break;
|
break;
|
||||||
case irc.RPL_SASLSUCCESS:
|
case irc.RPL_SASLSUCCESS:
|
||||||
console.log("SASL authentication success");
|
console.log("SASL authentication success");
|
||||||
if (!this.registered) {
|
if (this.status != Client.Status.REGISTERED) {
|
||||||
this.send({ command: "CAP", params: ["END"] });
|
this.send({ command: "CAP", params: ["END"] });
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -119,7 +149,7 @@ export default class Client extends EventTarget {
|
||||||
case irc.ERR_SASLABORTED:
|
case irc.ERR_SASLABORTED:
|
||||||
case irc.ERR_SASLALREADY:
|
case irc.ERR_SASLALREADY:
|
||||||
this.dispatchEvent(new CustomEvent("error", { detail: "SASL error (" + msg.command + "): " + msg.params[1] }));
|
this.dispatchEvent(new CustomEvent("error", { detail: "SASL error (" + msg.command + "): " + msg.params[1] }));
|
||||||
this.close();
|
this.disconnect();
|
||||||
break;
|
break;
|
||||||
case "PING":
|
case "PING":
|
||||||
this.send({ command: "PONG", params: [msg.params[0]] });
|
this.send({ command: "PONG", params: [msg.params[0]] });
|
||||||
|
@ -148,7 +178,7 @@ export default class Client extends EventTarget {
|
||||||
break;
|
break;
|
||||||
case "ERROR":
|
case "ERROR":
|
||||||
this.dispatchEvent(new CustomEvent("error", { detail: "Fatal IRC error: " + msg.params[0] }));
|
this.dispatchEvent(new CustomEvent("error", { detail: "Fatal IRC error: " + msg.params[0] }));
|
||||||
this.close();
|
this.disconnect();
|
||||||
break;
|
break;
|
||||||
case irc.ERR_PASSWDMISMATCH:
|
case irc.ERR_PASSWDMISMATCH:
|
||||||
case irc.ERR_ERRONEUSNICKNAME:
|
case irc.ERR_ERRONEUSNICKNAME:
|
||||||
|
@ -158,8 +188,8 @@ export default class Client extends EventTarget {
|
||||||
case irc.ERR_NOPERMFORHOST:
|
case irc.ERR_NOPERMFORHOST:
|
||||||
case irc.ERR_YOUREBANNEDCREEP:
|
case irc.ERR_YOUREBANNEDCREEP:
|
||||||
this.dispatchEvent(new CustomEvent("error", { detail: "Error (" + msg.command + "): " + msg.params[msg.params.length - 1] }));
|
this.dispatchEvent(new CustomEvent("error", { detail: "Error (" + msg.command + "): " + msg.params[msg.params.length - 1] }));
|
||||||
if (!this.registered) {
|
if (this.status != Client.Status.REGISTERED) {
|
||||||
this.close();
|
this.disconnect();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -229,7 +259,7 @@ export default class Client extends EventTarget {
|
||||||
|
|
||||||
this.requestCaps(reqCaps);
|
this.requestCaps(reqCaps);
|
||||||
|
|
||||||
if (!this.registered && capEnd) {
|
if (this.status != Client.Status.REGISTERED && capEnd) {
|
||||||
this.send({ command: "CAP", params: ["END"] });
|
this.send({ command: "CAP", params: ["END"] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,7 +291,7 @@ export default class Client extends EventTarget {
|
||||||
break;
|
break;
|
||||||
case "NAK":
|
case "NAK":
|
||||||
console.log("Server nak'ed caps:", args[0]);
|
console.log("Server nak'ed caps:", args[0]);
|
||||||
if (!this.registered) {
|
if (this.status != Client.Status.REGISTERED) {
|
||||||
this.send({ command: "CAP", params: ["END"] });
|
this.send({ command: "CAP", params: ["END"] });
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -287,11 +317,6 @@ export default class Client extends EventTarget {
|
||||||
console.log("Sent:", msg);
|
console.log("Sent:", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
|
||||||
this.ws.close(1000);
|
|
||||||
this.registered = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Execute a command that expects a response. `done` is called with message
|
/* Execute a command that expects a response. `done` is called with message
|
||||||
* events until it returns a truthy value. */
|
* events until it returns a truthy value. */
|
||||||
roundtrip(msg, done) {
|
roundtrip(msg, done) {
|
||||||
|
|
8
state.js
8
state.js
|
@ -1,3 +1,5 @@
|
||||||
|
import Client from "/lib/client.js";
|
||||||
|
|
||||||
export const SERVER_BUFFER = "*";
|
export const SERVER_BUFFER = "*";
|
||||||
|
|
||||||
export const BufferType = {
|
export const BufferType = {
|
||||||
|
@ -6,11 +8,7 @@ export const BufferType = {
|
||||||
NICK: "nick",
|
NICK: "nick",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Status = {
|
export const NetworkStatus = Client.Status;
|
||||||
DISCONNECTED: "disconnected",
|
|
||||||
CONNECTING: "connecting",
|
|
||||||
REGISTERED: "registered",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Unread = {
|
export const Unread = {
|
||||||
NONE: "",
|
NONE: "",
|
||||||
|
|
Loading…
Reference in a new issue