diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index e8bd87cd1..c41337eee 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -3,6 +3,20 @@
"message": "You 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": {
"message": "This log will be posted publicly online for contributors to view. You may examine and edit it before submitting."
},
diff --git a/background.html b/background.html
index a6ea82e61..242975df7 100644
--- a/background.html
+++ b/background.html
@@ -42,6 +42,11 @@
+
+
diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js
index af051d554..2fc333275 100644
--- a/js/views/conversation_view.js
+++ b/js/views/conversation_view.js
@@ -201,11 +201,45 @@
this.$('.bottom-bar form').addClass('active');
},
+ updateUnread: function() {
+ this.updateLastSeenIndicator();
+ this.model.markRead();
+ },
+
onOpened: function() {
this.view.resetScrollPosition();
this.$el.trigger('force-resize');
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() {
@@ -215,15 +249,18 @@
fetchMessages: function() {
console.log('fetchMessages');
this.$('.bar-container').show();
- return this.model.fetchContacts().then(function() {
+ this.inProgressFetch = this.model.fetchContacts().then(function() {
return this.model.fetchMessages().then(function() {
this.$('.bar-container').hide();
this.model.messageCollection.where({unread: 1}).forEach(function(m) {
m.fetch();
});
+ this.inProgressFetch = null;
}.bind(this));
}.bind(this));
// TODO catch?
+
+ return this.inProgressFetch;
},
onExpired: function(message) {
@@ -241,6 +278,10 @@
this.model.messageCollection.add(message, {merge: true});
message.setToExpire();
+ if (this.lastSeenIndicator) {
+ this.lastSeenIndicator.increment(1);
+ }
+
if (!this.isHidden() && window.isFocused()) {
this.markRead();
}
@@ -345,6 +386,8 @@
},
sendMessage: function(e) {
+ this.removeLastSeenIndicator();
+
var toast;
if (extension.expired()) {
toast = new Whisper.ExpiredToast();
diff --git a/js/views/last_seen_indicator_view.js b/js/views/last_seen_indicator_view.js
new file mode 100644
index 000000000..0ce6a945a
--- /dev/null
+++ b/js/views/last_seen_indicator_view.js
@@ -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
+ };
+ }
+ });
+})();
diff --git a/stylesheets/_android.scss b/stylesheets/_android.scss
index 03824943e..2ea2631fc 100644
--- a/stylesheets/_android.scss
+++ b/stylesheets/_android.scss
@@ -70,4 +70,8 @@
.inactive button.back {
@include header-icon-black('/images/back.svg');
}
+
+ .message-list .last-seen-indicator-view .text {
+ margin-top: 2em;
+ }
}
diff --git a/stylesheets/_conversation.scss b/stylesheets/_conversation.scss
index 1308290df..aed6f3523 100644
--- a/stylesheets/_conversation.scss
+++ b/stylesheets/_conversation.scss
@@ -682,3 +682,17 @@ li.entry .error-icon-container {
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;
+ }
+}
diff --git a/stylesheets/android-dark.scss b/stylesheets/android-dark.scss
index 51fff994a..d7ec3f26d 100644
--- a/stylesheets/android-dark.scss
+++ b/stylesheets/android-dark.scss
@@ -202,4 +202,9 @@ $text-dark: #CCCCCC;
.recorder {
background: $grey-dark_l2;
}
+
+ .message-list .last-seen-indicator-view .text {
+ margin-top: 2em;
+ background-color: $grey-dark_l2;
+ }
}
diff --git a/stylesheets/manifest.css b/stylesheets/manifest.css
index 0e31b59a3..df5b4f0f6 100644
--- a/stylesheets/manifest.css
+++ b/stylesheets/manifest.css
@@ -1496,6 +1496,16 @@ li.entry .error-icon-container {
color: #454545;
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 {
height: 64px;
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-size: 100%;
background-color: black; }
+.android .message-list .last-seen-indicator-view .text {
+ margin-top: 2em; }
.android-dark {
color: #CCCCCC; }
@@ -2098,5 +2110,8 @@ li.entry .error-icon-container {
background-color: #292929; }
.android-dark .recorder {
background: #292929; }
+ .android-dark .message-list .last-seen-indicator-view .text {
+ margin-top: 2em;
+ background-color: #292929; }
/*# sourceMappingURL=manifest.css.map */