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
This commit is contained in:
lilia 2016-03-22 14:47:17 -07:00
parent c48484e04f
commit 3901bcb8df
7 changed files with 100 additions and 90 deletions

View file

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

View file

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

View file

@ -211,6 +211,14 @@
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) {
var errors = _.partition(this.get('errors'), function(e) {
return e.number === number &&

View file

@ -38,8 +38,7 @@
this.listenTo(this.model, 'change', this.render);
},
events: {
'click .back': 'goBack',
'click .retry': 'retryMessage',
'click .back': 'goBack'
},
goBack: function() {
this.trigger('back');
@ -65,16 +64,6 @@
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) {
var view = new ContactView({
model: contact,
@ -105,11 +94,6 @@
},
render: function() {
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'];
if (unknownErrors) {
unknownErrors = unknownErrors.filter(function(e) {
@ -124,10 +108,7 @@
title : i18n('messageDetail'),
sent : i18n('sent'),
received : i18n('received'),
resend : i18n('resend'),
failedToSend: i18n('failedToSend'),
errorLabel : i18n('error'),
hasRetry : hasRetry,
hasConflict : this.model.hasKeyConflicts()
}));
this.view.$el.prependTo(this.$('.message-container'));

View file

@ -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 NetworkErrorView = Whisper.View.extend({
tagName: 'span',
className: 'hasRetry',
templateName: 'hasRetry',
render_attributes: {
messageNotSent: i18n('messageNotSent'),
resend: i18n('resend')
}
});
Whisper.MessageView = Whisper.View.extend({
tagName: 'li',
templateName: 'message',
@ -22,9 +32,21 @@
this.timeStampView = new Whisper.ExtendedTimestampView();
},
events: {
'click .meta': 'select',
'click .retry': 'retryMessage',
'click .timestamp': 'select',
'click .status': '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) {
this.$el.trigger('select', {message: this.model});
e.stopPropagation();
@ -63,6 +85,11 @@
} else {
this.$el.removeClass('error');
}
if (this.model.hasNetworkError()) {
this.$('.meta').prepend(new NetworkErrorView().render().el);
} else {
this.$('.meta .hasRetry').remove();
}
},
renderControl: function() {
if (this.model.isEndSession() || this.model.isGroupUpdate()) {
@ -79,7 +106,7 @@
message: this.model.get('body'),
timestamp: this.model.get('sent_at'),
sender: (contact && contact.getTitle()) || '',
avatar: (contact && contact.getAvatar())
avatar: (contact && contact.getAvatar()),
}, this.render_partials())
);
this.timeStampView.setElement(this.$('.timestamp'));

View file

@ -70,8 +70,7 @@
}
.message-detail {
.key-conflict-dialogue,
.hasRetry {
.key-conflict-dialogue {
background: #F3F3A7;
border-radius: 5px;
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 {
.content p {
max-width: 40em;
@ -247,7 +228,6 @@
}
.timestamp {
font-size: smaller;
margin-right: 3px;
}
@ -300,28 +280,43 @@
}
.meta {
font-size: smaller;
margin-top: 3px;
float: right;
cursor: pointer;
.timestamp, .status {
opacity: 0.5;
.hasRetry + .timestamp {
&:before {
content:"\00b7"; // &middot
font-weight: bold;
padding: 0 5px 0 4px;
text-decoration: none;
opacity: 0.5;
}
}
&:hover {
.timestamp, .status {
.retry {
text-decoration: underline;
cursor: pointer;
}
.hasRetry, .timestamp, .status {
float: left;
}
.timestamp, .status {
cursor: pointer;
opacity: 0.5;
&:hover {
opacity: 1.0;
}
.timestamp {
text-decoration: underline;
}
}
}
.status {
float: right;
width: 18px;
height: 1em;
height: 14px;
line-height: 1em;
}
.sent .status {
display: inline-block;

View file

@ -675,14 +675,12 @@ input.search {
.key-verification .placeholder {
font-weight: bold; }
.message-detail .key-conflict-dialogue,
.message-detail .hasRetry {
.message-detail .key-conflict-dialogue {
background: #F3F3A7;
border-radius: 5px;
padding: 1em;
margin: 1em; }
.message-detail .key-conflict-dialogue button,
.message-detail .hasRetry button {
.message-detail .key-conflict-dialogue button {
outline: none;
border: none;
border-radius: 5px;
@ -690,22 +688,8 @@ input.search {
padding: 0.5em 1em;
font-weight: bold;
line-height: 18px; }
.message-detail .key-conflict-dialogue button span,
.message-detail .hasRetry button span {
.message-detail .key-conflict-dialogue button span {
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 {
max-width: 40em;
margin: 1em auto; }
@ -807,7 +791,6 @@ input.search {
font-weight: bold; }
.timestamp {
font-size: smaller;
margin-right: 3px; }
.message-container,
@ -853,25 +836,39 @@ input.search {
margin: 0; }
.message-container .meta,
.message-list .meta {
font-size: smaller;
margin-top: 3px;
float: right;
cursor: pointer; }
float: right; }
.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-list .meta .timestamp,
.message-list .meta .status {
cursor: pointer;
opacity: 0.5; }
.message-container .meta:hover .timestamp, .message-container .meta:hover .status,
.message-list .meta:hover .timestamp,
.message-list .meta:hover .status {
opacity: 1.0; }
.message-container .meta:hover .timestamp,
.message-list .meta:hover .timestamp {
text-decoration: underline; }
.message-container .meta .timestamp:hover, .message-container .meta .status:hover,
.message-list .meta .timestamp:hover,
.message-list .meta .status:hover {
opacity: 1.0; }
.message-container .status,
.message-list .status {
float: right;
width: 18px;
height: 1em; }
height: 14px;
line-height: 1em; }
.message-container .sent .status,
.message-list .sent .status {
display: inline-block;