Fixes to improve chat experience. Suppress spurrious ping messages. Try to reconnect when a network connection is restored. Send messages to multiple sessions from the same user.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3230 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
		@@ -281,8 +281,10 @@ function setErrorMessage(message) {
 | 
			
		||||
	while (node.firstChild) {
 | 
			
		||||
		node.removeChild(node.firstChild);
 | 
			
		||||
	}
 | 
			
		||||
	node.appendChild(document.createTextNode(message));
 | 
			
		||||
	node.setAttribute("style", "display: inline; color: #dc322f");
 | 
			
		||||
	if (message) {
 | 
			
		||||
		node.appendChild(document.createTextNode(message));
 | 
			
		||||
		node.setAttribute("style", "display: inline; color: #dc322f");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function send(command) {
 | 
			
		||||
@@ -429,11 +431,17 @@ function hashChange() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function focus() {
 | 
			
		||||
	send({event: "focus"});
 | 
			
		||||
	if (gSocket && gSocket.readyState == gSocket.CLOSED) {
 | 
			
		||||
		connectSocket();
 | 
			
		||||
	} else {
 | 
			
		||||
		send({event: "focus"});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function blur() {
 | 
			
		||||
	send({event: "blur"});
 | 
			
		||||
	if (gSocket && gSocket.readyState == gSocket.OPEN) {
 | 
			
		||||
		send({event: "blur"});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onMessage(event) {
 | 
			
		||||
@@ -452,6 +460,39 @@ function submitButton() {
 | 
			
		||||
	send({event: "submit", value: data});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function connectSocket() {
 | 
			
		||||
	if (!gSocket || gSocket.readyState == gSocket.CLOSED) {
 | 
			
		||||
		gSocket = new WebSocket(
 | 
			
		||||
			(window.location.protocol == "https:" ? "wss://" : "ws://")
 | 
			
		||||
			+ window.location.hostname
 | 
			
		||||
			+ (window.location.port.length ? ":" + window.location.port : "")
 | 
			
		||||
			+ "/terminal/socket");
 | 
			
		||||
		gSocket.onopen = function() {
 | 
			
		||||
			setErrorMessage(null);
 | 
			
		||||
			gSocket.send(JSON.stringify({
 | 
			
		||||
				action: "hello",
 | 
			
		||||
				path: window.location.pathname,
 | 
			
		||||
				terminalApi: [
 | 
			
		||||
					['clear'],
 | 
			
		||||
					['notify', 'title', 'options'],
 | 
			
		||||
					['postMessageToIframe', 'name', 'message'],
 | 
			
		||||
					['setHash', 'value'],
 | 
			
		||||
					['setPassword', 'value'],
 | 
			
		||||
					['setPrompt', 'value'],
 | 
			
		||||
					['setTitle', 'value'],
 | 
			
		||||
					['split', 'options'],
 | 
			
		||||
				],
 | 
			
		||||
			}));
 | 
			
		||||
		}
 | 
			
		||||
		gSocket.onmessage = function(event) {
 | 
			
		||||
			receive(JSON.parse(event.data));
 | 
			
		||||
		}
 | 
			
		||||
		gSocket.onclose = function(event) {
 | 
			
		||||
			setErrorMessage("Connection closed with code " + event.code);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
window.addEventListener("load", function() {
 | 
			
		||||
	if (window.Notification) {
 | 
			
		||||
		Notification.requestPermission();
 | 
			
		||||
@@ -463,33 +504,7 @@ window.addEventListener("load", function() {
 | 
			
		||||
	window.addEventListener("focus", focus);
 | 
			
		||||
	window.addEventListener("blur", blur);
 | 
			
		||||
	window.addEventListener("message", onMessage, false);
 | 
			
		||||
	window.addEventListener("online", connectSocket);
 | 
			
		||||
	enableDragDrop();
 | 
			
		||||
 | 
			
		||||
	gSocket = new WebSocket(
 | 
			
		||||
		(window.location.protocol == "https:" ? "wss://" : "ws://")
 | 
			
		||||
		+ window.location.hostname
 | 
			
		||||
		+ (window.location.port.length ? ":" + window.location.port : "")
 | 
			
		||||
		+ "/terminal/socket");
 | 
			
		||||
	gSocket.onopen = function() {
 | 
			
		||||
		gSocket.send(JSON.stringify({
 | 
			
		||||
			action: "hello",
 | 
			
		||||
			path: window.location.pathname,
 | 
			
		||||
			terminalApi: [
 | 
			
		||||
				['clear'],
 | 
			
		||||
				['notify', 'title', 'options'],
 | 
			
		||||
				['postMessageToIframe', 'name', 'message'],
 | 
			
		||||
				['setHash', 'value'],
 | 
			
		||||
				['setPassword', 'value'],
 | 
			
		||||
				['setPrompt', 'value'],
 | 
			
		||||
				['setTitle', 'value'],
 | 
			
		||||
				['split', 'options'],
 | 
			
		||||
			],
 | 
			
		||||
		}));
 | 
			
		||||
	}
 | 
			
		||||
	gSocket.onmessage = function(event) {
 | 
			
		||||
		receive(JSON.parse(event.data));
 | 
			
		||||
	}
 | 
			
		||||
	gSocket.onclose = function(event) {
 | 
			
		||||
		setErrorMessage("Connection closed with code " + event.code);
 | 
			
		||||
	}
 | 
			
		||||
	connectSocket();
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -105,7 +105,7 @@ function configureAccount(id, updates) {
 | 
			
		||||
				if (schema) {
 | 
			
		||||
					for (var i in schema) {
 | 
			
		||||
						let field = schema[i];
 | 
			
		||||
						terminal.print({input: field.type, name: field.name, value: account[field.name] || field.default});
 | 
			
		||||
						terminal.print({style: "font-weight: bold", value: field.name + ": "}, {input: field.type, name: field.name, value: account[field.name] || field.default});
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -154,7 +154,6 @@ function connect(id) {
 | 
			
		||||
					session.conversations = {};
 | 
			
		||||
					getConversation(session, {});
 | 
			
		||||
					session.getConversations().then(function(conversations) {
 | 
			
		||||
						print(conversations);
 | 
			
		||||
						for (let j in conversations) {
 | 
			
		||||
							getConversation(session, {conversation: conversations[j]});
 | 
			
		||||
						}
 | 
			
		||||
@@ -207,7 +206,6 @@ function updateConversation() {
 | 
			
		||||
			gCurrentConversation.session.getHistory(gCurrentConversation.name),
 | 
			
		||||
			gCurrentConversation.session.getParticipants(gCurrentConversation.name),
 | 
			
		||||
		]).then(function(data) {
 | 
			
		||||
			print(data);
 | 
			
		||||
			let history = data[0];
 | 
			
		||||
			let participants = data[1];
 | 
			
		||||
			gCurrentConversation.messages = history;
 | 
			
		||||
@@ -232,6 +230,7 @@ function updateUsers() {
 | 
			
		||||
	terminal.clear();
 | 
			
		||||
	terminal.print({style: "font-size: x-large", value: "Users"});
 | 
			
		||||
	if (gCurrentConversation) {
 | 
			
		||||
		gCurrentConversation.participants.sort();
 | 
			
		||||
		for (var i in gCurrentConversation.participants) {
 | 
			
		||||
			terminal.print(gCurrentConversation.participants[i]);
 | 
			
		||||
		}
 | 
			
		||||
@@ -283,20 +282,41 @@ function getConversation(session, message) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function chatCallback(event) {
 | 
			
		||||
	print(event);
 | 
			
		||||
	if (event.action == "message") {
 | 
			
		||||
		let conversation = getConversation(this.session, event);
 | 
			
		||||
		if (conversation == gCurrentConversation) {
 | 
			
		||||
			printMessage(event);
 | 
			
		||||
		}
 | 
			
		||||
		conversation.messages.push(event);
 | 
			
		||||
	try {
 | 
			
		||||
		if (event.action == "message") {
 | 
			
		||||
			let conversation = getConversation(this.session, event);
 | 
			
		||||
			if (conversation == gCurrentConversation) {
 | 
			
		||||
				printMessage(event);
 | 
			
		||||
			}
 | 
			
		||||
			conversation.messages.push(event);
 | 
			
		||||
 | 
			
		||||
		if (!gFocus) {
 | 
			
		||||
			gUnread++;
 | 
			
		||||
			updateTitle();
 | 
			
		||||
			if (!gFocus) {
 | 
			
		||||
				gUnread++;
 | 
			
		||||
				updateTitle();
 | 
			
		||||
			}
 | 
			
		||||
		} else if (event.action == "presence") {
 | 
			
		||||
			let conversation = event.jid.split('/', 2)[0];
 | 
			
		||||
			if (gCurrentConversation.name == conversation) {
 | 
			
		||||
				let index = gCurrentConversation.participants.indexOf(event.name);
 | 
			
		||||
				if (event.type == "unavailable") {
 | 
			
		||||
					if (index != -1) {
 | 
			
		||||
						gCurrentConversation.participants.splice(index, 1);
 | 
			
		||||
						updateUsers();
 | 
			
		||||
						terminal.print(new Date().toString(), ": ", event.name + " has left the room.");
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					if (index == -1) {
 | 
			
		||||
						gCurrentConversation.participants.push(event.name);
 | 
			
		||||
						updateUsers();
 | 
			
		||||
						terminal.print(new Date().toString(), ": ", event.name + " has joined the room.");
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			terminal.print("Unhandled event: ", JSON.stringify(event));
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		terminal.print("Unhandled event: ", JSON.stringify(event));
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		terminal.print("chatCallback: ", error);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -683,7 +683,7 @@ var gPingCount = 0;
 | 
			
		||||
class XmppService {
 | 
			
		||||
	constructor(options) {
 | 
			
		||||
		let self = this;
 | 
			
		||||
		self._callback = options.callback;
 | 
			
		||||
		self._callbacks = [options.callback];
 | 
			
		||||
		self._conversations = {};
 | 
			
		||||
 | 
			
		||||
		network.newConnection().then(function(socket) {
 | 
			
		||||
@@ -716,6 +716,21 @@ class XmppService {
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	invokeCallback(message) {
 | 
			
		||||
		let self = this;
 | 
			
		||||
		for (let i = self._callbacks.length - 1; i >= 0; i--) {
 | 
			
		||||
			let callback = self._callbacks[i];
 | 
			
		||||
			try {
 | 
			
		||||
				callback(message);
 | 
			
		||||
			} catch (error) {
 | 
			
		||||
				self._callbacks.splice(i, 1);
 | 
			
		||||
 | 
			
		||||
				// XXX: Send it to the other connections?
 | 
			
		||||
				print(error);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_connect(options) {
 | 
			
		||||
		let self = this;
 | 
			
		||||
		var kTrustedCertificate = "-----BEGIN CERTIFICATE-----\n" +
 | 
			
		||||
@@ -741,7 +756,7 @@ class XmppService {
 | 
			
		||||
		let server = options.server;
 | 
			
		||||
		self._socket.connect("jabber.troubleimpact.com", 5222).then(function() {
 | 
			
		||||
			print("actually connected");
 | 
			
		||||
			self._callback({action: "connected"});
 | 
			
		||||
			self.invokeCallback({action: "connected"});
 | 
			
		||||
			print("wtf");
 | 
			
		||||
			var parse = new XmlStanzaParser(1);
 | 
			
		||||
			self._socket.write("<?xml version='1.0'?>");
 | 
			
		||||
@@ -753,7 +768,7 @@ class XmppService {
 | 
			
		||||
			self._socket.read(function(data) {
 | 
			
		||||
				try {
 | 
			
		||||
					if (!data) {
 | 
			
		||||
						self._callback({action: "disconnected"});
 | 
			
		||||
						self.invokeCallback({action: "disconnected"});
 | 
			
		||||
						return;
 | 
			
		||||
					}
 | 
			
		||||
					parse.parse(data).forEach(function(stanza) {
 | 
			
		||||
@@ -786,10 +801,10 @@ class XmppService {
 | 
			
		||||
								self._socket.write("<presence to='chadhappyfuntime@conference.jabber.troubleimpact.com/" + userName + "'><priority>1</priority><x xmlns='http://jabber.org/protocol/muc'/></presence>");
 | 
			
		||||
								self._schedulePing();
 | 
			
		||||
								self._conversations["chadhappyfuntime@conference.jabber.troubleimpact.com"] = {participants: [], history: []};
 | 
			
		||||
							} else if (stanza.attributes.id == "ping" + gPingCount) {
 | 
			
		||||
							} else if (stanza.children.length && stanza.children[0].name == "ping") {
 | 
			
		||||
								// Ping response.
 | 
			
		||||
							} else {
 | 
			
		||||
								self._callback({
 | 
			
		||||
								self.invokeCallback({
 | 
			
		||||
									action: "unknown",
 | 
			
		||||
									stanza: stanza,
 | 
			
		||||
								});
 | 
			
		||||
@@ -797,7 +812,7 @@ class XmppService {
 | 
			
		||||
						} else if (stanza.name == "message") {
 | 
			
		||||
							let message = self._convertMessage(stanza);
 | 
			
		||||
							self._conversations[message.conversation].history.push(message);
 | 
			
		||||
							self._callback(message);
 | 
			
		||||
							self.invokeCallback(message);
 | 
			
		||||
						} else if (stanza.name == "challenge") {
 | 
			
		||||
							var challenge = Base64.decode(stanza.text);
 | 
			
		||||
							var parts = challenge.split(',');
 | 
			
		||||
@@ -835,21 +850,22 @@ class XmppService {
 | 
			
		||||
							let name = stanza.attributes.from.split('/', 2)[1];
 | 
			
		||||
							let conversation = stanza.attributes.from.split('/', 2)[0];
 | 
			
		||||
							let leaving = stanza.attributes.type == "unavailable";
 | 
			
		||||
							let index = self._conversations[conversation].participants.indexOf(name);
 | 
			
		||||
							if (leaving) {
 | 
			
		||||
								self._conversations[conversation].participants.remove(name);
 | 
			
		||||
								self._conversations[conversation].participants.splice(index, 1);
 | 
			
		||||
							} else {
 | 
			
		||||
								if (self._conversations[conversation].participants.indexOf(name) == -1) {
 | 
			
		||||
								if (index == -1) {
 | 
			
		||||
									self._conversations[conversation].participants.push(name);
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
							self._callback({
 | 
			
		||||
							self.invokeCallback({
 | 
			
		||||
								action: "presence",
 | 
			
		||||
								name: name,
 | 
			
		||||
								jid: stanza.attributes.from,
 | 
			
		||||
								type: stanza.attributes.type,
 | 
			
		||||
							});
 | 
			
		||||
						} else {
 | 
			
		||||
							self._callback({
 | 
			
		||||
							self.invokeCallback({
 | 
			
		||||
								action: "unknown",
 | 
			
		||||
								stanza: stanza,
 | 
			
		||||
							});
 | 
			
		||||
@@ -869,7 +885,7 @@ class XmppService {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_reportError(error) {
 | 
			
		||||
		this._callback({
 | 
			
		||||
		this.invokeCallback({
 | 
			
		||||
			action: "error",
 | 
			
		||||
			error: error,
 | 
			
		||||
		}).catch(function(error) {
 | 
			
		||||
@@ -929,7 +945,9 @@ core.register("onMessage", function(sender, options) {
 | 
			
		||||
		service = new XmppService(options);
 | 
			
		||||
		gSessions[options.name] = service;
 | 
			
		||||
	} else {
 | 
			
		||||
		service._callback = options.callback;
 | 
			
		||||
		if (service._callbacks.indexOf(options.callback) == -1) {
 | 
			
		||||
			service._callbacks.push(options.callback);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return {
 | 
			
		||||
		sendMessage: service.sendMessage.bind(service),
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user