From b2360d8e1aceab734e02cb0b255d23ed47ec937b Mon Sep 17 00:00:00 2001
From: Ryan ZHAO <>
Date: Fri, 9 Feb 2024 17:02:44 +1100
Subject: [PATCH] fix an issue where DaR messages are read on linked devices
 won't have correct expiration start time

---
 Session.xcodeproj/project.pbxproj             |   4 +
 .../DisappearingMessageConfiguration.swift    |  21 ++-
 .../MessageReceiver+Calls.swift               |   3 +-
 .../MessageReceiver+ExpirationTimers.swift    |  37 +---
 .../MessageReceiver+VisibleMessages.swift     |  78 ++++-----
 ...MessageReceiver+DisappearingMessages.swift | 158 ++++++++++++++++++
 .../Sending & Receiving/MessageReceiver.swift |   7 +-
 .../NotificationServiceExtension.swift        |   3 +-
 8 files changed, 223 insertions(+), 88 deletions(-)
 create mode 100644 SessionMessagingKit/Sending & Receiving/MessageReceiver+DisappearingMessages.swift

diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj
index 644a99dc7..910ebbcad 100644
--- a/Session.xcodeproj/project.pbxproj
+++ b/Session.xcodeproj/project.pbxproj
@@ -161,6 +161,7 @@
 		7BFD1A8A2745C4F000FB91B9 /* Permissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BFD1A892745C4F000FB91B9 /* Permissions.swift */; };
 		7BFD1A8C2747150E00FB91B9 /* TurnServerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BFD1A8B2747150E00FB91B9 /* TurnServerInfo.swift */; };
 		7BFD1A972747689000FB91B9 /* Session-Turn-Server in Resources */ = {isa = PBXBuildFile; fileRef = 7BFD1A962747689000FB91B9 /* Session-Turn-Server */; };
+		943C6D822B75E061004ACE64 /* MessageReceiver+DisappearingMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943C6D812B75E061004ACE64 /* MessageReceiver+DisappearingMessages.swift */; };
 		9593A1E796C9E6BE2352EA6F /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8B0BA5257C58DC6FF797278 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_SessionSnodeKit.framework */; };
 		99978E3F7A80275823CA9014 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_SessionNotificationServiceExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29E827FDF6C1032BB985740C /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_SessionNotificationServiceExtension.framework */; };
 		A11CD70D17FA230600A2D1B1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A11CD70C17FA230600A2D1B1 /* QuartzCore.framework */; };
@@ -1307,6 +1308,7 @@
 		8E946CB54A221018E23599DE /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig"; path = "Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig"; sourceTree = "<group>"; };
 		92E8569C96285EE3CDB5960D /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SignalUtilitiesKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SignalUtilitiesKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		93359C81CF2660040B7CD106 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit_SessionUtilitiesKitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit_SessionUtilitiesKitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		943C6D812B75E061004ACE64 /* MessageReceiver+DisappearingMessages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageReceiver+DisappearingMessages.swift"; sourceTree = "<group>"; };
 		A11CD70C17FA230600A2D1B1 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
 		A163E8AA16F3F6A90094D68B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
 		A1C32D4D17A0652C000A904E /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; };
@@ -2833,6 +2835,7 @@
 				C3471ECA2555356A00297E91 /* MessageSender+Encryption.swift */,
 				C300A5FB2554B0A000555489 /* MessageReceiver.swift */,
 				C3471F4B25553AB000297E91 /* MessageReceiver+Decryption.swift */,
+				943C6D812B75E061004ACE64 /* MessageReceiver+DisappearingMessages.swift */,
 			);
 			path = "Sending & Receiving";
 			sourceTree = "<group>";
@@ -6092,6 +6095,7 @@
 				FDC13D4B2A16ECBA007267C7 /* SubscribeResponse.swift in Sources */,
 				FD7115F228C6CB3900B47552 /* _010_AddThreadIdToFTS.swift in Sources */,
 				FD716E6428502DDD00C96BF4 /* CallManagerProtocol.swift in Sources */,
+				943C6D822B75E061004ACE64 /* MessageReceiver+DisappearingMessages.swift in Sources */,
 				FDC438C727BB6DF000C60D73 /* DirectMessage.swift in Sources */,
 				FDC13D502A16EE50007267C7 /* PushNotificationAPIEndpoint.swift in Sources */,
 				FD432434299C6985008A0213 /* PendingReadReceipt.swift in Sources */,
diff --git a/SessionMessagingKit/Database/Models/DisappearingMessageConfiguration.swift b/SessionMessagingKit/Database/Models/DisappearingMessageConfiguration.swift
index 0e9d7281e..cbfafa8aa 100644
--- a/SessionMessagingKit/Database/Models/DisappearingMessageConfiguration.swift
+++ b/SessionMessagingKit/Database/Models/DisappearingMessageConfiguration.swift
@@ -220,6 +220,7 @@ public extension DisappearingMessagesConfiguration {
         authorId: String,
         timestampMs: Int64,
         serverHash: String?,
+        serverExpirationTimestamp: TimeInterval?,
         updatedConfiguration: DisappearingMessagesConfiguration,
         using dependencies: Dependencies = Dependencies()
     ) throws -> Int64? {
@@ -252,7 +253,13 @@ public extension DisappearingMessagesConfiguration {
                 openGroup: nil
             )
         )
-        let expiresStartedAtMs: Double? = (updatedConfiguration.type == .disappearAfterSend || wasRead) ? Double(timestampMs) : nil
+        let messageExpirationInfo: MessageReceiver.MessageExpirationInfo = MessageReceiver.getMessageExpirationInfo(
+            db, 
+            wasRead: wasRead, 
+            serverExpirationTimestamp: serverExpirationTimestamp,
+            expiresInSeconds: (updatedConfiguration.type == .disappearAfterSend) ? Double(timestampMs) : nil,
+            expiresStartedAtMs: updatedConfiguration.durationSeconds
+        )
         let interaction = try Interaction(
             serverHash: serverHash,
             threadId: threadId,
@@ -265,9 +272,19 @@ public extension DisappearingMessagesConfiguration {
             timestampMs: timestampMs,
             wasRead: wasRead,
             expiresInSeconds: (threadVariant == .legacyGroup ? nil : updatedConfiguration.durationSeconds), // Do not expire this control message in legacy groups
-            expiresStartedAtMs: (threadVariant == .legacyGroup ? nil : expiresStartedAtMs)
+            expiresStartedAtMs: (threadVariant == .legacyGroup ? nil : messageExpirationInfo.expiresStartedAtMs)
         ).inserted(db)
         
+        if messageExpirationInfo.shouldUpdateExpiry {
+            MessageReceiver.updateExpiryForDisappearAfterReadMessages(
+                db,
+                threadId: threadId,
+                serverHash: serverHash,
+                expiresInSeconds: messageExpirationInfo.expiresInSeconds,
+                expiresStartedAtMs: messageExpirationInfo.expiresStartedAtMs
+            )
+        }
+        
         return interaction.id
     }
 }
diff --git a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+Calls.swift b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+Calls.swift
index 0c0fdb9c0..2c1f8d07d 100644
--- a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+Calls.swift	
+++ b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+Calls.swift	
@@ -11,7 +11,8 @@ extension MessageReceiver {
         _ db: Database,
         threadId: String,
         threadVariant: SessionThread.Variant,
-        message: CallMessage
+        message: CallMessage,
+        serverExpirationTimestamp: TimeInterval?
     ) throws {
         // Only support calls from contact threads
         guard threadVariant == .contact else { return }
diff --git a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ExpirationTimers.swift b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ExpirationTimers.swift
index bb1c0c245..90cd48c75 100644
--- a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ExpirationTimers.swift	
+++ b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ExpirationTimers.swift	
@@ -131,47 +131,12 @@ extension MessageReceiver {
         ).inserted(db)
     }
     
-    public static func updateContactDisappearingMessagesVersionIfNeeded(
-        _ db: Database,
-        messageVariant: Message.Variant?,
-        contactId: String?,
-        version: FeatureVersion?
-    ) {
-        guard
-            let messageVariant: Message.Variant = messageVariant,
-            let contactId: String = contactId,
-            let version: FeatureVersion = version
-        else {
-            return
-        }
-        
-        guard [ .visibleMessage, .expirationTimerUpdate ].contains(messageVariant) else { return }
-        
-        _ = try? Contact
-            .filter(id: contactId)
-            .updateAllAndConfig(
-                db,
-                Contact.Columns.lastKnownClientVersion.set(to: version)
-            )
-        
-        guard Features.useNewDisappearingMessagesConfig else { return }
-        
-        if contactId == getUserHexEncodedPublicKey(db) {
-            switch version {
-                case .legacyDisappearingMessages:
-                    TopBannerController.show(warning: .outdatedUserConfig)
-                case .newDisappearingMessages:
-                    TopBannerController.hide()
-            }
-        }
-        
-    }
-    
     internal static func handleExpirationTimerUpdate(
         _ db: Database,
         threadId: String,
         threadVariant: SessionThread.Variant,
         message: ExpirationTimerUpdate,
+        serverExpirationTimestamp: TimeInterval?,
         proto: SNProtoContent
     ) throws {
         guard proto.hasExpirationType || proto.hasExpirationTimer else { return }
diff --git a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+VisibleMessages.swift b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+VisibleMessages.swift
index 2d1d2edf0..cac4b1cc5 100644
--- a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+VisibleMessages.swift	
+++ b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+VisibleMessages.swift	
@@ -12,6 +12,7 @@ extension MessageReceiver {
         threadId: String,
         threadVariant: SessionThread.Variant,
         message: VisibleMessage,
+        serverExpirationTimestamp: TimeInterval?,
         associatedWithProto proto: SNProtoContent,
         using dependencies: Dependencies = Dependencies()
     ) throws -> Int64 {
@@ -139,7 +140,24 @@ extension MessageReceiver {
         // prevent the ability to insert duplicate interactions at a database level
         // so we don't need to check for the existance of a message beforehand anymore
         let interaction: Interaction
-        
+        // Auto-mark sent messages or messages older than the 'lastReadTimestampMs' as read
+        let wasRead: Bool = (
+            variant == .standardOutgoing ||
+            SessionUtil.timestampAlreadyRead(
+                threadId: thread.id,
+                threadVariant: thread.variant,
+                timestampMs: Int64(messageSentTimestamp * 1000),
+                userPublicKey: currentUserPublicKey,
+                openGroup: maybeOpenGroup
+            )
+        )
+        let messageExpirationInfo: MessageExpirationInfo = getMessageExpirationInfo(
+            db,
+            wasRead: wasRead,
+            serverExpirationTimestamp: serverExpirationTimestamp,
+            expiresInSeconds: message.expiresInSeconds,
+            expiresStartedAtMs: message.expiresStartedAtMs
+        )
         do {
             interaction = try Interaction(
                 serverHash: message.serverHash, // Keep track of server hash
@@ -148,17 +166,7 @@ extension MessageReceiver {
                 variant: variant,
                 body: message.text,
                 timestampMs: Int64(messageSentTimestamp * 1000),
-                wasRead: (
-                    // Auto-mark sent messages or messages older than the 'lastReadTimestampMs' as read
-                    variant == .standardOutgoing ||
-                    SessionUtil.timestampAlreadyRead(
-                        threadId: thread.id,
-                        threadVariant: thread.variant,
-                        timestampMs: Int64(messageSentTimestamp * 1000),
-                        userPublicKey: currentUserPublicKey,
-                        openGroup: maybeOpenGroup
-                    )
-                ),
+                wasRead: wasRead,
                 hasMention: Interaction.isUserMentioned(
                     db,
                     threadId: thread.id,
@@ -166,8 +174,8 @@ extension MessageReceiver {
                     quoteAuthorId: dataMessage.quote?.author,
                     using: dependencies
                 ),
-                expiresInSeconds: message.expiresInSeconds,
-                expiresStartedAtMs: message.expiresStartedAtMs,
+                expiresInSeconds: messageExpirationInfo.expiresInSeconds,
+                expiresStartedAtMs: messageExpirationInfo.expiresStartedAtMs,
                 // OpenGroupInvitations are stored as LinkPreview's in the database
                 linkPreviewUrl: (message.linkPreview?.url ?? message.openGroupInvitation?.url),
                 // Keep track of the open group server message ID ↔ message ID relationship
@@ -235,6 +243,16 @@ extension MessageReceiver {
             syncTarget: message.syncTarget
         )
         
+        if messageExpirationInfo.shouldUpdateExpiry {
+            updateExpiryForDisappearAfterReadMessages(
+                db,
+                threadId: threadId,
+                serverHash: message.serverHash,
+                expiresInSeconds: messageExpirationInfo.expiresInSeconds,
+                expiresStartedAtMs: messageExpirationInfo.expiresStartedAtMs
+            )
+        }
+        
         getExpirationForOutgoingDisappearingMessages(
             db,
             threadId: threadId,
@@ -511,36 +529,4 @@ extension MessageReceiver {
             _ = try pendingReadReceipt.delete(db)
         }
     }
-    
-    private static func getExpirationForOutgoingDisappearingMessages(
-        _ db: Database,
-        threadId: String,
-        variant: Interaction.Variant,
-        serverHash: String?,
-        expireInSeconds: TimeInterval?
-    ) {
-        guard
-            variant == .standardOutgoing,
-            let serverHash: String = serverHash,
-            let expireInSeconds: TimeInterval = expireInSeconds,
-            expireInSeconds > 0
-        else {
-            return
-        }
-        
-        let startedAtTimestampMs: Double = Double(SnodeAPI.currentOffsetTimestampMs())
-        
-        JobRunner.add(
-            db,
-            job: Job(
-                variant: .getExpiration,
-                behaviour: .runOnce,
-                threadId: threadId,
-                details: GetExpirationJob.Details(
-                    expirationInfo: [serverHash: expireInSeconds],
-                    startedAtTimestampMs: startedAtTimestampMs
-                )
-            )
-        )
-    }
 }
diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+DisappearingMessages.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+DisappearingMessages.swift
new file mode 100644
index 000000000..04fe294a0
--- /dev/null
+++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+DisappearingMessages.swift	
@@ -0,0 +1,158 @@
+// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
+
+import Foundation
+import GRDB
+import SessionSnodeKit
+import SessionUIKit
+import SessionUtilitiesKit
+
+extension MessageReceiver {
+    public static func updateContactDisappearingMessagesVersionIfNeeded(
+        _ db: Database,
+        messageVariant: Message.Variant?,
+        contactId: String?,
+        version: FeatureVersion?
+    ) {
+        guard
+            let messageVariant: Message.Variant = messageVariant,
+            let contactId: String = contactId,
+            let version: FeatureVersion = version
+        else {
+            return
+        }
+        
+        guard [ .visibleMessage, .expirationTimerUpdate ].contains(messageVariant) else { return }
+        
+        _ = try? Contact
+            .filter(id: contactId)
+            .updateAllAndConfig(
+                db,
+                Contact.Columns.lastKnownClientVersion.set(to: version)
+            )
+        
+        guard Features.useNewDisappearingMessagesConfig else { return }
+        
+        if contactId == getUserHexEncodedPublicKey(db) {
+            switch version {
+                case .legacyDisappearingMessages:
+                    TopBannerController.show(warning: .outdatedUserConfig)
+                case .newDisappearingMessages:
+                    TopBannerController.hide()
+            }
+        }
+    }
+    
+    public struct MessageExpirationInfo {
+        let expiresStartedAtMs: Double?
+        let expiresInSeconds: TimeInterval?
+        let shouldUpdateExpiry: Bool
+    }
+    
+    public static func getMessageExpirationInfo(
+        _ db: Database,
+        wasRead: Bool,
+        serverExpirationTimestamp: TimeInterval?,
+        expiresInSeconds: TimeInterval?,
+        expiresStartedAtMs: Double?,
+        using dependencies: Dependencies = Dependencies()
+    ) -> MessageExpirationInfo {
+        var shouldUpdateExpiry: Bool = false
+        let expiresStartedAtMs: Double? = {
+            // Disappear after sent
+            guard expiresStartedAtMs == nil else {
+                return expiresStartedAtMs
+            }
+            
+            // Disappear after read
+            guard
+                let expiresInSeconds: TimeInterval = expiresInSeconds,
+                expiresInSeconds > 0,
+                wasRead,
+                let serverExpirationTimestamp: TimeInterval = serverExpirationTimestamp
+            else {
+                return nil
+            }
+            
+            let nowMs: Double = Double(SnodeAPI.currentOffsetTimestampMs())
+            let serverExpirationTimestampMs: Double = serverExpirationTimestamp * 1000
+            let expiresInMs: Double = expiresInSeconds * 1000
+            
+            if serverExpirationTimestampMs <= (nowMs + expiresInMs) {
+                // seems to have been shortened already
+                return (serverExpirationTimestampMs - expiresInMs)
+            } else {
+                // consider that message unread
+                shouldUpdateExpiry = true
+                return (nowMs + expiresInSeconds)
+            }
+        }()
+        
+        return MessageExpirationInfo(
+            expiresStartedAtMs: expiresStartedAtMs,
+            expiresInSeconds: expiresInSeconds,
+            shouldUpdateExpiry: shouldUpdateExpiry
+        )
+    }
+    
+    public static func getExpirationForOutgoingDisappearingMessages(
+        _ db: Database,
+        threadId: String,
+        variant: Interaction.Variant,
+        serverHash: String?,
+        expireInSeconds: TimeInterval?
+    ) {
+        guard
+            variant == .standardOutgoing,
+            let serverHash: String = serverHash,
+            let expireInSeconds: TimeInterval = expireInSeconds,
+            expireInSeconds > 0
+        else {
+            return
+        }
+        
+        let startedAtTimestampMs: Double = Double(SnodeAPI.currentOffsetTimestampMs())
+        
+        JobRunner.add(
+            db,
+            job: Job(
+                variant: .getExpiration,
+                behaviour: .runOnce,
+                threadId: threadId,
+                details: GetExpirationJob.Details(
+                    expirationInfo: [serverHash: expireInSeconds],
+                    startedAtTimestampMs: startedAtTimestampMs
+                )
+            )
+        )
+    }
+    
+    public static func updateExpiryForDisappearAfterReadMessages(
+        _ db: Database,
+        threadId: String,
+        serverHash: String?,
+        expiresInSeconds: TimeInterval?,
+        expiresStartedAtMs: Double?
+    ) {
+        guard
+            let serverHash: String = serverHash,
+            let expiresInSeconds: TimeInterval = expiresInSeconds,
+            let expiresStartedAtMs: Double = expiresStartedAtMs
+        else {
+            return
+        }
+        
+        let expirationTimestampMs: Int64 = Int64(expiresStartedAtMs + expiresInSeconds * 1000)
+        JobRunner.add(
+            db,
+            job: Job(
+                variant: .expirationUpdate,
+                behaviour: .runOnce,
+                threadId: threadId,
+                details: ExpirationUpdateJob.Details(
+                    serverHashes: [serverHash],
+                    expirationTimestampMs: expirationTimestampMs
+                )
+            )
+        )
+    }
+}
diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift
index 2b08f0515..79895d953 100644
--- a/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift	
+++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift	
@@ -264,6 +264,7 @@ public enum MessageReceiver {
                     threadId: threadId,
                     threadVariant: threadVariant,
                     message: message,
+                    serverExpirationTimestamp: serverExpirationTimestamp,
                     proto: proto
                 )
                 
@@ -280,7 +281,8 @@ public enum MessageReceiver {
                     db,
                     threadId: threadId,
                     threadVariant: threadVariant,
-                    message: message
+                    message: message,
+                    serverExpirationTimestamp: serverExpirationTimestamp
                 )
                 
             case let message as MessageRequestResponse:
@@ -295,7 +297,8 @@ public enum MessageReceiver {
                     db,
                     threadId: threadId,
                     threadVariant: threadVariant,
-                    message: message,
+                    message: message, 
+                    serverExpirationTimestamp: serverExpirationTimestamp,
                     associatedWithProto: proto
                 )
                 
diff --git a/SessionNotificationServiceExtension/NotificationServiceExtension.swift b/SessionNotificationServiceExtension/NotificationServiceExtension.swift
index 97970dc86..e6e8d7eb2 100644
--- a/SessionNotificationServiceExtension/NotificationServiceExtension.swift
+++ b/SessionNotificationServiceExtension/NotificationServiceExtension.swift
@@ -135,7 +135,8 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
                                 db,
                                 threadId: processedMessage.threadId,
                                 threadVariant: processedMessage.threadVariant,
-                                message: callMessage
+                                message: callMessage,
+                                serverExpirationTimestamp: processedMessage.messageInfo.serverExpirationTimestamp
                             )
                             
                             guard case .preOffer = callMessage.kind else { return self.completeSilenty() }