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:
parent
1c85875db4
commit
e7feab4a4a
@ -281,8 +281,10 @@ function setErrorMessage(message) {
|
|||||||
while (node.firstChild) {
|
while (node.firstChild) {
|
||||||
node.removeChild(node.firstChild);
|
node.removeChild(node.firstChild);
|
||||||
}
|
}
|
||||||
node.appendChild(document.createTextNode(message));
|
if (message) {
|
||||||
node.setAttribute("style", "display: inline; color: #dc322f");
|
node.appendChild(document.createTextNode(message));
|
||||||
|
node.setAttribute("style", "display: inline; color: #dc322f");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function send(command) {
|
function send(command) {
|
||||||
@ -429,11 +431,17 @@ function hashChange() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function focus() {
|
function focus() {
|
||||||
send({event: "focus"});
|
if (gSocket && gSocket.readyState == gSocket.CLOSED) {
|
||||||
|
connectSocket();
|
||||||
|
} else {
|
||||||
|
send({event: "focus"});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function blur() {
|
function blur() {
|
||||||
send({event: "blur"});
|
if (gSocket && gSocket.readyState == gSocket.OPEN) {
|
||||||
|
send({event: "blur"});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMessage(event) {
|
function onMessage(event) {
|
||||||
@ -452,6 +460,39 @@ function submitButton() {
|
|||||||
send({event: "submit", value: data});
|
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() {
|
window.addEventListener("load", function() {
|
||||||
if (window.Notification) {
|
if (window.Notification) {
|
||||||
Notification.requestPermission();
|
Notification.requestPermission();
|
||||||
@ -463,33 +504,7 @@ window.addEventListener("load", function() {
|
|||||||
window.addEventListener("focus", focus);
|
window.addEventListener("focus", focus);
|
||||||
window.addEventListener("blur", blur);
|
window.addEventListener("blur", blur);
|
||||||
window.addEventListener("message", onMessage, false);
|
window.addEventListener("message", onMessage, false);
|
||||||
|
window.addEventListener("online", connectSocket);
|
||||||
enableDragDrop();
|
enableDragDrop();
|
||||||
|
connectSocket();
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
@ -105,7 +105,7 @@ function configureAccount(id, updates) {
|
|||||||
if (schema) {
|
if (schema) {
|
||||||
for (var i in schema) {
|
for (var i in schema) {
|
||||||
let field = schema[i];
|
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 = {};
|
session.conversations = {};
|
||||||
getConversation(session, {});
|
getConversation(session, {});
|
||||||
session.getConversations().then(function(conversations) {
|
session.getConversations().then(function(conversations) {
|
||||||
print(conversations);
|
|
||||||
for (let j in conversations) {
|
for (let j in conversations) {
|
||||||
getConversation(session, {conversation: conversations[j]});
|
getConversation(session, {conversation: conversations[j]});
|
||||||
}
|
}
|
||||||
@ -207,7 +206,6 @@ function updateConversation() {
|
|||||||
gCurrentConversation.session.getHistory(gCurrentConversation.name),
|
gCurrentConversation.session.getHistory(gCurrentConversation.name),
|
||||||
gCurrentConversation.session.getParticipants(gCurrentConversation.name),
|
gCurrentConversation.session.getParticipants(gCurrentConversation.name),
|
||||||
]).then(function(data) {
|
]).then(function(data) {
|
||||||
print(data);
|
|
||||||
let history = data[0];
|
let history = data[0];
|
||||||
let participants = data[1];
|
let participants = data[1];
|
||||||
gCurrentConversation.messages = history;
|
gCurrentConversation.messages = history;
|
||||||
@ -232,6 +230,7 @@ function updateUsers() {
|
|||||||
terminal.clear();
|
terminal.clear();
|
||||||
terminal.print({style: "font-size: x-large", value: "Users"});
|
terminal.print({style: "font-size: x-large", value: "Users"});
|
||||||
if (gCurrentConversation) {
|
if (gCurrentConversation) {
|
||||||
|
gCurrentConversation.participants.sort();
|
||||||
for (var i in gCurrentConversation.participants) {
|
for (var i in gCurrentConversation.participants) {
|
||||||
terminal.print(gCurrentConversation.participants[i]);
|
terminal.print(gCurrentConversation.participants[i]);
|
||||||
}
|
}
|
||||||
@ -283,20 +282,41 @@ function getConversation(session, message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function chatCallback(event) {
|
function chatCallback(event) {
|
||||||
print(event);
|
try {
|
||||||
if (event.action == "message") {
|
if (event.action == "message") {
|
||||||
let conversation = getConversation(this.session, event);
|
let conversation = getConversation(this.session, event);
|
||||||
if (conversation == gCurrentConversation) {
|
if (conversation == gCurrentConversation) {
|
||||||
printMessage(event);
|
printMessage(event);
|
||||||
}
|
}
|
||||||
conversation.messages.push(event);
|
conversation.messages.push(event);
|
||||||
|
|
||||||
if (!gFocus) {
|
if (!gFocus) {
|
||||||
gUnread++;
|
gUnread++;
|
||||||
updateTitle();
|
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 {
|
} catch (error) {
|
||||||
terminal.print("Unhandled event: ", JSON.stringify(event));
|
terminal.print("chatCallback: ", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -683,7 +683,7 @@ var gPingCount = 0;
|
|||||||
class XmppService {
|
class XmppService {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
let self = this;
|
let self = this;
|
||||||
self._callback = options.callback;
|
self._callbacks = [options.callback];
|
||||||
self._conversations = {};
|
self._conversations = {};
|
||||||
|
|
||||||
network.newConnection().then(function(socket) {
|
network.newConnection().then(function(socket) {
|
||||||
@ -716,6 +716,21 @@ class XmppService {
|
|||||||
return result;
|
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) {
|
_connect(options) {
|
||||||
let self = this;
|
let self = this;
|
||||||
var kTrustedCertificate = "-----BEGIN CERTIFICATE-----\n" +
|
var kTrustedCertificate = "-----BEGIN CERTIFICATE-----\n" +
|
||||||
@ -741,7 +756,7 @@ class XmppService {
|
|||||||
let server = options.server;
|
let server = options.server;
|
||||||
self._socket.connect("jabber.troubleimpact.com", 5222).then(function() {
|
self._socket.connect("jabber.troubleimpact.com", 5222).then(function() {
|
||||||
print("actually connected");
|
print("actually connected");
|
||||||
self._callback({action: "connected"});
|
self.invokeCallback({action: "connected"});
|
||||||
print("wtf");
|
print("wtf");
|
||||||
var parse = new XmlStanzaParser(1);
|
var parse = new XmlStanzaParser(1);
|
||||||
self._socket.write("<?xml version='1.0'?>");
|
self._socket.write("<?xml version='1.0'?>");
|
||||||
@ -753,7 +768,7 @@ class XmppService {
|
|||||||
self._socket.read(function(data) {
|
self._socket.read(function(data) {
|
||||||
try {
|
try {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
self._callback({action: "disconnected"});
|
self.invokeCallback({action: "disconnected"});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
parse.parse(data).forEach(function(stanza) {
|
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._socket.write("<presence to='chadhappyfuntime@conference.jabber.troubleimpact.com/" + userName + "'><priority>1</priority><x xmlns='http://jabber.org/protocol/muc'/></presence>");
|
||||||
self._schedulePing();
|
self._schedulePing();
|
||||||
self._conversations["chadhappyfuntime@conference.jabber.troubleimpact.com"] = {participants: [], history: []};
|
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.
|
// Ping response.
|
||||||
} else {
|
} else {
|
||||||
self._callback({
|
self.invokeCallback({
|
||||||
action: "unknown",
|
action: "unknown",
|
||||||
stanza: stanza,
|
stanza: stanza,
|
||||||
});
|
});
|
||||||
@ -797,7 +812,7 @@ class XmppService {
|
|||||||
} else if (stanza.name == "message") {
|
} else if (stanza.name == "message") {
|
||||||
let message = self._convertMessage(stanza);
|
let message = self._convertMessage(stanza);
|
||||||
self._conversations[message.conversation].history.push(message);
|
self._conversations[message.conversation].history.push(message);
|
||||||
self._callback(message);
|
self.invokeCallback(message);
|
||||||
} else if (stanza.name == "challenge") {
|
} else if (stanza.name == "challenge") {
|
||||||
var challenge = Base64.decode(stanza.text);
|
var challenge = Base64.decode(stanza.text);
|
||||||
var parts = challenge.split(',');
|
var parts = challenge.split(',');
|
||||||
@ -835,21 +850,22 @@ class XmppService {
|
|||||||
let name = stanza.attributes.from.split('/', 2)[1];
|
let name = stanza.attributes.from.split('/', 2)[1];
|
||||||
let conversation = stanza.attributes.from.split('/', 2)[0];
|
let conversation = stanza.attributes.from.split('/', 2)[0];
|
||||||
let leaving = stanza.attributes.type == "unavailable";
|
let leaving = stanza.attributes.type == "unavailable";
|
||||||
|
let index = self._conversations[conversation].participants.indexOf(name);
|
||||||
if (leaving) {
|
if (leaving) {
|
||||||
self._conversations[conversation].participants.remove(name);
|
self._conversations[conversation].participants.splice(index, 1);
|
||||||
} else {
|
} else {
|
||||||
if (self._conversations[conversation].participants.indexOf(name) == -1) {
|
if (index == -1) {
|
||||||
self._conversations[conversation].participants.push(name);
|
self._conversations[conversation].participants.push(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self._callback({
|
self.invokeCallback({
|
||||||
action: "presence",
|
action: "presence",
|
||||||
name: name,
|
name: name,
|
||||||
jid: stanza.attributes.from,
|
jid: stanza.attributes.from,
|
||||||
type: stanza.attributes.type,
|
type: stanza.attributes.type,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
self._callback({
|
self.invokeCallback({
|
||||||
action: "unknown",
|
action: "unknown",
|
||||||
stanza: stanza,
|
stanza: stanza,
|
||||||
});
|
});
|
||||||
@ -869,7 +885,7 @@ class XmppService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_reportError(error) {
|
_reportError(error) {
|
||||||
this._callback({
|
this.invokeCallback({
|
||||||
action: "error",
|
action: "error",
|
||||||
error: error,
|
error: error,
|
||||||
}).catch(function(error) {
|
}).catch(function(error) {
|
||||||
@ -929,7 +945,9 @@ core.register("onMessage", function(sender, options) {
|
|||||||
service = new XmppService(options);
|
service = new XmppService(options);
|
||||||
gSessions[options.name] = service;
|
gSessions[options.name] = service;
|
||||||
} else {
|
} else {
|
||||||
service._callback = options.callback;
|
if (service._callbacks.indexOf(options.callback) == -1) {
|
||||||
|
service._callbacks.push(options.callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
sendMessage: service.sendMessage.bind(service),
|
sendMessage: service.sendMessage.bind(service),
|
||||||
|
Loading…
Reference in New Issue
Block a user