fix an issue where DaR messages are read on linked devices won't have correct expiration start time

pull/731/head
Ryan ZHAO 3 months ago
parent 9a528f8c71
commit b2360d8e1a

@ -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 */,

@ -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
}
}

@ -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 }

@ -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 }

@ -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
)
)
)
}
}

@ -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
)
)
)
}
}

@ -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
)

@ -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() }

Loading…
Cancel
Save