mirror of
https://we.phorge.it/source/phorge.git
synced 2024-09-21 09:48:47 +02:00
02194f0fc8
Summary: Fixes T12563. If we've ever seen an "open", mark all future connections as reconnects. When we reconnect, replay recent history. (Until duplicate messages (T12564) are handled better this may cause some notification duplication.) Also emit a reconnect event (for T12566) but don't use it yet. Test Plan: {F4912044} Reviewers: chad Reviewed By: chad Maniphest Tasks: T12563 Differential Revision: https://secure.phabricator.com/D17708
217 lines
4.9 KiB
JavaScript
217 lines
4.9 KiB
JavaScript
/**
|
|
* @provides javelin-aphlict
|
|
* @requires javelin-install
|
|
* javelin-util
|
|
* javelin-websocket
|
|
* javelin-leader
|
|
* javelin-json
|
|
*/
|
|
|
|
/**
|
|
* Client for the notification server. Example usage:
|
|
*
|
|
* var aphlict = new JX.Aphlict('ws://localhost:22280', subscriptions)
|
|
* .setHandler(function(message) {
|
|
* // ...
|
|
* })
|
|
* .start();
|
|
*
|
|
*/
|
|
JX.install('Aphlict', {
|
|
|
|
construct: function(uri, subscriptions) {
|
|
if (__DEV__) {
|
|
if (JX.Aphlict._instance) {
|
|
JX.$E('Aphlict object is a singleton.');
|
|
}
|
|
}
|
|
|
|
this._uri = uri;
|
|
this._subscriptions = subscriptions;
|
|
this._setStatus('setup');
|
|
|
|
JX.Aphlict._instance = this;
|
|
},
|
|
|
|
events: ['didChangeStatus'],
|
|
|
|
members: {
|
|
_uri: null,
|
|
_socket: null,
|
|
_subscriptions: null,
|
|
_status: null,
|
|
_isReconnect: false,
|
|
|
|
start: function() {
|
|
JX.Leader.listen('onBecomeLeader', JX.bind(this, this._lead));
|
|
JX.Leader.listen('onReceiveBroadcast', JX.bind(this, this._receive));
|
|
JX.Leader.start();
|
|
|
|
JX.Leader.call(JX.bind(this, this._begin));
|
|
},
|
|
|
|
getSubscriptions: function() {
|
|
return this._subscriptions;
|
|
},
|
|
|
|
setSubscriptions: function(subscriptions) {
|
|
this._subscriptions = subscriptions;
|
|
JX.Leader.broadcast(
|
|
null,
|
|
{type: 'aphlict.subscribe', data: this._subscriptions});
|
|
},
|
|
|
|
clearSubscriptions: function(subscriptions) {
|
|
this._subscriptions = null;
|
|
JX.Leader.broadcast(
|
|
null,
|
|
{type: 'aphlict.unsubscribe', data: subscriptions});
|
|
},
|
|
|
|
getStatus: function() {
|
|
return this._status;
|
|
},
|
|
|
|
getWebsocket: function() {
|
|
return this._socket;
|
|
},
|
|
|
|
_begin: function() {
|
|
JX.Leader.broadcast(
|
|
null,
|
|
{type: 'aphlict.getstatus'});
|
|
JX.Leader.broadcast(
|
|
null,
|
|
{type: 'aphlict.subscribe', data: this._subscriptions});
|
|
},
|
|
|
|
_lead: function() {
|
|
this._socket = new JX.WebSocket(this._uri);
|
|
this._socket.setOpenHandler(JX.bind(this, this._open));
|
|
this._socket.setMessageHandler(JX.bind(this, this._message));
|
|
this._socket.setCloseHandler(JX.bind(this, this._close));
|
|
|
|
this._socket.open();
|
|
},
|
|
|
|
_open: function() {
|
|
// If this is a reconnect, ask the server to replay recent messages
|
|
// after other tabs have had a chance to subscribe. Do this before we
|
|
// broadcast that the connection status is now open.
|
|
if (this._isReconnect) {
|
|
setTimeout(JX.bind(this, this._reconnect), 100);
|
|
}
|
|
|
|
this._broadcastStatus('open');
|
|
JX.Leader.broadcast(null, {type: 'aphlict.getsubscribers'});
|
|
},
|
|
|
|
_reconnect: function() {
|
|
this.replay();
|
|
|
|
JX.Leader.broadcast(null, {type: 'aphlict.reconnect', data: null});
|
|
},
|
|
|
|
replay: function() {
|
|
var replay = {
|
|
age: 60000
|
|
};
|
|
|
|
JX.Leader.broadcast(null, {type: 'aphlict.replay', data: replay});
|
|
},
|
|
|
|
_close: function() {
|
|
this._broadcastStatus('closed');
|
|
},
|
|
|
|
_broadcastStatus: function(status) {
|
|
JX.Leader.broadcast(null, {type: 'aphlict.status', data: status});
|
|
},
|
|
|
|
_message: function(raw) {
|
|
var message = JX.JSON.parse(raw);
|
|
JX.Leader.broadcast(null, {type: 'aphlict.server', data: message});
|
|
},
|
|
|
|
_receive: function(message, is_leader) {
|
|
switch (message.type) {
|
|
case 'aphlict.status':
|
|
this._setStatus(message.data);
|
|
break;
|
|
|
|
case 'aphlict.getstatus':
|
|
if (is_leader) {
|
|
this._broadcastStatus(this.getStatus());
|
|
}
|
|
break;
|
|
|
|
case 'aphlict.getsubscribers':
|
|
JX.Leader.broadcast(
|
|
null,
|
|
{type: 'aphlict.subscribe', data: this._subscriptions});
|
|
break;
|
|
|
|
case 'aphlict.subscribe':
|
|
if (is_leader) {
|
|
this._writeCommand('subscribe', message.data);
|
|
}
|
|
break;
|
|
|
|
case 'aphlict.replay':
|
|
if (is_leader) {
|
|
this._writeCommand('replay', message.data);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
var handler = this.getHandler();
|
|
handler && handler(message);
|
|
break;
|
|
}
|
|
},
|
|
|
|
_setStatus: function(status) {
|
|
this._status = status;
|
|
|
|
// If we've ever seen an open connection, any new connection we make
|
|
// is a reconnect and should replay history.
|
|
if (status == 'open') {
|
|
this._isReconnect = true;
|
|
}
|
|
|
|
this.invoke('didChangeStatus');
|
|
},
|
|
|
|
_write: function(message) {
|
|
this._socket.send(JX.JSON.stringify(message));
|
|
},
|
|
|
|
_writeCommand: function(command, message) {
|
|
var frame = {
|
|
command: command,
|
|
data: message
|
|
};
|
|
|
|
return this._write(frame);
|
|
}
|
|
|
|
},
|
|
|
|
properties: {
|
|
handler: null
|
|
},
|
|
|
|
statics: {
|
|
_instance: null,
|
|
|
|
getInstance: function() {
|
|
var self = JX.Aphlict;
|
|
if (!self._instance) {
|
|
return null;
|
|
}
|
|
return self._instance;
|
|
}
|
|
|
|
}
|
|
|
|
});
|