Style resend button as an inline link

For messages that failed to send due to network errors, this change
allows retrying them directly from the main conversation view rather
than only from the message detail view.

// FREEBIE
pull/749/head
lilia 10 years ago
parent c48484e04f
commit 3901bcb8df

@ -238,5 +238,9 @@
"restartSignal": { "restartSignal": {
"message": "Restart Signal", "message": "Restart Signal",
"description": "Menu item for restarting the program." "description": "Menu item for restarting the program."
},
"messageNotSent": {
"message": "Message not sent.",
"description": "Informational label, appears on messages that failed to send"
} }
} }

@ -110,6 +110,10 @@
<img src='{{ source }}' class='preview' /> <img src='{{ source }}' class='preview' />
<div class='close'>x</div> <div class='close'>x</div>
</script> </script>
<script type='text/x-tmpl-mustache' id='hasRetry'>
{{ messageNotSent }}
<span href='#' class='retry'>{{ resend }}</span>
</script>
<script type='text/x-tmpl-mustache' id='message'> <script type='text/x-tmpl-mustache' id='message'>
{{> avatar }} {{> avatar }}
<div class='bubble'> <div class='bubble'>
@ -196,12 +200,6 @@
</script> </script>
<script type='text/x-tmpl-mustache' id='message-detail'> <script type='text/x-tmpl-mustache' id='message-detail'>
<div class='container'> <div class='container'>
{{ #hasRetry }}
<div class='hasRetry clearfix'>
<h3 class='retryMessage'>{{ failedToSend }}</h3>
<button class='retry'>{{ resend }}</button>
</div>
{{ /hasRetry }}
{{ #hasConflict }} {{ #hasConflict }}
<div class='hasConflict clearfix'> <div class='hasConflict clearfix'>
<div class='conflicts'> <div class='conflicts'>

@ -211,6 +211,14 @@
this.set({errors: errors}); this.set({errors: errors});
}, },
hasNetworkError: function(number) {
var error = _.find(this.get('errors'), function(e) {
return (e.name === 'MessageError' ||
e.name === 'OutgoingMessageError' ||
e.name === 'SendMessageNetworkError');
});
return !!error;
},
removeOutgoingErrors: function(number) { removeOutgoingErrors: function(number) {
var errors = _.partition(this.get('errors'), function(e) { var errors = _.partition(this.get('errors'), function(e) {
return e.number === number && return e.number === number &&

@ -38,8 +38,7 @@
this.listenTo(this.model, 'change', this.render); this.listenTo(this.model, 'change', this.render);
}, },
events: { events: {
'click .back': 'goBack', 'click .back': 'goBack'
'click .retry': 'retryMessage',
}, },
goBack: function() { goBack: function() {
this.trigger('back'); this.trigger('back');
@ -65,16 +64,6 @@
return this.conversation.contactCollection.models; return this.conversation.contactCollection.models;
} }
}, },
retryMessage: function() {
var retrys = _.filter(this.model.get('errors'), function(e) {
return (e.name === 'MessageError' ||
e.name === 'OutgoingMessageError' ||
e.name === 'SendMessageNetworkError');
});
_.map(retrys, 'number').forEach(function(number) {
this.model.resend(number);
}.bind(this));
},
renderContact: function(contact) { renderContact: function(contact) {
var view = new ContactView({ var view = new ContactView({
model: contact, model: contact,
@ -105,11 +94,6 @@
}, },
render: function() { render: function() {
this.errors = _.groupBy(this.model.get('errors'), 'number'); this.errors = _.groupBy(this.model.get('errors'), 'number');
var hasRetry = _.find(this.model.get('errors'), function(e) {
return (e.name === 'MessageError' ||
e.name === 'OutgoingMessageError' ||
e.name === 'SendMessageNetworkError');
});
var unknownErrors = this.errors['undefined']; var unknownErrors = this.errors['undefined'];
if (unknownErrors) { if (unknownErrors) {
unknownErrors = unknownErrors.filter(function(e) { unknownErrors = unknownErrors.filter(function(e) {
@ -124,10 +108,7 @@
title : i18n('messageDetail'), title : i18n('messageDetail'),
sent : i18n('sent'), sent : i18n('sent'),
received : i18n('received'), received : i18n('received'),
resend : i18n('resend'),
failedToSend: i18n('failedToSend'),
errorLabel : i18n('error'), errorLabel : i18n('error'),
hasRetry : hasRetry,
hasConflict : this.model.hasKeyConflicts() hasConflict : this.model.hasKeyConflicts()
})); }));
this.view.$el.prependTo(this.$('.message-container')); this.view.$el.prependTo(this.$('.message-container'));

@ -7,6 +7,16 @@
var URL_REGEX = /(^|[\s\n]|<br\/?>)((?:https?|ftp):\/\/[\-A-Z0-9\u00A0-\uD7FF\uE000-\uFDCF\uFDF0-\uFFFD+\u0026\u2019@#\/%?=()~_|!:,.;]*[\-A-Z0-9+\u0026@#\/%=~()_|])/gi; var URL_REGEX = /(^|[\s\n]|<br\/?>)((?:https?|ftp):\/\/[\-A-Z0-9\u00A0-\uD7FF\uE000-\uFDCF\uFDF0-\uFFFD+\u0026\u2019@#\/%?=()~_|!:,.;]*[\-A-Z0-9+\u0026@#\/%=~()_|])/gi;
var NetworkErrorView = Whisper.View.extend({
tagName: 'span',
className: 'hasRetry',
templateName: 'hasRetry',
render_attributes: {
messageNotSent: i18n('messageNotSent'),
resend: i18n('resend')
}
});
Whisper.MessageView = Whisper.View.extend({ Whisper.MessageView = Whisper.View.extend({
tagName: 'li', tagName: 'li',
templateName: 'message', templateName: 'message',
@ -22,9 +32,21 @@
this.timeStampView = new Whisper.ExtendedTimestampView(); this.timeStampView = new Whisper.ExtendedTimestampView();
}, },
events: { events: {
'click .meta': 'select', 'click .retry': 'retryMessage',
'click .timestamp': 'select',
'click .status': 'select',
'click .error': 'select' 'click .error': 'select'
}, },
retryMessage: function() {
var retrys = _.filter(this.model.get('errors'), function(e) {
return (e.name === 'MessageError' ||
e.name === 'OutgoingMessageError' ||
e.name === 'SendMessageNetworkError');
});
_.map(retrys, 'number').forEach(function(number) {
this.model.resend(number);
}.bind(this));
},
select: function(e) { select: function(e) {
this.$el.trigger('select', {message: this.model}); this.$el.trigger('select', {message: this.model});
e.stopPropagation(); e.stopPropagation();
@ -63,6 +85,11 @@
} else { } else {
this.$el.removeClass('error'); this.$el.removeClass('error');
} }
if (this.model.hasNetworkError()) {
this.$('.meta').prepend(new NetworkErrorView().render().el);
} else {
this.$('.meta .hasRetry').remove();
}
}, },
renderControl: function() { renderControl: function() {
if (this.model.isEndSession() || this.model.isGroupUpdate()) { if (this.model.isEndSession() || this.model.isGroupUpdate()) {
@ -79,7 +106,7 @@
message: this.model.get('body'), message: this.model.get('body'),
timestamp: this.model.get('sent_at'), timestamp: this.model.get('sent_at'),
sender: (contact && contact.getTitle()) || '', sender: (contact && contact.getTitle()) || '',
avatar: (contact && contact.getAvatar()) avatar: (contact && contact.getAvatar()),
}, this.render_partials()) }, this.render_partials())
); );
this.timeStampView.setElement(this.$('.timestamp')); this.timeStampView.setElement(this.$('.timestamp'));

@ -70,8 +70,7 @@
} }
.message-detail { .message-detail {
.key-conflict-dialogue, .key-conflict-dialogue {
.hasRetry {
background: #F3F3A7; background: #F3F3A7;
border-radius: 5px; border-radius: 5px;
padding: 1em; padding: 1em;
@ -92,24 +91,6 @@
} }
} }
.hasRetry {
padding: 10px 20px;
button {
margin: 5px;
background: $blue;
&:before {
content: '';
display: inline-block;
vertical-align: middle;
width: 18px;
height: 18px;
background: url('/images/refresh.png') no-repeat center center;
background-size: 100%;
}
}
}
.key-conflict-dialogue { .key-conflict-dialogue {
.content p { .content p {
max-width: 40em; max-width: 40em;
@ -247,7 +228,6 @@
} }
.timestamp { .timestamp {
font-size: smaller;
margin-right: 3px; margin-right: 3px;
} }
@ -300,28 +280,43 @@
} }
.meta { .meta {
font-size: smaller;
margin-top: 3px; margin-top: 3px;
float: right; float: right;
cursor: pointer;
.hasRetry + .timestamp {
&:before {
content:"\00b7"; // &middot
font-weight: bold;
padding: 0 5px 0 4px;
text-decoration: none;
opacity: 0.5;
}
}
.retry {
text-decoration: underline;
cursor: pointer;
}
.hasRetry, .timestamp, .status {
float: left;
}
.timestamp, .status { .timestamp, .status {
cursor: pointer;
opacity: 0.5; opacity: 0.5;
}
&:hover { &:hover {
.timestamp, .status {
opacity: 1.0; opacity: 1.0;
} }
.timestamp {
text-decoration: underline;
}
} }
} }
.status { .status {
float: right;
width: 18px; width: 18px;
height: 1em; height: 14px;
line-height: 1em;
} }
.sent .status { .sent .status {
display: inline-block; display: inline-block;

@ -675,14 +675,12 @@ input.search {
.key-verification .placeholder { .key-verification .placeholder {
font-weight: bold; } font-weight: bold; }
.message-detail .key-conflict-dialogue, .message-detail .key-conflict-dialogue {
.message-detail .hasRetry {
background: #F3F3A7; background: #F3F3A7;
border-radius: 5px; border-radius: 5px;
padding: 1em; padding: 1em;
margin: 1em; } margin: 1em; }
.message-detail .key-conflict-dialogue button, .message-detail .key-conflict-dialogue button {
.message-detail .hasRetry button {
outline: none; outline: none;
border: none; border: none;
border-radius: 5px; border-radius: 5px;
@ -690,22 +688,8 @@ input.search {
padding: 0.5em 1em; padding: 0.5em 1em;
font-weight: bold; font-weight: bold;
line-height: 18px; } line-height: 18px; }
.message-detail .key-conflict-dialogue button span, .message-detail .key-conflict-dialogue button span {
.message-detail .hasRetry button span {
vertical-align: middle; } vertical-align: middle; }
.message-detail .hasRetry {
padding: 10px 20px; }
.message-detail .hasRetry button {
margin: 5px;
background: #2090ea; }
.message-detail .hasRetry button:before {
content: '';
display: inline-block;
vertical-align: middle;
width: 18px;
height: 18px;
background: url("/images/refresh.png") no-repeat center center;
background-size: 100%; }
.message-detail .key-conflict-dialogue .content p { .message-detail .key-conflict-dialogue .content p {
max-width: 40em; max-width: 40em;
margin: 1em auto; } margin: 1em auto; }
@ -807,7 +791,6 @@ input.search {
font-weight: bold; } font-weight: bold; }
.timestamp { .timestamp {
font-size: smaller;
margin-right: 3px; } margin-right: 3px; }
.message-container, .message-container,
@ -853,25 +836,39 @@ input.search {
margin: 0; } margin: 0; }
.message-container .meta, .message-container .meta,
.message-list .meta { .message-list .meta {
font-size: smaller;
margin-top: 3px; margin-top: 3px;
float: right; float: right; }
cursor: pointer; } .message-container .meta .hasRetry + .timestamp:before,
.message-list .meta .hasRetry + .timestamp:before {
content: "\00b7";
font-weight: bold;
padding: 0 5px 0 4px;
text-decoration: none;
opacity: 0.5; }
.message-container .meta .retry,
.message-list .meta .retry {
text-decoration: underline;
cursor: pointer; }
.message-container .meta .hasRetry, .message-container .meta .timestamp, .message-container .meta .status,
.message-list .meta .hasRetry,
.message-list .meta .timestamp,
.message-list .meta .status {
float: left; }
.message-container .meta .timestamp, .message-container .meta .status, .message-container .meta .timestamp, .message-container .meta .status,
.message-list .meta .timestamp, .message-list .meta .timestamp,
.message-list .meta .status { .message-list .meta .status {
cursor: pointer;
opacity: 0.5; } opacity: 0.5; }
.message-container .meta:hover .timestamp, .message-container .meta:hover .status, .message-container .meta .timestamp:hover, .message-container .meta .status:hover,
.message-list .meta:hover .timestamp, .message-list .meta .timestamp:hover,
.message-list .meta:hover .status { .message-list .meta .status:hover {
opacity: 1.0; } opacity: 1.0; }
.message-container .meta:hover .timestamp,
.message-list .meta:hover .timestamp {
text-decoration: underline; }
.message-container .status, .message-container .status,
.message-list .status { .message-list .status {
float: right;
width: 18px; width: 18px;
height: 1em; } height: 14px;
line-height: 1em; }
.message-container .sent .status, .message-container .sent .status,
.message-list .sent .status { .message-list .sent .status {
display: inline-block; display: inline-block;

Loading…
Cancel
Save