diff --git a/SessionMessagingKit/Messages/Control Messages/ExpirationTimerUpdate.swift b/SessionMessagingKit/Messages/Control Messages/ExpirationTimerUpdate.swift index 473259c32..a1717fd60 100644 --- a/SessionMessagingKit/Messages/Control Messages/ExpirationTimerUpdate.swift +++ b/SessionMessagingKit/Messages/Control Messages/ExpirationTimerUpdate.swift @@ -2,13 +2,20 @@ import SessionUtilitiesKit @objc(SNExpirationTimerUpdate) public final class ExpirationTimerUpdate : ControlMessage { + /// In the case of a sync message, the public key of the person the message was targeted at. + /// + /// - Note: `nil` if this isn't a sync message. + public var syncTarget: String? public var duration: UInt32? + public override var isSelfSendValid: Bool { true } + // MARK: Initialization public override init() { super.init() } - internal init(duration: UInt32) { + internal init(syncTarget: String?, duration: UInt32) { super.init() + self.syncTarget = syncTarget self.duration = duration } @@ -21,11 +28,13 @@ public final class ExpirationTimerUpdate : ControlMessage { // MARK: Coding public required init?(coder: NSCoder) { super.init(coder: coder) + if let syncTarget = coder.decodeObject(forKey: "syncTarget") as! String? { self.syncTarget = syncTarget } if let duration = coder.decodeObject(forKey: "durationSeconds") as! UInt32? { self.duration = duration } } public override func encode(with coder: NSCoder) { super.encode(with: coder) + coder.encode(syncTarget, forKey: "syncTarget") coder.encode(duration, forKey: "durationSeconds") } @@ -34,8 +43,9 @@ public final class ExpirationTimerUpdate : ControlMessage { guard let dataMessageProto = proto.dataMessage else { return nil } let isExpirationTimerUpdate = (dataMessageProto.flags & UInt32(SNProtoDataMessage.SNProtoDataMessageFlags.expirationTimerUpdate.rawValue)) != 0 guard isExpirationTimerUpdate else { return nil } + let syncTarget = dataMessageProto.syncTarget let duration = dataMessageProto.expireTimer - return ExpirationTimerUpdate(duration: duration) + return ExpirationTimerUpdate(syncTarget: syncTarget, duration: duration) } public override func toProto(using transaction: YapDatabaseReadWriteTransaction) -> SNProtoContent? { @@ -46,6 +56,7 @@ public final class ExpirationTimerUpdate : ControlMessage { let dataMessageProto = SNProtoDataMessage.builder() dataMessageProto.setFlags(UInt32(SNProtoDataMessage.SNProtoDataMessageFlags.expirationTimerUpdate.rawValue)) dataMessageProto.setExpireTimer(duration) + if let syncTarget = syncTarget { dataMessageProto.setSyncTarget(syncTarget) } // Group context do { try setGroupContextIfNeeded(on: dataMessageProto, using: transaction) @@ -67,6 +78,7 @@ public final class ExpirationTimerUpdate : ControlMessage { public override var description: String { """ ExpirationTimerUpdate( + syncTarget: \(syncTarget ?? "null") duration: \(duration?.description ?? "null") ) """ diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift index 8ffbac115..dd38082cd 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift @@ -96,13 +96,13 @@ extension MessageReceiver { private static func handleExpirationTimerUpdate(_ message: ExpirationTimerUpdate, using transaction: Any) { if message.duration! > 0 { - setExpirationTimer(to: message.duration!, for: message.sender!, groupPublicKey: message.groupPublicKey, using: transaction) + setExpirationTimer(to: message.duration!, for: message.sender!, syncTarget: message.syncTarget, groupPublicKey: message.groupPublicKey, using: transaction) } else { - disableExpirationTimer(for: message.sender!, groupPublicKey: message.groupPublicKey, using: transaction) + disableExpirationTimer(for: message.sender!, syncTarget: message.syncTarget, groupPublicKey: message.groupPublicKey, using: transaction) } } - public static func setExpirationTimer(to duration: UInt32, for senderPublicKey: String, groupPublicKey: String?, using transaction: Any) { + public static func setExpirationTimer(to duration: UInt32, for senderPublicKey: String, syncTarget: String?, groupPublicKey: String?, using transaction: Any) { let transaction = transaction as! YapDatabaseReadWriteTransaction var threadOrNil: TSThread? if let groupPublicKey = groupPublicKey { @@ -110,7 +110,7 @@ extension MessageReceiver { let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey) threadOrNil = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) } else { - threadOrNil = TSContactThread.getWithContactId(senderPublicKey, transaction: transaction) + threadOrNil = TSContactThread.getWithContactId(syncTarget ?? senderPublicKey, transaction: transaction) } guard let thread = threadOrNil else { return } let configuration = OWSDisappearingMessagesConfiguration(threadId: thread.uniqueId!, enabled: true, durationSeconds: duration) @@ -122,7 +122,7 @@ extension MessageReceiver { SSKEnvironment.shared.disappearingMessagesJob.startIfNecessary() } - public static func disableExpirationTimer(for senderPublicKey: String, groupPublicKey: String?, using transaction: Any) { + public static func disableExpirationTimer(for senderPublicKey: String, syncTarget: String?, groupPublicKey: String?, using transaction: Any) { let transaction = transaction as! YapDatabaseReadWriteTransaction var threadOrNil: TSThread? if let groupPublicKey = groupPublicKey { @@ -130,7 +130,7 @@ extension MessageReceiver { let groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(groupPublicKey) threadOrNil = TSGroupThread.fetch(uniqueId: TSGroupThread.threadId(fromGroupId: groupID), transaction: transaction) } else { - threadOrNil = TSContactThread.getWithContactId(senderPublicKey, transaction: transaction) + threadOrNil = TSContactThread.getWithContactId(syncTarget ?? senderPublicKey, transaction: transaction) } guard let thread = threadOrNil else { return } let configuration = OWSDisappearingMessagesConfiguration(threadId: thread.uniqueId!, enabled: false, durationSeconds: 24 * 60 * 60) diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender.swift b/SessionMessagingKit/Sending & Receiving/MessageSender.swift index 093885aab..318bc5c89 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender.swift @@ -346,33 +346,36 @@ public final class MessageSender : NSObject { public static func handleSuccessfulMessageSend(_ message: Message, to destination: Message.Destination, isSyncMessage: Bool = false, using transaction: Any) { let storage = SNMessagingKitConfiguration.shared.storage let transaction = transaction as! YapDatabaseReadWriteTransaction - guard let tsMessage = TSOutgoingMessage.find(withTimestamp: message.sentTimestamp!) else { return } // Ignore future self-sends Storage.shared.addReceivedMessageTimestamp(message.sentTimestamp!, using: transaction) - // Track the open group server message ID - tsMessage.openGroupServerMessageID = message.openGroupServerMessageID ?? 0 - tsMessage.save(with: transaction) - if let serverID = message.openGroupServerMessageID { - storage.setIDForMessage(withServerID: serverID, to: tsMessage.uniqueId!, using: transaction) - } - // Mark the message as sent - var recipients = [ message.recipient! ] - if case .closedGroup(_) = destination, let threadID = message.threadID, // threadID should always be set at this point - let thread = TSGroupThread.fetch(uniqueId: threadID, transaction: transaction), thread.isClosedGroup { - recipients = thread.groupModel.groupMemberIds - } - recipients.forEach { recipient in - tsMessage.update(withSentRecipient: recipient, wasSentByUD: true, transaction: transaction) + // Get the visible message if possible + if let tsMessage = TSOutgoingMessage.find(withTimestamp: message.sentTimestamp!) { + // Track the open group server message ID + tsMessage.openGroupServerMessageID = message.openGroupServerMessageID ?? 0 + tsMessage.save(with: transaction) + if let serverID = message.openGroupServerMessageID { + storage.setIDForMessage(withServerID: serverID, to: tsMessage.uniqueId!, using: transaction) + } + // Mark the message as sent + var recipients = [ message.recipient! ] + if case .closedGroup(_) = destination, let threadID = message.threadID, // threadID should always be set at this point + let thread = TSGroupThread.fetch(uniqueId: threadID, transaction: transaction), thread.isClosedGroup { + recipients = thread.groupModel.groupMemberIds + } + recipients.forEach { recipient in + tsMessage.update(withSentRecipient: recipient, wasSentByUD: true, transaction: transaction) + } + // Start the disappearing messages timer if needed + OWSDisappearingMessagesJob.shared().startAnyExpiration(for: tsMessage, expirationStartedAt: NSDate.millisecondTimestamp(), transaction: transaction) } - // Start the disappearing messages timer if needed - OWSDisappearingMessagesJob.shared().startAnyExpiration(for: tsMessage, expirationStartedAt: NSDate.millisecondTimestamp(), transaction: transaction) // Sync the message if: - // • it's a visible message + // • it's a visible message or an expiration timer update // • the destination was a contact // • we didn't sync it already let userPublicKey = getUserHexEncodedPublicKey() - if case .contact(let publicKey) = destination, !isSyncMessage, let message = message as? VisibleMessage { - message.syncTarget = publicKey + if case .contact(let publicKey) = destination, !isSyncMessage { + if let message = message as? VisibleMessage { message.syncTarget = publicKey } + if let message = message as? ExpirationTimerUpdate { message.syncTarget = publicKey } // FIXME: Make this a job sendToSnodeDestination(.contact(publicKey: userPublicKey), message: message, using: transaction, isSyncMessage: true).retainUntilComplete() }