Add new Last Seen Indicator with unread count, scroll to it
This is to ensure that when there are a lot of unread messages, the user is given the chance to see all of them by being scrolled to the oldest new message. When a new message comes in, the indicator will be incremented. When the user sends a message or switches away from the conversation, the last seen indicator will be removed. FREEBIE
This commit is contained in:
parent
bec69826ae
commit
fed26c36ca
8 changed files with 133 additions and 2 deletions
|
@ -3,6 +3,20 @@
|
||||||
"message": "You left the group",
|
"message": "You left the group",
|
||||||
"description": "Displayed when a user can't send a message because they have left the group"
|
"description": "Displayed when a user can't send a message because they have left the group"
|
||||||
},
|
},
|
||||||
|
"unreadMessage": {
|
||||||
|
"message": "1 unread message",
|
||||||
|
"description": "Text for unread message separator, just one message"
|
||||||
|
},
|
||||||
|
"unreadMessages": {
|
||||||
|
"message": "$count$ unread messages",
|
||||||
|
"description": "Text for unread message separator, with count",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"debugLogExplanation": {
|
"debugLogExplanation": {
|
||||||
"message": "This log will be posted publicly online for contributors to view. You may examine and edit it before submitting."
|
"message": "This log will be posted publicly online for contributors to view. You may examine and edit it before submitting."
|
||||||
},
|
},
|
||||||
|
|
|
@ -42,6 +42,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
<script type='text/x-tmpl-mustache' id='last-seen-indicator-view'>
|
||||||
|
<div class='text'>
|
||||||
|
{{ unreadMessages }}
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
<script type='text/x-tmpl-mustache' id='expired_alert'>
|
<script type='text/x-tmpl-mustache' id='expired_alert'>
|
||||||
<a target='_blank' href='https://chrome.google.com/webstore/detail/bikioccmkafdpakkkcpdbppfkghcmihk'>
|
<a target='_blank' href='https://chrome.google.com/webstore/detail/bikioccmkafdpakkkcpdbppfkghcmihk'>
|
||||||
<button class='upgrade'>{{ upgrade }}</button>
|
<button class='upgrade'>{{ upgrade }}</button>
|
||||||
|
@ -630,6 +635,7 @@
|
||||||
<script type='text/javascript' src='js/emoji_util.js'></script>
|
<script type='text/javascript' src='js/emoji_util.js'></script>
|
||||||
|
|
||||||
<script type='text/javascript' src='js/views/whisper_view.js'></script>
|
<script type='text/javascript' src='js/views/whisper_view.js'></script>
|
||||||
|
<script type='text/javascript' src='js/views/last_seen_indicator_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/debug_log_view.js'></script>
|
<script type='text/javascript' src='js/views/debug_log_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/toast_view.js'></script>
|
<script type='text/javascript' src='js/views/toast_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/attachment_preview_view.js'></script>
|
<script type='text/javascript' src='js/views/attachment_preview_view.js'></script>
|
||||||
|
|
|
@ -201,11 +201,45 @@
|
||||||
this.$('.bottom-bar form').addClass('active');
|
this.$('.bottom-bar form').addClass('active');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateUnread: function() {
|
||||||
|
this.updateLastSeenIndicator();
|
||||||
|
this.model.markRead();
|
||||||
|
},
|
||||||
|
|
||||||
onOpened: function() {
|
onOpened: function() {
|
||||||
this.view.resetScrollPosition();
|
this.view.resetScrollPosition();
|
||||||
this.$el.trigger('force-resize');
|
this.$el.trigger('force-resize');
|
||||||
this.focusMessageField();
|
this.focusMessageField();
|
||||||
this.model.markRead();
|
|
||||||
|
if (this.inProgressFetch) {
|
||||||
|
this.inProgressFetch.then(this.updateUnread.bind(this));
|
||||||
|
} else {
|
||||||
|
this.updateUnread();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
removeLastSeenIndicator: function() {
|
||||||
|
if (this.lastSeenIndicator) {
|
||||||
|
this.lastSeenIndicator.remove();
|
||||||
|
this.lastSeenIndicator = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateLastSeenIndicator: function() {
|
||||||
|
this.removeLastSeenIndicator();
|
||||||
|
|
||||||
|
var oldestUnread = this.model.messageCollection.find(function(model) {
|
||||||
|
return model.get('unread');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (oldestUnread) {
|
||||||
|
var unreadCount = this.model.get('unreadCount');
|
||||||
|
this.lastSeenIndicator = new Whisper.LastSeenIndicatorView({count: unreadCount});
|
||||||
|
var unreadEl = this.lastSeenIndicator.render().$el;
|
||||||
|
|
||||||
|
unreadEl.insertBefore(this.$('#' + oldestUnread.get('id')));
|
||||||
|
var position = unreadEl[0].scrollIntoView(true);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
focusMessageField: function() {
|
focusMessageField: function() {
|
||||||
|
@ -215,15 +249,18 @@
|
||||||
fetchMessages: function() {
|
fetchMessages: function() {
|
||||||
console.log('fetchMessages');
|
console.log('fetchMessages');
|
||||||
this.$('.bar-container').show();
|
this.$('.bar-container').show();
|
||||||
return this.model.fetchContacts().then(function() {
|
this.inProgressFetch = this.model.fetchContacts().then(function() {
|
||||||
return this.model.fetchMessages().then(function() {
|
return this.model.fetchMessages().then(function() {
|
||||||
this.$('.bar-container').hide();
|
this.$('.bar-container').hide();
|
||||||
this.model.messageCollection.where({unread: 1}).forEach(function(m) {
|
this.model.messageCollection.where({unread: 1}).forEach(function(m) {
|
||||||
m.fetch();
|
m.fetch();
|
||||||
});
|
});
|
||||||
|
this.inProgressFetch = null;
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
// TODO catch?
|
// TODO catch?
|
||||||
|
|
||||||
|
return this.inProgressFetch;
|
||||||
},
|
},
|
||||||
|
|
||||||
onExpired: function(message) {
|
onExpired: function(message) {
|
||||||
|
@ -241,6 +278,10 @@
|
||||||
this.model.messageCollection.add(message, {merge: true});
|
this.model.messageCollection.add(message, {merge: true});
|
||||||
message.setToExpire();
|
message.setToExpire();
|
||||||
|
|
||||||
|
if (this.lastSeenIndicator) {
|
||||||
|
this.lastSeenIndicator.increment(1);
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.isHidden() && window.isFocused()) {
|
if (!this.isHidden() && window.isFocused()) {
|
||||||
this.markRead();
|
this.markRead();
|
||||||
}
|
}
|
||||||
|
@ -345,6 +386,8 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
sendMessage: function(e) {
|
sendMessage: function(e) {
|
||||||
|
this.removeLastSeenIndicator();
|
||||||
|
|
||||||
var toast;
|
var toast;
|
||||||
if (extension.expired()) {
|
if (extension.expired()) {
|
||||||
toast = new Whisper.ExpiredToast();
|
toast = new Whisper.ExpiredToast();
|
||||||
|
|
30
js/views/last_seen_indicator_view.js
Normal file
30
js/views/last_seen_indicator_view.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* vim: ts=4:sw=4:expandtab
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
window.Whisper = window.Whisper || {};
|
||||||
|
|
||||||
|
Whisper.LastSeenIndicatorView = Whisper.View.extend({
|
||||||
|
className: 'last-seen-indicator-view',
|
||||||
|
templateName: 'last-seen-indicator-view',
|
||||||
|
initialize: function(options) {
|
||||||
|
options = options || {};
|
||||||
|
this.count = options.count || 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
increment: function(count) {
|
||||||
|
this.count += count;
|
||||||
|
this.render();
|
||||||
|
},
|
||||||
|
|
||||||
|
render_attributes: function() {
|
||||||
|
var unreadMessages = this.count === 1 ? i18n('unreadMessage')
|
||||||
|
: i18n('unreadMessages', [this.count]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
unreadMessages: unreadMessages
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
|
@ -70,4 +70,8 @@
|
||||||
.inactive button.back {
|
.inactive button.back {
|
||||||
@include header-icon-black('/images/back.svg');
|
@include header-icon-black('/images/back.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-list .last-seen-indicator-view .text {
|
||||||
|
margin-top: 2em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -682,3 +682,17 @@ li.entry .error-icon-container {
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-list .last-seen-indicator-view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.text {
|
||||||
|
border-radius: $border-radius;
|
||||||
|
padding: 5px 10px;
|
||||||
|
margin: 1em;
|
||||||
|
|
||||||
|
background-color: $grey_l2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -202,4 +202,9 @@ $text-dark: #CCCCCC;
|
||||||
.recorder {
|
.recorder {
|
||||||
background: $grey-dark_l2;
|
background: $grey-dark_l2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-list .last-seen-indicator-view .text {
|
||||||
|
margin-top: 2em;
|
||||||
|
background-color: $grey-dark_l2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1496,6 +1496,16 @@ li.entry .error-icon-container {
|
||||||
color: #454545;
|
color: #454545;
|
||||||
border-radius: 5px; }
|
border-radius: 5px; }
|
||||||
|
|
||||||
|
.message-list .last-seen-indicator-view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center; }
|
||||||
|
.message-list .last-seen-indicator-view .text {
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
margin: 1em;
|
||||||
|
background-color: #d9d9d9; }
|
||||||
|
|
||||||
.ios #header {
|
.ios #header {
|
||||||
height: 64px;
|
height: 64px;
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
@ -1838,6 +1848,8 @@ li.entry .error-icon-container {
|
||||||
-webkit-mask: url("/images/back.svg") no-repeat center;
|
-webkit-mask: url("/images/back.svg") no-repeat center;
|
||||||
-webkit-mask-size: 100%;
|
-webkit-mask-size: 100%;
|
||||||
background-color: black; }
|
background-color: black; }
|
||||||
|
.android .message-list .last-seen-indicator-view .text {
|
||||||
|
margin-top: 2em; }
|
||||||
|
|
||||||
.android-dark {
|
.android-dark {
|
||||||
color: #CCCCCC; }
|
color: #CCCCCC; }
|
||||||
|
@ -2098,5 +2110,8 @@ li.entry .error-icon-container {
|
||||||
background-color: #292929; }
|
background-color: #292929; }
|
||||||
.android-dark .recorder {
|
.android-dark .recorder {
|
||||||
background: #292929; }
|
background: #292929; }
|
||||||
|
.android-dark .message-list .last-seen-indicator-view .text {
|
||||||
|
margin-top: 2em;
|
||||||
|
background-color: #292929; }
|
||||||
|
|
||||||
/*# sourceMappingURL=manifest.css.map */
|
/*# sourceMappingURL=manifest.css.map */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue