From 6524dc5dd2735c48c0ea1e8470e52f9b1e653457 Mon Sep 17 00:00:00 2001
From: Simon Ser <contact@emersion.fr>
Date: Wed, 10 Mar 2021 11:00:33 +0100
Subject: [PATCH] Add support for the soju.im/bouncer-networks-notify cap

---
 components/app.js | 65 +++++++++++++++++++++++++++++++++--------------
 lib/client.js     |  4 +++
 2 files changed, 50 insertions(+), 19 deletions(-)

diff --git a/components/app.js b/components/app.js
index 74544b8..9657692 100644
--- a/components/app.js
+++ b/components/app.js
@@ -542,19 +542,19 @@ export default class App extends Component {
 		}
 	}
 
+	networkFromBouncerNetwork(bouncerNetworkID) {
+		for (var [id, client] of this.clients) {
+			if (client.params.bouncerNetwork === bouncerNetworkID) {
+				return id;
+			}
+		}
+		return null;
+	}
+
 	handleMessage(netID, msg) {
 		var client = this.clients.get(netID);
 		switch (msg.command) {
 		case irc.RPL_WELCOME:
-			if (client.enabledCaps["soju.im/bouncer-networks"] && !client.params.bouncerNetwork) {
-				client.listBouncerNetworks().then((bouncerNetworks) => {
-					this.setState((state) => {
-						return { bouncerNetworks };
-					});
-					this.openSecondaryClients(client, bouncerNetworks);
-				});
-			}
-
 			if (this.state.connectParams.autojoin.length > 0) {
 				client.send({
 					command: "JOIN",
@@ -745,11 +745,47 @@ export default class App extends Component {
 			this.setState({ error: description });
 			this.addMessage(netID, SERVER_BUFFER, msg);
 			break;
+		case "BOUNCER":
+			if (msg.params[0] !== "NETWORK") {
+				break; // We're only interested in network updates
+			}
+
+			var id = msg.params[1];
+			var attrs = null;
+			if (msg.params[2] !== "*") {
+				attrs = irc.parseTags(msg.params[2]);
+			}
+
+			var isNew = false;
+			this.setState((state) => {
+				var bouncerNetworks = new Map(state.bouncerNetworks);
+				if (!attrs) {
+					bouncerNetworks.delete(id);
+				} else {
+					var prev = bouncerNetworks.get(id);
+					isNew = prev === undefined;
+					attrs = { ...prev, ...attrs };
+					bouncerNetworks.set(id, attrs);
+				}
+				return { bouncerNetworks };
+			}, () => {
+				if (!attrs) {
+					var netID = this.networkFromBouncerNetwork(id);
+					if (netID) {
+						this.close({ network: netID, name: SERVER_BUFFER });
+					}
+				} else if (isNew) {
+					this.connect({
+						...client.params,
+						bouncerNetwork: id,
+					});
+				}
+			});
+			break;
 		case "CAP":
 		case "AUTHENTICATE":
 		case "PING":
 		case "BATCH":
-		case "BOUNCER":
 			// Ignore these
 			break;
 		default:
@@ -757,15 +793,6 @@ export default class App extends Component {
 		}
 	}
 
-	openSecondaryClients(client, bouncerNetworks) {
-		bouncerNetworks.forEach((attrs, id) => {
-			this.connect({
-				...client.params,
-				bouncerNetwork: id,
-			});
-		});
-	}
-
 	handleConnectSubmit(connectParams) {
 		this.setState({ error: null });
 
diff --git a/lib/client.js b/lib/client.js
index 81809dc..48ff69d 100644
--- a/lib/client.js
+++ b/lib/client.js
@@ -308,6 +308,10 @@ export default class Client extends EventTarget {
 					capEnd = false;
 				}
 
+				if (!this.params.bouncerNetwork && this.availableCaps["soju.im/bouncer-networks-notify"] !== undefined) {
+					reqCaps.push("soju.im/bouncer-networks-notify");
+				}
+
 				this.requestCaps(reqCaps);
 
 				if (this.status != Client.Status.REGISTERED && capEnd) {