diff --git a/images/error_red.png b/images/error_red.png
new file mode 100644
index 00000000..069e12c2
Binary files /dev/null and b/images/error_red.png differ
diff --git a/images/refresh.png b/images/refresh.png
new file mode 100644
index 00000000..94ca8a1b
Binary files /dev/null and b/images/refresh.png differ
diff --git a/index.html b/index.html
index 8d57e41c..808b4e71 100644
--- a/index.html
+++ b/index.html
@@ -18,6 +18,7 @@
New Message
+
diff --git a/js/background.js b/js/background.js
index bf6b8488..8eea93b1 100644
--- a/js/background.js
+++ b/js/background.js
@@ -16,6 +16,7 @@
;(function() {
'use strict';
+ var socket;
var conversations = new Whisper.ConversationCollection();
var messages = new Whisper.MessageCollection();
@@ -29,11 +30,20 @@
}
extension.on('registration_done', init);
+ window.getSocketStatus = function() {
+ if (socket) {
+ return socket.getStatus();
+ } else {
+ return WebSocket.CONNECTING;
+ }
+ };
+
function init() {
if (!textsecure.registration.isDone()) { return; }
// initialize the socket and start listening for messages
- var socket = textsecure.api.getMessageWebsocket();
+ socket = textsecure.api.getMessageWebsocket();
+
new WebSocketResource(socket, function(request) {
// TODO: handle different types of requests. for now we only expect
// PUT /messages
@@ -58,6 +68,14 @@
});
extension.browserAction(window.openInbox);
+
+ // refresh views
+ var views = extension.windows.getViews();
+ for (var i = 0; i < views.length; ++i) {
+ if (views[i] !== window) {
+ views[i].location.reload();
+ }
+ }
}
function onMessageReceived(pushMessage) {
diff --git a/js/chromium.js b/js/chromium.js
index 38b26d2e..c7414f99 100644
--- a/js/chromium.js
+++ b/js/chromium.js
@@ -74,6 +74,10 @@
getBackground: function() {
return chrome.extension.getBackgroundPage();
+ },
+
+ getViews: function() {
+ return chrome.extension.getViews();
}
};
diff --git a/js/libtextsecure.js b/js/libtextsecure.js
index 4ce8f80e..77893689 100644
--- a/js/libtextsecure.js
+++ b/js/libtextsecure.js
@@ -15905,14 +15905,16 @@ window.axolotl.sessions = {
*/
window.textsecure.websocket = function (url) {
- var socketWrapper = {
- onmessage : function() {},
- ondisconnect : function() {},
- };
- var socket;
var keepAliveTimer;
var reconnectSemaphore = 0;
var reconnectTimeout = 1000;
+ var socket;
+ var socketWrapper = {
+ onmessage : function() {},
+ onclose : function() {},
+ onerror : function() {},
+ getStatus : function() { return socket.readyState; }
+ };
function resetKeepAliveTimer() {
clearTimeout(keepAliveTimer);
@@ -15928,10 +15930,27 @@ window.axolotl.sessions = {
}, 15000);
};
- function reconnect(e) {
- reconnectSemaphore--;
- setTimeout(connect, reconnectTimeout);
- socketWrapper.ondisconnect(e);
+ function onclose(e) {
+ if (e.code === 1000) { // CLOSE_NORMAL
+ reconnectSemaphore--;
+ setTimeout(connect, reconnectTimeout);
+ } else {
+ console.log('websocket closed', e.code);
+ }
+ socketWrapper.onclose(e);
+ };
+
+ function onerror(e) {
+ socketWrapper.onerror(e);
+ };
+
+ function onmessage(response) {
+ socketWrapper.onmessage(response);
+ resetKeepAliveTimer();
+ };
+
+ function send(msg) {
+ socket.send(msg);
};
function connect() {
@@ -15941,19 +15960,12 @@ window.axolotl.sessions = {
if (socket) { socket.close(); }
socket = new WebSocket(url);
- socket.onerror = reconnect;
- socket.onclose = reconnect;
- socket.onopen = resetKeepAliveTimer;
-
- socket.onmessage = function(response) {
- socketWrapper.onmessage(response);
- resetKeepAliveTimer();
- };
-
- socketWrapper.send = function(msg) {
- socket.send(msg);
- }
- }
+ socket.onopen = resetKeepAliveTimer;
+ socket.onerror = onerror
+ socket.onclose = onclose;
+ socket.onmessage = onmessage;
+ socketWrapper.send = send;
+ };
connect();
return socketWrapper;
diff --git a/js/views/inbox_view.js b/js/views/inbox_view.js
index cbe87ffa..8f43a257 100644
--- a/js/views/inbox_view.js
+++ b/js/views/inbox_view.js
@@ -19,6 +19,40 @@
window.Whisper = window.Whisper || {};
var bg = extension.windows.getBackground();
+ var SocketView = Whisper.View.extend({
+ className: 'status',
+ initialize: function() {
+ setInterval(function() {
+ var className, message = '';
+ switch(bg.getSocketStatus && bg.getSocketStatus()) {
+ case WebSocket.CONNECTING:
+ className = 'connecting';
+ break;
+ case WebSocket.OPEN:
+ className = 'open';
+ break;
+ case WebSocket.CLOSING:
+ className = 'closing';
+ break;
+ case WebSocket.CLOSED:
+ className = 'closed';
+ message = 'Websocket closed';
+ break;
+ }
+ if (!this.$el.hasClass(className)) {
+ this.$el.attr('class', className);
+ this.$el.text(message);
+ }
+ }.bind(this), 1000);
+ },
+ events: {
+ 'click': 'reloadBackgroundPage'
+ },
+ reloadBackgroundPage: function() {
+ bg.location.reload();
+ }
+ });
+
Whisper.InboxView = Backbone.View.extend({
initialize: function () {
this.$gutter = $('#gutter');
@@ -34,6 +68,8 @@
collection : bg.inbox
}).render();
+ new SocketView().render().$el.appendTo(this.$el.find('.socket-status'));
+
window.addEventListener('beforeunload', function () {
this.inbox.stopListening();
}.bind(this));
diff --git a/libtextsecure/websocket.js b/libtextsecure/websocket.js
index a1c88b57..f6a50f87 100644
--- a/libtextsecure/websocket.js
+++ b/libtextsecure/websocket.js
@@ -25,14 +25,16 @@
*/
window.textsecure.websocket = function (url) {
- var socketWrapper = {
- onmessage : function() {},
- ondisconnect : function() {},
- };
- var socket;
var keepAliveTimer;
var reconnectSemaphore = 0;
var reconnectTimeout = 1000;
+ var socket;
+ var socketWrapper = {
+ onmessage : function() {},
+ onclose : function() {},
+ onerror : function() {},
+ getStatus : function() { return socket.readyState; }
+ };
function resetKeepAliveTimer() {
clearTimeout(keepAliveTimer);
@@ -48,10 +50,27 @@
}, 15000);
};
- function reconnect(e) {
- reconnectSemaphore--;
- setTimeout(connect, reconnectTimeout);
- socketWrapper.ondisconnect(e);
+ function onclose(e) {
+ if (e.code === 1000) { // CLOSE_NORMAL
+ reconnectSemaphore--;
+ setTimeout(connect, reconnectTimeout);
+ } else {
+ console.log('websocket closed', e.code);
+ }
+ socketWrapper.onclose(e);
+ };
+
+ function onerror(e) {
+ socketWrapper.onerror(e);
+ };
+
+ function onmessage(response) {
+ socketWrapper.onmessage(response);
+ resetKeepAliveTimer();
+ };
+
+ function send(msg) {
+ socket.send(msg);
};
function connect() {
@@ -61,19 +80,12 @@
if (socket) { socket.close(); }
socket = new WebSocket(url);
- socket.onerror = reconnect;
- socket.onclose = reconnect;
- socket.onopen = resetKeepAliveTimer;
-
- socket.onmessage = function(response) {
- socketWrapper.onmessage(response);
- resetKeepAliveTimer();
- };
-
- socketWrapper.send = function(msg) {
- socket.send(msg);
- }
- }
+ socket.onopen = resetKeepAliveTimer;
+ socket.onerror = onerror
+ socket.onclose = onclose;
+ socket.onmessage = onmessage;
+ socketWrapper.send = send;
+ };
connect();
return socketWrapper;
diff --git a/stylesheets/_index.scss b/stylesheets/_index.scss
index 5f9f20cb..9ca4e78e 100644
--- a/stylesheets/_index.scss
+++ b/stylesheets/_index.scss
@@ -13,6 +13,31 @@
// TODO: spinner
}
+.socket-status {
+ float: left;
+ padding: 6px;
+
+ * {
+ cursor: pointer;
+ padding-left: 20px;
+ border-radius: $header-height;
+ min-height: 20px;
+
+ &:hover {
+ background: $blue url('/images/refresh.png') center;
+ }
+ }
+ .connecting .icon {
+ background-color: $blue;
+ }
+ .closing {
+ background-color: $blue_l;
+ }
+ .closed {
+ background: url('/images/error_red.png') no-repeat left center;
+ }
+}
+
.contact {
.number, .checkbox {
display: none;
diff --git a/stylesheets/manifest.css b/stylesheets/manifest.css
index 713dbc99..ce5ef48b 100644
--- a/stylesheets/manifest.css
+++ b/stylesheets/manifest.css
@@ -143,6 +143,23 @@ button.back {
#contacts {
overflow: auto; }
+.socket-status {
+ float: left;
+ padding: 6px; }
+ .socket-status * {
+ cursor: pointer;
+ padding-left: 20px;
+ border-radius: 36px;
+ min-height: 20px; }
+ .socket-status *:hover {
+ background: #2a92e7 url("/images/refresh.png") center; }
+ .socket-status .connecting .icon {
+ background-color: #2a92e7; }
+ .socket-status .closing {
+ background-color: #a2d2f4; }
+ .socket-status .closed {
+ background: url("/images/error_red.png") no-repeat left center; }
+
.contact .number, .contact .checkbox {
display: none; }