diff --git a/background.html b/background.html
index eb902fcb..13e3ddf9 100644
--- a/background.html
+++ b/background.html
@@ -248,6 +248,10 @@
{{ #conflict }}
{{ /conflict }}
+ {{ #retry }}
+
+ {{message}}
+ {{ /retry }}
{{ #errors }}
{{message}}
diff --git a/js/libtextsecure.js b/js/libtextsecure.js
index daebf462..addc3e40 100644
--- a/js/libtextsecure.js
+++ b/js/libtextsecure.js
@@ -7,9 +7,9 @@
var registeredFunctions = {};
var Type = {
- SEND_MESSAGE: 1,
+ ENCRYPT_MESSAGE: 1,
INIT_SESSION: 2,
- NETWORK_REQUEST: 3,
+ TRANSMIT_MESSAGE: 3,
};
window.textsecure = window.textsecure || {};
window.textsecure.replay = {
@@ -48,7 +48,7 @@
function OutgoingIdentityKeyError(number, message, timestamp, identityKey) {
ReplayableError.call(this, {
- functionCode : Type.SEND_MESSAGE,
+ functionCode : Type.ENCRYPT_MESSAGE,
args : [number, message, timestamp]
});
this.name = 'OutgoingIdentityKeyError';
@@ -59,23 +59,40 @@
OutgoingIdentityKeyError.prototype = new ReplayableError();
OutgoingIdentityKeyError.prototype.constructor = OutgoingIdentityKeyError;
- function NetworkError(number, jsonData, legacy, code) {
+ function OutgoingMessageError(number, message, timestamp, httpError) {
ReplayableError.call(this, {
- functionCode : Type.NETWORK_REQUEST,
+ functionCode : Type.ENCRYPT_MESSAGE,
+ args : [number, message, timestamp]
+ });
+ this.name = 'OutgoingMessageError';
+ if (httpError) {
+ this.code = httpError.code;
+ this.message = httpError.message;
+ this.stack = httpError.stack;
+ }
+ }
+ OutgoingMessageError.prototype = new ReplayableError();
+ OutgoingMessageError.prototype.constructor = OutgoingMessageError;
+
+ function SendMessageNetworkError(number, jsonData, legacy, httpError) {
+ ReplayableError.call(this, {
+ functionCode : Type.TRANSMIT_MESSAGE,
args : [number, jsonData, legacy]
});
- this.name = 'NetworkError';
- this.message = 'Network request failed'
- this.code = code;
+ this.name = 'SendMessageNetworkError';
this.number = number;
+ this.code = httpError.code;
+ this.message = httpError.message;
+ this.stack = httpError.stack;
}
- NetworkError.prototype = new ReplayableError();
- NetworkError.prototype.constructor = NetworkError;
+ SendMessageNetworkError.prototype = new ReplayableError();
+ SendMessageNetworkError.prototype.constructor = SendMessageNetworkError;
- window.textsecure.NetworkError = NetworkError;
+ window.textsecure.SendMessageNetworkError = SendMessageNetworkError;
window.textsecure.IncomingIdentityKeyError = IncomingIdentityKeyError;
window.textsecure.OutgoingIdentityKeyError = OutgoingIdentityKeyError;
window.textsecure.ReplayableError = ReplayableError;
+ window.textsecure.OutgoingMessageError = OutgoingMessageError;
})();
@@ -39637,14 +39654,14 @@ MessageSender.prototype = {
});
})).then(function(jsonData) {
var legacy = (message instanceof textsecure.protobuf.DataMessage);
- return this.sendRequest(number, jsonData, legacy);
+ return this.sendMessageRequest(number, jsonData, legacy);
}.bind(this));
},
- sendRequest: function(number, jsonData, legacy) {
+ sendMessageRequest: function(number, jsonData, legacy) {
return this.server.sendMessages(number, jsonData, legacy).catch(function(e) {
- if (e.name === 'HTTPError' && e.code === -1) {
- throw new NetworkError(number, jsonData, legacy);
+ if (e.name === 'HTTPError' && (e.code !== 409 && e.code !== 410)) {
+ throw new textsecure.SendMessageNetworkError(number, jsonData, legacy, e);
}
throw e;
});
@@ -39684,9 +39701,10 @@ MessageSender.prototype = {
};
var registerError = function(number, reason, error) {
- if (!error) {
- error = new Error(reason);
+ if (!error || error.name === 'HTTPError') {
+ error = new textsecure.OutgoingMessageError(number, message.toArrayBuffer(), timestamp, error);
}
+
error.number = number;
error.reason = reason;
errors[errors.length] = error;
@@ -40025,8 +40043,8 @@ window.textsecure = window.textsecure || {};
textsecure.MessageSender = function(url, username, password) {
var sender = new MessageSender(url, username, password);
- textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.SEND_MESSAGE);
- textsecure.replay.registerFunction(sender.sendRequest.bind(sender), textsecure.replay.Type.NETWORK_REQUEST);
+ textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.ENCRYPT_MESSAGE);
+ textsecure.replay.registerFunction(sender.sendMessageRequest.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE);
this.sendRequestGroupSyncMessage = sender.sendRequestGroupSyncMessage .bind(sender);
this.sendRequestContactSyncMessage = sender.sendRequestContactSyncMessage.bind(sender);
diff --git a/js/models/messages.js b/js/models/messages.js
index be7bcbbe..a2a9a4c2 100644
--- a/js/models/messages.js
+++ b/js/models/messages.js
@@ -175,8 +175,18 @@
this.set({errors: errors});
},
+ removeOutgoingErrors: function(number) {
+ var errors = _.partition(this.get('errors'), function(e) {
+ return e.number === number &&
+ (e.name === 'OutgoingMessageError' ||
+ e.name === 'SendMessageNetworkError');
+ });
+ this.set({errors: errors[1]});
+ return errors[0][0];
+ },
+
resend: function(number) {
- var error = this.getSendError(number);
+ var error = this.removeOutgoingErrors(number);
if (error) {
var promise = new textsecure.ReplayableError(error).replay();
this.send(promise);
diff --git a/js/views/message_detail_view.js b/js/views/message_detail_view.js
index 2f0b8e9b..9a2298db 100644
--- a/js/views/message_detail_view.js
+++ b/js/views/message_detail_view.js
@@ -10,22 +10,33 @@
templateName: 'contact-detail',
initialize: function(options) {
this.conflict = options.conflict;
+ this.retry = _.find(options.errors, function(e) {
+ return (e.name === 'OutgoingMessageError' ||
+ e.name === 'SendMessageNetworkError');
+ });
this.errors = _.reject(options.errors, function(e) {
return (e.name === 'IncomingIdentityKeyError' ||
- e.name === 'OutgoingIdentityKeyError');
+ e.name === 'OutgoingIdentityKeyError' ||
+ e.name === 'OutgoingMessageError' ||
+ e.name === 'SendMessageNetworkError');
});
},
events: {
- 'click .conflict': 'triggerConflict'
+ 'click .conflict': 'triggerConflict',
+ 'click .retry': 'triggerRetry'
},
triggerConflict: function() {
this.$el.trigger('conflict', {conflict: this.conflict});
},
+ triggerRetry: function() {
+ this.$el.trigger('retry', {error: this.retry});
+ },
render_attributes: function() {
return {
name : this.model.getTitle(),
avatar : this.model.getAvatar(),
conflict : this.conflict,
+ retry : this.retry,
errors : this.errors
};
}
@@ -43,7 +54,8 @@
},
events: {
'click .back': 'goBack',
- 'conflict': 'conflictDialogue'
+ 'conflict': 'conflictDialogue',
+ 'retry': 'retryMessage',
},
goBack: function() {
this.trigger('back');
@@ -82,6 +94,9 @@
this.render();
});
},
+ retryMessage: function(e, data) {
+ this.model.resend(data.error.number);
+ },
renderContact: function(contact) {
var v = new ContactView({
model: contact,
diff --git a/libtextsecure/errors.js b/libtextsecure/errors.js
index d0eb5f3c..4f1d2c6f 100644
--- a/libtextsecure/errors.js
+++ b/libtextsecure/errors.js
@@ -6,9 +6,9 @@
var registeredFunctions = {};
var Type = {
- SEND_MESSAGE: 1,
+ ENCRYPT_MESSAGE: 1,
INIT_SESSION: 2,
- NETWORK_REQUEST: 3,
+ TRANSMIT_MESSAGE: 3,
};
window.textsecure = window.textsecure || {};
window.textsecure.replay = {
@@ -47,7 +47,7 @@
function OutgoingIdentityKeyError(number, message, timestamp, identityKey) {
ReplayableError.call(this, {
- functionCode : Type.SEND_MESSAGE,
+ functionCode : Type.ENCRYPT_MESSAGE,
args : [number, message, timestamp]
});
this.name = 'OutgoingIdentityKeyError';
@@ -58,22 +58,39 @@
OutgoingIdentityKeyError.prototype = new ReplayableError();
OutgoingIdentityKeyError.prototype.constructor = OutgoingIdentityKeyError;
- function NetworkError(number, jsonData, legacy, code) {
+ function OutgoingMessageError(number, message, timestamp, httpError) {
ReplayableError.call(this, {
- functionCode : Type.NETWORK_REQUEST,
+ functionCode : Type.ENCRYPT_MESSAGE,
+ args : [number, message, timestamp]
+ });
+ this.name = 'OutgoingMessageError';
+ if (httpError) {
+ this.code = httpError.code;
+ this.message = httpError.message;
+ this.stack = httpError.stack;
+ }
+ }
+ OutgoingMessageError.prototype = new ReplayableError();
+ OutgoingMessageError.prototype.constructor = OutgoingMessageError;
+
+ function SendMessageNetworkError(number, jsonData, legacy, httpError) {
+ ReplayableError.call(this, {
+ functionCode : Type.TRANSMIT_MESSAGE,
args : [number, jsonData, legacy]
});
- this.name = 'NetworkError';
- this.message = 'Network request failed'
- this.code = code;
+ this.name = 'SendMessageNetworkError';
this.number = number;
+ this.code = httpError.code;
+ this.message = httpError.message;
+ this.stack = httpError.stack;
}
- NetworkError.prototype = new ReplayableError();
- NetworkError.prototype.constructor = NetworkError;
+ SendMessageNetworkError.prototype = new ReplayableError();
+ SendMessageNetworkError.prototype.constructor = SendMessageNetworkError;
- window.textsecure.NetworkError = NetworkError;
+ window.textsecure.SendMessageNetworkError = SendMessageNetworkError;
window.textsecure.IncomingIdentityKeyError = IncomingIdentityKeyError;
window.textsecure.OutgoingIdentityKeyError = OutgoingIdentityKeyError;
window.textsecure.ReplayableError = ReplayableError;
+ window.textsecure.OutgoingMessageError = OutgoingMessageError;
})();
diff --git a/libtextsecure/sendmessage.js b/libtextsecure/sendmessage.js
index bc70ab94..abd968e0 100644
--- a/libtextsecure/sendmessage.js
+++ b/libtextsecure/sendmessage.js
@@ -37,14 +37,14 @@ MessageSender.prototype = {
});
})).then(function(jsonData) {
var legacy = (message instanceof textsecure.protobuf.DataMessage);
- return this.sendRequest(number, jsonData, legacy);
+ return this.sendMessageRequest(number, jsonData, legacy);
}.bind(this));
},
- sendRequest: function(number, jsonData, legacy) {
+ sendMessageRequest: function(number, jsonData, legacy) {
return this.server.sendMessages(number, jsonData, legacy).catch(function(e) {
- if (e.name === 'HTTPError' && e.code === -1) {
- throw new NetworkError(number, jsonData, legacy);
+ if (e.name === 'HTTPError' && (e.code !== 409 && e.code !== 410)) {
+ throw new textsecure.SendMessageNetworkError(number, jsonData, legacy, e);
}
throw e;
});
@@ -84,9 +84,10 @@ MessageSender.prototype = {
};
var registerError = function(number, reason, error) {
- if (!error) {
- error = new Error(reason);
+ if (!error || error.name === 'HTTPError') {
+ error = new textsecure.OutgoingMessageError(number, message.toArrayBuffer(), timestamp, error);
}
+
error.number = number;
error.reason = reason;
errors[errors.length] = error;
@@ -425,8 +426,8 @@ window.textsecure = window.textsecure || {};
textsecure.MessageSender = function(url, username, password) {
var sender = new MessageSender(url, username, password);
- textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.SEND_MESSAGE);
- textsecure.replay.registerFunction(sender.sendRequest.bind(sender), textsecure.replay.Type.NETWORK_REQUEST);
+ textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.ENCRYPT_MESSAGE);
+ textsecure.replay.registerFunction(sender.sendMessageRequest.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE);
this.sendRequestGroupSyncMessage = sender.sendRequestGroupSyncMessage .bind(sender);
this.sendRequestContactSyncMessage = sender.sendRequestContactSyncMessage.bind(sender);
diff --git a/stylesheets/_conversation.scss b/stylesheets/_conversation.scss
index 6b40562e..58ba0515 100644
--- a/stylesheets/_conversation.scss
+++ b/stylesheets/_conversation.scss
@@ -61,13 +61,18 @@
}
}
- .conflict {
+ .contact-detail button {
border: none;
border-radius: 5px;
color: white;
padding: 0.5em;
font-weight: bold;
- background: #d00;
+ background: $blue;
+
+ span {
+ vertical-align: middle;
+ padding-left: 5px;
+ }
&:before {
content: '';
@@ -75,13 +80,15 @@
vertical-align: middle;
width: 18px;
height: 18px;
- background: url('/images/error.png') no-repeat center center;
+ background: url('/images/refresh.png') no-repeat center center;
background-size: 100%;
}
+ }
+ .conflict {
+ background: #d00;
- span {
- vertical-align: middle;
- padding-left: 5px;
+ &:before {
+ background: url('/images/error.png') no-repeat center center;
}
}
}
diff --git a/stylesheets/manifest.css b/stylesheets/manifest.css
index 9787f87a..8234fc8a 100644
--- a/stylesheets/manifest.css
+++ b/stylesheets/manifest.css
@@ -550,24 +550,28 @@ input.search {
font-weight: bold;
padding-right: 1em;
vertical-align: top; }
- .message-detail .conflict {
+ .message-detail .contact-detail button {
border: none;
border-radius: 5px;
color: white;
padding: 0.5em;
font-weight: bold;
- background: #d00; }
- .message-detail .conflict:before {
+ background: #2090ea; }
+ .message-detail .contact-detail button span {
+ vertical-align: middle;
+ padding-left: 5px; }
+ .message-detail .contact-detail button:before {
content: '';
display: inline-block;
vertical-align: middle;
width: 18px;
height: 18px;
- background: url("/images/error.png") no-repeat center center;
+ background: url("/images/refresh.png") no-repeat center center;
background-size: 100%; }
- .message-detail .conflict span {
- vertical-align: middle;
- padding-left: 5px; }
+ .message-detail .conflict {
+ background: #d00; }
+ .message-detail .conflict:before {
+ background: url("/images/error.png") no-repeat center center; }
.group-update {
font-size: smaller; }