Parcourir la source

Implement recurrent wakeup for unsent messages

See https://github.com/threema-ch/app-remote-protocol/pull/5 for
a rationale of the change.
Lennart Grahl il y a 6 ans
Parent
commit
5c17ff4bab
4 fichiers modifiés avec 78 ajouts et 32 suppressions
  1. 14 0
      src/services/message.ts
  2. 44 22
      src/services/webclient.ts
  3. 1 1
      src/threema.d.ts
  4. 19 9
      src/threema/container.ts

+ 14 - 0
src/services/message.ts

@@ -230,4 +230,18 @@ export class MessageService {
         }
         return message;
     }
+
+    /**
+     * Return whether the app has attempted to send this message to the server
+     * (successful or not).
+     */
+    public isSentOrSendingFailed(message: threema.Message): boolean {
+        switch (message.state) {
+            case 'pending':
+            case 'sending':
+                return false;
+            default:
+                return true;
+        }
+    }
 }

+ 44 - 22
src/services/webclient.ts

@@ -2583,18 +2583,18 @@ export class WebClientService {
         this._receiveReplyReceiver(message, 'distributionList', future);
     }
 
-    private _receiveCreateMessage(message: threema.WireMessage): void {
+    private _receiveCreateMessage(wireMessage: threema.WireMessage): void {
         this.arpLog.debug('Received create message response');
-        const future = this.popWireMessageFuture(message);
+        const future = this.popWireMessageFuture(wireMessage);
 
         // Handle error (if any)
-        if (!message.ack.success) {
-            return future.reject(message.ack.error);
+        if (!wireMessage.ack.success) {
+            return future.reject(wireMessage.ack.error);
         }
 
         // Unpack data and arguments
-        const args = message.args;
-        const data = message.data;
+        const args = wireMessage.args;
+        const data = wireMessage.data;
         if (args === undefined || data === undefined) {
             this.arpLog.warn('Invalid create message received, arguments or data missing');
             return future.reject('invalidResponse');
@@ -2613,12 +2613,22 @@ export class WebClientService {
             type: receiverType,
             id: receiverId,
         } as threema.Receiver;
-        this.messages.bindTemporaryToMessageId(
+        const message = this.messages.bindTemporaryToMessageId(
             receiver,
-            message.ack.id,
+            wireMessage.ack.id,
             messageId,
         );
         future.resolve(messageId);
+
+        // Add a special future that resolves once the message has been
+        // identified as sent. As long as an unacknowledged wire message future
+        // exists, the app will be continuously awoken if the connection
+        // has been lost.
+        if (!this.messageService.isSentOrSendingFailed(message)) {
+            const sentId = `${message.id}-sent`;
+            this.wireMessageFutures.set(sentId, new Future());
+            this.arpLogV.debug(`Added special wire message future: ${sentId}`);
+        }
     }
 
     private _receiveResponseConversations(message: threema.WireMessage) {
@@ -2863,18 +2873,18 @@ export class WebClientService {
         }
     }
 
-    private _receiveUpdateMessages(message: threema.WireMessage): void {
+    private _receiveUpdateMessages(wireMessage: threema.WireMessage): void {
         this.arpLog.debug('Received messages update');
-        const future = this.popWireMessageFuture(message, true);
+        const future = this.popWireMessageFuture(wireMessage, true);
 
         // Handle error (if any)
-        if (message.ack !== undefined && !message.ack.success) {
-            return future.reject(message.ack.error);
+        if (wireMessage.ack !== undefined && !wireMessage.ack.success) {
+            return future.reject(wireMessage.ack.error);
         }
 
         // Unpack data and arguments
-        const args = message.args;
-        const data: threema.Message[] = message.data;
+        const args = wireMessage.args;
+        const data: threema.Message[] = wireMessage.data;
         if (args === undefined || data === undefined) {
             this.arpLog.warn('Invalid message update, data or arguments missing');
             return future.reject('invalidResponse');
@@ -2893,19 +2903,31 @@ export class WebClientService {
             return future.reject('invalidResponse');
         }
         if (this.config.ARP_LOG_TRACE) {
-            this.logChatMessages(message.type, message.subType, type, id, mode, data);
+            this.logChatMessages(wireMessage.type, wireMessage.subType, type, id, mode, data);
         }
         const receiver: threema.BaseReceiver = {type: type, id: id};
 
         // React depending on mode
         let notify = false;
-        for (const msg of data) {
+        for (const message of data) {
+            // Pop special future to be resolved if the message has been
+            // identified as sent.
+            if (this.messageService.isSentOrSendingFailed(message)) {
+                const sentId = `${message.id}-sent`;
+                const sentFuture = this.wireMessageFutures.get(sentId);
+                if (sentFuture !== undefined) {
+                    this.wireMessageFutures.delete(sentId);
+                    this.arpLogV.debug(`Removed special wire message future: ${sentId}`);
+                    sentFuture.resolve();
+                }
+            }
+
             switch (mode) {
                 case WebClientService.ARGUMENT_MODE_NEW:
                     // 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]);
+                    if (!this.messages.update(receiver, message)) {
+                        this.messages.addNewer(receiver, [message]);
 
                         // If we have received a new message, it is highly unlikely that the contact is still typing
                         this.typing.unsetTyping(receiver);
@@ -2913,8 +2935,8 @@ export class WebClientService {
                     notify = true;
                     break;
                 case WebClientService.ARGUMENT_MODE_MODIFIED:
-                    if (!this.messages.update(receiver, msg)) {
-                        const log = `Received message update for unknown message (id ${msg.id})`;
+                    if (!this.messages.update(receiver, message)) {
+                        const log = `Received message update for unknown message (id ${message.id})`;
                         this.arpLog.error(log);
                         if (this.config.ARP_LOG_TRACE) {
                             this.messages.addStatusMessage(receiver, 'Warning: ' + log);
@@ -2923,8 +2945,8 @@ export class WebClientService {
                     }
                     break;
                 case WebClientService.ARGUMENT_MODE_REMOVED:
-                    if (!this.messages.remove(receiver, msg.id)) {
-                        this.arpLog.error(`Received message deletion for unknown message (id ${msg.id})`);
+                    if (!this.messages.remove(receiver, message.id)) {
+                        this.arpLog.error(`Received message deletion for unknown message (id ${message.id})`);
                     }
                     notify = true;
                     break;

+ 1 - 1
src/threema.d.ts

@@ -874,7 +874,7 @@ declare namespace threema {
             setThumbnail(receiver: BaseReceiver, messageId: string, thumbnailImage: ArrayBuffer): boolean;
             remove(receiver: BaseReceiver, messageId: string): boolean;
             removeTemporary(receiver: BaseReceiver, temporaryMessageId: string): boolean;
-            bindTemporaryToMessageId(receiver: BaseReceiver, temporaryId: string, messageId: string): boolean;
+            bindTemporaryToMessageId(receiver: BaseReceiver, temporaryId: string, messageId: string): Message | null;
             notify(receiver: BaseReceiver, $scope: ng.IScope): void;
             register(receiver: BaseReceiver, $scope: ng.IScope, callback: any): Message[];
             updateFirstUnreadMessage(receiver: BaseReceiver);

+ 19 - 9
src/threema/container.ts

@@ -694,6 +694,7 @@ class Messages implements threema.Container.Messages {
         }
         return false;
     }
+
     /**
      * Remove a message.
      *
@@ -711,24 +712,33 @@ class Messages implements threema.Container.Messages {
         return false;
     }
 
-    public bindTemporaryToMessageId(receiver: threema.BaseReceiver, temporaryId: string, messageId: string): boolean {
+    /**
+     * Look up a message with a specific temporary id. If it has been found,
+     * replaces the temporary id with the message id and returns the message
+     * instance.
+     */
+    public bindTemporaryToMessageId(
+        receiver: threema.BaseReceiver,
+        temporaryId: string,
+        messageId: string,
+    ): threema.Message | null {
         const list = this.getList(receiver);
-        for (const item of list) {
-            if (item.temporaryId === temporaryId) {
-                if (item.id !== undefined) {
+        for (const message of list) {
+            if (message.temporaryId === temporaryId) {
+                if (message.id !== undefined) {
                     // do not bind to a new message id
-                    return false;
+                    return message;
                 }
 
                 // reset temporary id
-                item.temporaryId = null;
+                message.temporaryId = null;
 
                 // assign to "real" message id
-                item.id = messageId;
-                return true;
+                message.id = messageId;
+                return message;
             }
         }
-        return false;
+        return null;
     }
 
     /**