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; }