Bladeren bron

Fix "unread messages" indicator

Danilo Bargen 7 jaren geleden
bovenliggende
commit
ae5aaf6abb
4 gewijzigde bestanden met toevoegingen van 86 en 29 verwijderingen
  1. 26 0
      src/message_helpers.ts
  2. 33 11
      src/partials/messenger.ts
  3. 7 1
      src/services/webclient.ts
  4. 20 17
      src/threema/container.ts

+ 26 - 0
src/message_helpers.ts

@@ -0,0 +1,26 @@
+/**
+ * This file is part of Threema Web.
+ *
+ * Threema Web is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// This file contains helper functions related to messages.
+// Try to keep all functions pure!
+
+/**
+ * Return wether a message is a "first unread" status message.
+ */
+export function isFirstUnreadStatusMessage(message: threema.Message) {
+    return message.type === 'status' && message.statusType === 'firstUnreadMessage';
+}

+ 33 - 11
src/partials/messenger.ts

@@ -186,6 +186,7 @@ class ConversationController {
     private $scope: ng.IScope;
     private $rootScope: ng.IRootScopeService;
     private $filter: ng.IFilterService;
+    private $translate: ng.translate.ITranslateService;
 
     // Own services
     private webClientService: WebClientService;
@@ -206,29 +207,36 @@ class ConversationController {
     // Scrolling
     public showScrollJump: boolean = false;
 
+    // The conversation receiver
     public receiver: threema.Receiver;
     public type: threema.ReceiverType;
+
+    // The conversation messages
+    private messages: threema.Message[];
+
+    // This will be set to true as soon as the initial messages have been loaded
+    private initialized = false;
+
+    // Mentions
+    public allMentions: threema.Mention[] = [];
+    public currentMentions: threema.Mention[] = [];
+    public currentMentionFilterWord = null;
+    public selectedMention: number = null;
+
     public message: string = '';
     public lastReadMsg: threema.Message | null = null;
     public msgReadReportPending = false;
     private hasMore = true;
     private latestRefMsgId: string | null = null;
     private allText: string;
-    private messages: threema.Message[];
     public initialData: threema.InitialConversationData = {
         draft: '',
         initialText: '',
     };
-    private $translate: ng.translate.ITranslateService;
     private locked = false;
     public maxTextLength: number;
     public isTyping = (): boolean => false;
 
-    public allMentions: threema.Mention[] = [];
-    public currentMentions: threema.Mention[] = [];
-    public currentMentionFilterWord = null;
-    public selectedMention: number = null;
-
     private uploading = {
         enabled: false,
         value1: 0,
@@ -356,24 +364,38 @@ class ConversationController {
 
             if (!this.receiver.locked) {
                 let latestHeight = 0;
-                // update unread count
-                this.webClientService.messages.updateFirstUnreadMessage(this.receiver);
+
+                // Subscribe to messages
                 this.messages = this.webClientService.messages.register(
                     this.receiver,
                     this.$scope,
                     (e, allMessages: threema.Message[], hasMore: boolean) => {
+                        // This function is called every time there are new or removed messages.
+
+                        // Update data
                         this.messages = allMessages;
+                        const wasInitialized = this.initialized;
+                        this.initialized = true;
                         this.hasMore = hasMore;
+
+                        // Update "first unread" divider
+                        if (!wasInitialized) {
+                            this.webClientService.messages.updateFirstUnreadMessage(this.receiver);
+                        }
+
+                        // Autoscroll
                         if (this.latestRefMsgId !== null) {
                             // scroll to div..
-                            this.domChatElement.scrollTop =
-                                this.domChatElement.scrollHeight - latestHeight;
+                            this.domChatElement.scrollTop = this.domChatElement.scrollHeight - latestHeight;
                             this.latestRefMsgId = null;
                         }
                         latestHeight = this.domChatElement.scrollHeight;
                     },
                 );
 
+                // Update "first unread" divider
+                this.webClientService.messages.updateFirstUnreadMessage(this.receiver);
+
                 // Enable mentions only in group chats
                 if (this.type === 'group') {
                     this.allMentions.push({

+ 7 - 1
src/services/webclient.ts

@@ -1931,6 +1931,7 @@ export class WebClientService {
             // Set "more" flag to indicate that more (older) messages are available.
             this.messages.setMore(receiver, more);
 
+            // Notify listeners
             this.messages.notify(receiver, this.$rootScope);
         } else {
             this.$log.warn("Ignoring message response that hasn't been requested");
@@ -2092,16 +2093,17 @@ export class WebClientService {
         const receiver: threema.BaseReceiver = {type: type, id: id};
 
         // React depending on mode
+        let notify = false;
         for (const msg of data) {
             switch (mode) {
                 case WebClientService.ARGUMENT_MODE_NEW:
                     this.$log.debug('New message', msg.id);
-
                     // It's possible that this message already exists (placeholder message on send).
                     // Try to update it first. If not, add it as a new msg.
                     if (!this.messages.update(receiver, msg)) {
                         this.messages.addNewer(receiver, [msg]);
                     }
+                    notify = true;
                     break;
                 case WebClientService.ARGUMENT_MODE_MODIFIED:
                     this.$log.debug('Modified message', msg.id);
@@ -2109,11 +2111,15 @@ export class WebClientService {
                     break;
                 case WebClientService.ARGUMENT_MODE_REMOVED:
                     this.messages.remove(receiver, msg.id);
+                    notify = true;
                     break;
                 default:
                     this.$log.warn('Invalid message response, unknown mode:', mode);
             }
         }
+        if (notify) {
+            this.messages.notify(receiver, this.$rootScope);
+        }
     }
 
     private _receiveUpdateReceiver(message: threema.WireMessage): void {

+ 20 - 17
src/threema/container.ts

@@ -15,6 +15,7 @@
  * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  */
 
+import {isFirstUnreadStatusMessage} from '../message_helpers';
 import {ReceiverService} from '../services/receiver';
 
 type ContactMap = Map<string, threema.ContactReceiver>;
@@ -644,14 +645,17 @@ class Messages implements threema.Container.Messages {
         return false;
     }
 
+    /**
+     * Notify listener that messages changed.
+     */
     public notify(receiver: threema.BaseReceiver, $scope: ng.IScope) {
         $scope.$broadcast('threema.receiver.' + receiver.type + '.' + receiver.id + '.messages',
             this.getList(receiver), this.hasMore(receiver));
     }
 
     /**
-     * register a message change notify on the given scope
-     * return the CURRENT list of loaded messages
+     * Register a function that is called every time the messages are added or removed.
+     * Return the CURRENT list of loaded messages.
      */
     public register(receiver: threema.BaseReceiver, $scope: ng.IScope, callback: any): threema.Message[] {
         $scope.$on('threema.receiver.' + receiver.type + '.' + receiver.id + '.messages', callback);
@@ -663,26 +667,25 @@ class Messages implements threema.Container.Messages {
      * entries and insert a new entry just before the oldest unread message.
      */
     public updateFirstUnreadMessage(receiver: threema.BaseReceiver): void {
-        const receiverMessages = this.getReceiverMessages(receiver);
+        const receiverMessages: ReceiverMessages = this.getReceiverMessages(receiver);
         if (receiverMessages !== undefined && receiverMessages.list.length > 0) {
-            // remove unread
-            let removedElements = 0;
             let firstUnreadMessageIndex;
-            receiverMessages.list = receiverMessages.list.filter((message: threema.Message, index: number) => {
-                if (message.type === 'status'
-                    && message.statusType === 'firstUnreadMessage') {
-                    removedElements++;
-                    return false;
-                } else if (firstUnreadMessageIndex === undefined
-                        && !message.isOutbox
-                        && message.unread) {
-                    firstUnreadMessageIndex = index;
+
+            // Remove unread messages
+            // Iterate in reverse, to avoid getting problems when removing items
+            for (let i = receiverMessages.list.length - 1; i >= 0; i--) {
+                const message: threema.Message = receiverMessages.list[i];
+                if (isFirstUnreadStatusMessage(message)) {
+                    receiverMessages.list.splice(i, 1);
+                    if (firstUnreadMessageIndex !== undefined) {
+                        firstUnreadMessageIndex -= 1;
+                    }
+                } else if (!message.isOutbox && message.unread) {
+                    firstUnreadMessageIndex = i;
                 }
-                return true;
-            });
+            }
 
             if (firstUnreadMessageIndex !== undefined) {
-                firstUnreadMessageIndex -= removedElements;
                 receiverMessages.list.splice(firstUnreadMessageIndex, 0 , {
                     type: 'status',
                     isStatus: true,