diff --git a/Scripts/sds_codegen/sds_config/sds-property_order.json b/Scripts/sds_codegen/sds_config/sds-property_order.json index e3ac5f948b..09dcbbc805 100644 --- a/Scripts/sds_codegen/sds_config/sds-property_order.json +++ b/Scripts/sds_codegen/sds_config/sds-property_order.json @@ -81,6 +81,7 @@ "InteractionRecord.hasEnded": 56, "InteractionRecord.hasLegacyMessageState": 22, "InteractionRecord.hasSyncedTranscript": 23, + "InteractionRecord.hiddenUntilTimestamp": 65, "InteractionRecord.infoMessageUserInfo": 50, "InteractionRecord.isFromLinkedDevice": 24, "InteractionRecord.isLocalChange": 25, diff --git a/Signal/src/ViewControllers/ConversationView/CV/CVMessageMapping.swift b/Signal/src/ViewControllers/ConversationView/CV/CVMessageMapping.swift index 4e13f09466..6ef6e16075 100644 --- a/Signal/src/ViewControllers/ConversationView/CV/CVMessageMapping.swift +++ b/Signal/src/ViewControllers/ConversationView/CV/CVMessageMapping.swift @@ -163,8 +163,8 @@ public class CVMessageMapping: NSObject { owsAssertDebug(count > 0) let count = max(1, min(count, maxInteractionCount)) - // The number of interactions currently in the conversation. - let conversationSize = interactionFinder.count(transaction: transaction) + // The number of visible interactions currently in the conversation. + let conversationSize = interactionFinder.count(includingHiddenInteractions: false, transaction: transaction) guard conversationSize > 0 else { self.loadedInteractions = [] updateCanLoadMore(fetchIndexSet: IndexSet(), conversationSize: conversationSize) @@ -190,7 +190,10 @@ public class CVMessageMapping: NSObject { // the distance from the oldest message, since most of the time the user will // be scrolled towards the bottom of the conversation. The further you scroll // from the bottom of the conversation, the more expensive this query will get. - guard let distanceFromLatest = try self.interactionFinder.distanceFromLatest(interactionUniqueId: interactionUniqueId, transaction: transaction) else { + guard let distanceFromLatest = try self.interactionFinder.distanceFromLatest( + interactionUniqueId: interactionUniqueId, + includingHiddenInteractions: false, + transaction: transaction) else { throw OWSAssertionError("viewIndex was unexpectedly nil") } return Int(conversationSize - distanceFromLatest - 1) @@ -474,6 +477,7 @@ public class CVMessageMapping: NSObject { var newItems: [TSInteraction] = [] try self.interactionFinder.enumerateInteractions(range: nsRange, + includingHiddenInteractions: DebugFlags.showFailedDecryptionPlaceholders.get(), transaction: transaction) { (interaction: TSInteraction, _) in newItems.append(interaction) } @@ -483,7 +487,10 @@ public class CVMessageMapping: NSObject { // Loading the mapping from the cache has the following steps: // // 1. Fetch the uniqueIds for the interactions in the load window/mapping. - let interactionIds = try interactionFinder.interactionIds(inRange: nsRange, transaction: transaction) + let interactionIds = try interactionFinder.interactionIds( + inRange: nsRange, + includingHiddenInteractions: DebugFlags.showFailedDecryptionPlaceholders.get(), + transaction: transaction) guard !interactionIds.isEmpty else { return [] } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index b06aef6f42..267e711724 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1663,6 +1663,9 @@ /* Error message for a decryption failure. Embeds {{sender short name}}. */ "ERROR_MESSAGE_DECRYPTION_FAILURE" = "A message from %@ could not be delivered"; +/* Error message for a decryption failure. */ +"ERROR_MESSAGE_DECRYPTION_FAILURE_UNKNOWN_SENDER" = "A message could not be delivered"; + /* No comment provided by engineer. */ "ERROR_MESSAGE_DUPLICATE_MESSAGE" = "Received a duplicate message."; diff --git a/SignalMessaging/Notifications/AppNotifications.swift b/SignalMessaging/Notifications/AppNotifications.swift index 6ef19deab1..b868874296 100644 --- a/SignalMessaging/Notifications/AppNotifications.swift +++ b/SignalMessaging/Notifications/AppNotifications.swift @@ -692,6 +692,8 @@ public class NotificationPresenter: NSObject, NotificationsProtocol { } public func notifyUser(for errorMessage: TSErrorMessage, thread: TSThread, transaction: SDSAnyWriteTransaction) { + guard (errorMessage is OWSRecoverableDecryptionPlaceholder) == false else { return } + switch errorMessage.errorType { case .noSession, .wrongTrustedIdentityKey, diff --git a/SignalServiceKit/Resources/schema.sql b/SignalServiceKit/Resources/schema.sql index 59681ab0be..006ef390f4 100644 --- a/SignalServiceKit/Resources/schema.sql +++ b/SignalServiceKit/Resources/schema.sql @@ -115,6 +115,7 @@ CREATE ,"paymentRequest" BLOB ,"viewed" BOOLEAN ,"serverGuid" TEXT + ,"hiddenUntilTimestamp" INTEGER ) ; diff --git a/SignalServiceKit/src/Contacts/TSThread.m b/SignalServiceKit/src/Contacts/TSThread.m index e9f36f50f0..ca93f5b977 100644 --- a/SignalServiceKit/src/Contacts/TSThread.m +++ b/SignalServiceKit/src/Contacts/TSThread.m @@ -357,7 +357,9 @@ lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPer - (NSUInteger)numberOfInteractionsWithTransaction:(SDSAnyReadTransaction *)transaction { OWSAssertDebug(transaction); - return [[[InteractionFinder alloc] initWithThreadUniqueId:self.uniqueId] countWithTransaction:transaction]; + return + [[[InteractionFinder alloc] initWithThreadUniqueId:self.uniqueId] countIncludingHiddenInteractions:YES + transaction:transaction]; } - (nullable TSInteraction *)lastInteractionForInboxWithTransaction:(SDSAnyReadTransaction *)transaction diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage+SDS.swift index e73662959e..c4f924d498 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage+SDS.swift @@ -125,7 +125,8 @@ class OWSDisappearingConfigurationUpdateInfoMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage+SDS.swift index c5361b58bf..19c6e47d43 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage+SDS.swift @@ -125,7 +125,8 @@ class OWSVerificationStateChangeMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage+SDS.swift index c0c8769167..c3d63896d6 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage+SDS.swift @@ -125,7 +125,8 @@ class TSErrorMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h index 7f82bcf8d4..b561007fde 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h @@ -129,6 +129,11 @@ NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp groupId:(nullable NSData *)groupId withTransaction:(SDSAnyWriteTransaction *)transaction; ++ (instancetype)failedDecryptionForSender:(nullable SignalServiceAddress *)sender + thread:(TSThread *)thread + timestamp:(uint64_t)timestamp + transaction:(SDSAnyWriteTransaction *)transaction; + @property (nonatomic, readonly) TSErrorMessageType errorType; @property (nullable, nonatomic, readonly) SignalServiceAddress *sender; @property (nullable, nonatomic, readonly) SignalServiceAddress *recipientAddress; diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m index d80d1a31fd..1bf73eee71 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m @@ -232,11 +232,16 @@ NSUInteger TSErrorMessageSchemaVersion = 2; return NSLocalizedString( @"ERROR_MESSAGE_SESSION_REFRESH", @"Text notifying the user that their secure session has been reset"); case TSErrorMessageDecryptionFailure: { - NSString *formatString = NSLocalizedString(@"ERROR_MESSAGE_DECRYPTION_FAILURE", - @"Error message for a decryption failure. Embeds {{sender short name}}."); - NSString *senderName = [self.contactsManager shortDisplayNameForAddress:self.sender - transaction:transaction]; - return [[NSString alloc] initWithFormat:formatString, senderName]; + if (self.sender) { + NSString *formatString = NSLocalizedString(@"ERROR_MESSAGE_DECRYPTION_FAILURE", + @"Error message for a decryption failure. Embeds {{sender short name}}."); + NSString *senderName = [self.contactsManager shortDisplayNameForAddress:self.sender + transaction:transaction]; + return [[NSString alloc] initWithFormat:formatString, senderName]; + } else { + return NSLocalizedString( + @"ERROR_MESSAGE_DECRYPTION_FAILURE_UNKNOWN_SENDER", @"Error message for a decryption failure."); + } } default: OWSFailDebug(@"failure: unknown error type"); @@ -296,6 +301,18 @@ NSUInteger TSErrorMessageSchemaVersion = 2; return [builder build]; } ++ (instancetype)failedDecryptionForSender:(nullable SignalServiceAddress *)sender + thread:(TSThread *)thread + timestamp:(uint64_t)timestamp + transaction:(SDSAnyWriteTransaction *)transaction +{ + TSErrorMessageBuilder *builder = + [TSErrorMessageBuilder errorMessageBuilderWithThread:thread errorType:TSErrorMessageDecryptionFailure]; + builder.senderAddress = sender; + builder.timestamp = timestamp; + return [builder build]; +} + + (instancetype)failedDecryptionForEnvelope:(SSKProtoEnvelope *)envelope groupId:(nullable NSData *)groupId withTransaction:(SDSAnyWriteTransaction *)transaction @@ -318,11 +335,7 @@ NSUInteger TSErrorMessageSchemaVersion = 2; if (!thread) { return nil; } - TSErrorMessageBuilder *builder = - [TSErrorMessageBuilder errorMessageBuilderWithThread:thread errorType:TSErrorMessageDecryptionFailure]; - builder.senderAddress = sender; - builder.timestamp = envelope.timestamp; - return [builder build]; + return [self failedDecryptionForSender:sender thread:thread timestamp:envelope.timestamp transaction:transaction]; } #pragma mark - OWSReadTracking diff --git a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+SDS.swift index 97f644af8e..562da909f7 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage+SDS.swift @@ -125,7 +125,8 @@ class TSIncomingMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = model.wasViewed let serverGuid: String? = model.serverGuid + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage+SDS.swift index 9ba59245b0..7a19442fcc 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage+SDS.swift @@ -125,7 +125,8 @@ class TSInfoMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSInteraction+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSInteraction+SDS.swift index cd5ca164f1..fccf3fe45e 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInteraction+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSInteraction+SDS.swift @@ -92,6 +92,7 @@ public struct InteractionRecord: SDSRecord { public let paymentRequest: Data? public let viewed: Bool? public let serverGuid: String? + public let hiddenUntilTimestamp: UInt64? public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable { case id @@ -161,6 +162,7 @@ public struct InteractionRecord: SDSRecord { case paymentRequest case viewed case serverGuid + case hiddenUntilTimestamp } public static func columnName(_ column: InteractionRecord.CodingKeys, fullyQualified: Bool = false) -> String { @@ -251,6 +253,7 @@ public extension InteractionRecord { paymentRequest = row[64] viewed = row[65] serverGuid = row[66] + hiddenUntilTimestamp = row[67] } } @@ -625,6 +628,7 @@ extension TSInteraction { let senderSerialized: Data? = record.sender let sender: SignalServiceAddress? = try SDSDeserialization.optionalUnarchive(senderSerialized, name: "sender") let wasIdentityVerified: Bool = try SDSDeserialization.required(record.wasIdentityVerified, name: "wasIdentityVerified") + let hiddenUntilTimestamp: UInt64 = try SDSDeserialization.required(record.hiddenUntilTimestamp, name: "hiddenUntilTimestamp") return OWSRecoverableDecryptionPlaceholder(grdbId: recordId, uniqueId: uniqueId, @@ -650,7 +654,8 @@ extension TSInteraction { read: read, recipientAddress: recipientAddress, sender: sender, - wasIdentityVerified: wasIdentityVerified) + wasIdentityVerified: wasIdentityVerified, + hiddenUntilTimestamp: hiddenUntilTimestamp) case .unknownContactBlockOfferMessage: @@ -3546,6 +3551,7 @@ extension TSInteraction: DeepCopyable { sender = nil } let wasIdentityVerified: Bool = modelToCopy.wasIdentityVerified + let hiddenUntilTimestamp: UInt64 = modelToCopy.hiddenUntilTimestamp return OWSRecoverableDecryptionPlaceholder(grdbId: id, uniqueId: uniqueId, @@ -3571,7 +3577,8 @@ extension TSInteraction: DeepCopyable { read: read, recipientAddress: recipientAddress, sender: sender, - wasIdentityVerified: wasIdentityVerified) + wasIdentityVerified: wasIdentityVerified, + hiddenUntilTimestamp: hiddenUntilTimestamp) } if let modelToCopy = self as? TSErrorMessage { @@ -3974,6 +3981,7 @@ extension TSInteractionSerializer { static let paymentRequestColumn = SDSColumnMetadata(columnName: "paymentRequest", columnType: .blob, isOptional: true) static let viewedColumn = SDSColumnMetadata(columnName: "viewed", columnType: .int, isOptional: true) static let serverGuidColumn = SDSColumnMetadata(columnName: "serverGuid", columnType: .unicodeString, isOptional: true) + static let hiddenUntilTimestampColumn = SDSColumnMetadata(columnName: "hiddenUntilTimestamp", columnType: .int64, isOptional: true) // TODO: We should decide on a naming convention for // tables that store models. @@ -4046,7 +4054,8 @@ extension TSInteractionSerializer { paymentNotificationColumn, paymentRequestColumn, viewedColumn, - serverGuidColumn + serverGuidColumn, + hiddenUntilTimestampColumn ]) } @@ -4520,8 +4529,9 @@ class TSInteractionSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSInteraction.swift b/SignalServiceKit/src/Messages/Interactions/TSInteraction.swift index 01e6e06f0c..bbe9df47ee 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInteraction.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSInteraction.swift @@ -56,10 +56,9 @@ extension TSInteraction { // there was no meaningful interaction. return false case .decryptionFailure: - if let replaceableInteraction = errorMessage as? OWSRecoverableDecryptionPlaceholder { - // Replaceable interactions may be temporarily hidden if we expect we'll be - // able to recover - return replaceableInteraction.isVisible + if errorMessage is OWSRecoverableDecryptionPlaceholder { + // Replaceable interactions should never be shown to the user + return false } else { return true } @@ -119,40 +118,9 @@ extension TSInteraction { if placeholder.supportsReplacement { placeholder.replaceWithInteraction(self, writeTx: transaction) return true - } else if placeholder.uniqueThreadId == uniqueThreadId { - // We've found the placeholder for the replacement message, but it is no longer eligible - // for replacement. We now want to preserve this placeholder as a permanent error message. - // - // In many places we assume interaction timestamps are unique for a thread. So we adjust the timestamp - // to preserve this uniqueness. It's likely nothing will collide, but we walk back over a small window - // to find an opening just in case. - let timestampWindow = (placeholder.timestamp - 10 ..< placeholder.timestamp).reversed() - - let newTimestamp = timestampWindow.first { candidate in - guard candidate > 0 else { return false } - - do { - return try InteractionFinder.interactions(withTimestamp: candidate, filter: { interaction in - interaction.uniqueThreadId == placeholder.uniqueThreadId - }, transaction: transaction).isEmpty - } catch { - owsFailDebug("\(error)") - return false - } - } - - if let newTimestamp = newTimestamp, newTimestamp > 0 { - Logger.info("Placeholder not eligible for replacement. Updating timestamp") - placeholder.anyUpdate(transaction: transaction) { interaction in - (interaction as? OWSRecoverableDecryptionPlaceholder)?.adjustTimestamp(newTimestamp) - } - } else { - Logger.warn("Placeholder not eligible for replacement. Failed to find free timestamp. Deleting placeholder.") - placeholder.anyRemove(transaction: transaction) - } - return false } else { - Logger.info("Placeholder not eligible for replacement. No need to adjust timestamp") + Logger.info("Placeholder not eligible for replacement, deleting.") + placeholder.anyRemove(transaction: transaction) return false } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSMessage+SDS.swift index f638d6d241..126ca4ccd3 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage+SDS.swift @@ -125,7 +125,8 @@ class TSMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage+SDS.swift index e765183d47..89810e72ff 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage+SDS.swift @@ -125,7 +125,8 @@ class TSOutgoingMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/Interactions/TSUnreadIndicatorInteraction+SDS.swift b/SignalServiceKit/src/Messages/Interactions/TSUnreadIndicatorInteraction+SDS.swift index 5d9d4a02a9..4e9f3e04d9 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSUnreadIndicatorInteraction+SDS.swift +++ b/SignalServiceKit/src/Messages/Interactions/TSUnreadIndicatorInteraction+SDS.swift @@ -125,7 +125,8 @@ class TSUnreadIndicatorInteractionSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyErrorMessage+SDS.swift b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyErrorMessage+SDS.swift index 6f59a384be..0c0e5885bc 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyErrorMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyErrorMessage+SDS.swift @@ -125,7 +125,8 @@ class TSInvalidIdentityKeyErrorMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage+SDS.swift b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage+SDS.swift index ef941ff819..7bf2aba914 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage+SDS.swift @@ -125,7 +125,8 @@ class TSInvalidIdentityKeyReceivingErrorMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage+SDS.swift b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage+SDS.swift index a1f24442e3..02591a8b29 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage+SDS.swift @@ -125,7 +125,8 @@ class TSInvalidIdentityKeySendingErrorMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage+SDS.swift index e6b13bbe0e..281d374e0a 100644 --- a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage+SDS.swift @@ -125,7 +125,8 @@ class OWSAddToContactsOfferMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage+SDS.swift index 8729f2bf7e..9eaa5472e6 100644 --- a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage+SDS.swift @@ -125,7 +125,8 @@ class OWSAddToProfileWhitelistOfferMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/OWSGroupCallMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSGroupCallMessage+SDS.swift index 19af4b0412..4dfe76c455 100644 --- a/SignalServiceKit/src/Messages/OWSGroupCallMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSGroupCallMessage+SDS.swift @@ -125,7 +125,8 @@ class OWSGroupCallMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/OWSMessageDecrypter.swift b/SignalServiceKit/src/Messages/OWSMessageDecrypter.swift index 80c193b1e7..26827c6d2d 100644 --- a/SignalServiceKit/src/Messages/OWSMessageDecrypter.swift +++ b/SignalServiceKit/src/Messages/OWSMessageDecrypter.swift @@ -51,6 +51,9 @@ public class OWSMessageDecryptResult: NSObject { public class OWSMessageDecrypter: OWSMessageHandler { private var senderIdsResetDuringCurrentBatch = NSMutableSet() + private var placeholderCleanupTimer: Timer? { + didSet { oldValue?.invalidate() } + } public override init() { super.init() @@ -63,6 +66,12 @@ public class OWSMessageDecrypter: OWSMessageHandler { name: MessageProcessor.messageProcessorDidFlushQueue, object: nil ) + + AppReadiness.runNowOrWhenAppDidBecomeReadySync { [weak self] in + self?.databaseStorage.read { [weak self] readTx in + self?.schedulePlaceholderCleanup(transaction: readTx) + } + } } public func decryptEnvelope(_ envelope: SSKProtoEnvelope, envelopeData: Data, transaction: SDSAnyWriteTransaction) -> Result { @@ -768,6 +777,59 @@ public class OWSMessageDecrypter: OWSMessageHandler { return error } } + + @objc + func schedulePlaceholderCleanup(transaction readTx: SDSAnyReadTransaction) { + guard let oldestPlaceholder = GRDBInteractionFinder.oldestPlaceholderInteraction(transaction: readTx.unwrapGrdbRead) else { return } + + DispatchQueue.main.async { + if oldestPlaceholder.expirationDate.isBeforeNow { + Logger.info("Oldest placeholder expirationDate: \(oldestPlaceholder.expirationDate). Will perform cleanup...") + self.placeholderCleanupTimer = nil + self.cleanupExpiredPlaceholders() + + } else if (self.placeholderCleanupTimer?.fireDate ?? .distantFuture).isAfter(oldestPlaceholder.expirationDate) { + Logger.info("Oldest placeholder expirationDate: \(oldestPlaceholder.expirationDate). Scheduling timer...") + + self.placeholderCleanupTimer = Timer.scheduledTimer( + withTimeInterval: oldestPlaceholder.expirationDate.timeIntervalSinceNow, + repeats: false, + block: { [weak self] _ in + self?.cleanupExpiredPlaceholders() + }) + } + } + } + + func cleanupExpiredPlaceholders() { + databaseStorage.asyncWrite { writeTx in + Logger.info("Performing placeholder cleanup") + GRDBInteractionFinder.enumeratePlaceholders(transaction: writeTx.unwrapGrdbWrite) { placeholder, _ in + if placeholder.expirationDate.isBeforeNow { + Logger.info("replacing placeholder \(placeholder.timestamp) with error message") + + let thread = placeholder.thread(transaction: writeTx) + let errorMessage = TSErrorMessage.failedDecryption( + forSender: placeholder.sender, + thread: thread, + timestamp: NSDate.ows_millisecondTimeStamp(), + transaction: writeTx) + + placeholder.anyRemove(transaction: writeTx) + errorMessage.anyInsert(transaction: writeTx) + + self.notificationsManager?.notifyUser(for: errorMessage, + thread: thread, + transaction: writeTx) + } + } + } completion: { + self.databaseStorage.read { readTx in + self.schedulePlaceholderCleanup(transaction: readTx) + } + } + + } } private extension SSKProtoEnvelope { diff --git a/SignalServiceKit/src/Messages/OWSOutgoingResendResponse.m b/SignalServiceKit/src/Messages/OWSOutgoingResendResponse.m index c64a4f42de..1fa6c020a2 100644 --- a/SignalServiceKit/src/Messages/OWSOutgoingResendResponse.m +++ b/SignalServiceKit/src/Messages/OWSOutgoingResendResponse.m @@ -24,18 +24,19 @@ { OWSAssertDebug(address); OWSAssertDebug(transaction); - - Payload *payloadRecord = [MessageSendLog fetchPayloadWithAddress:address - deviceId:deviceId - timestamp:failedTimestamp - transaction:transaction]; - - TSThread *originalThread = [TSThread anyFetchWithUniqueId:payloadRecord.uniqueThreadId transaction:transaction]; TSThread *targetThread = [TSContactThread getOrCreateThreadWithContactAddress:address transaction:transaction]; TSOutgoingMessageBuilder *builder = [TSOutgoingMessageBuilder outgoingMessageBuilderWithThread:targetThread]; + Payload *_Nullable payloadRecord = [MessageSendLog fetchPayloadWithAddress:address + deviceId:deviceId + timestamp:failedTimestamp + transaction:transaction]; + + TSThread *_Nullable originalThread = nil; if (payloadRecord) { OWSLogInfo(@"Found an MSL record for resend request: %lli", failedTimestamp); + originalThread = [TSThread anyFetchWithUniqueId:payloadRecord.uniqueThreadId transaction:transaction]; + // We should inherit the timestamp of the failed message. This allows the recipient of this message // to correlate the resend response with the original failed message. builder.timestamp = payloadRecord.sentTimestamp; diff --git a/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+Replace.swift b/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+Replace.swift index 9ab06a4b58..bd4664d5dc 100644 --- a/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+Replace.swift +++ b/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+Replace.swift @@ -10,7 +10,9 @@ extension OWSRecoverableDecryptionPlaceholder { func replaceWithInteraction(_ interaction: TSInteraction, writeTx: SDSAnyWriteTransaction) { Logger.info("Replacing placeholder with recovered interaction: \(interaction.timestamp)") guard let inheritedId = grdbId?.int64Value else { return owsFailDebug("Missing rowId") } + interaction.replaceRowId(inheritedId, uniqueId: uniqueId) + interaction.replaceSortId(UInt64(inheritedId)) interaction.anyOverwritingUpdate(transaction: writeTx) } diff --git a/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+SDS.swift b/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+SDS.swift index 8c8c662466..693b5e11eb 100644 --- a/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder+SDS.swift @@ -125,7 +125,8 @@ class OWSRecoverableDecryptionPlaceholderSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = model.hiddenUntilTimestamp - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder.h b/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder.h index 50b17ce020..e9d3aa66f2 100644 --- a/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder.h +++ b/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder.h @@ -16,14 +16,10 @@ NS_ASSUME_NONNULL_BEGIN @property (assign, nonatomic, readonly) BOOL isVisible; @property (assign, nonatomic, readonly) BOOL supportsReplacement; +@property (assign, nonatomic, readonly) uint64_t hiddenUntilTimestamp; -// Used to resolve timestamp collisions for a long-lived placeholder. -// If a placeholder has outlived its eligible replacement period, it will live on as an error message -// even after the replacement content has been received. -// -// Since we need to persist *both* the error and the replacement message, we adjust the placeholder's timestamp -// slightly to resolve the timestamp collision. -- (void)adjustTimestamp:(uint64_t)timestamp; +/// After this date, the placeholder is no longer eligible for replacement with the original content. +@property (strong, nonatomic, readonly) NSDate *expirationDate; // --- CODE GENERATION MARKER @@ -31,6 +27,35 @@ NS_ASSUME_NONNULL_BEGIN // `sds_codegen.sh`. // clang-format off + +- (instancetype)initWithGrdbId:(int64_t)grdbId + uniqueId:(NSString *)uniqueId + receivedAtTimestamp:(uint64_t)receivedAtTimestamp + sortId:(uint64_t)sortId + timestamp:(uint64_t)timestamp + uniqueThreadId:(NSString *)uniqueThreadId + attachmentIds:(NSArray *)attachmentIds + body:(nullable NSString *)body + bodyRanges:(nullable MessageBodyRanges *)bodyRanges + contactShare:(nullable OWSContact *)contactShare + expireStartedAt:(uint64_t)expireStartedAt + expiresAt:(uint64_t)expiresAt + expiresInSeconds:(unsigned int)expiresInSeconds + isViewOnceComplete:(BOOL)isViewOnceComplete + isViewOnceMessage:(BOOL)isViewOnceMessage + linkPreview:(nullable OWSLinkPreview *)linkPreview + messageSticker:(nullable MessageSticker *)messageSticker + quotedMessage:(nullable TSQuotedMessage *)quotedMessage + storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + wasRemotelyDeleted:(BOOL)wasRemotelyDeleted + errorType:(TSErrorMessageType)errorType + read:(BOOL)read + recipientAddress:(nullable SignalServiceAddress *)recipientAddress + sender:(nullable SignalServiceAddress *)sender + wasIdentityVerified:(BOOL)wasIdentityVerified + hiddenUntilTimestamp:(uint64_t)hiddenUntilTimestamp +NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:attachmentIds:body:bodyRanges:contactShare:expireStartedAt:expiresAt:expiresInSeconds:isViewOnceComplete:isViewOnceMessage:linkPreview:messageSticker:quotedMessage:storedShouldStartExpireTimer:wasRemotelyDeleted:errorType:read:recipientAddress:sender:wasIdentityVerified:hiddenUntilTimestamp:)); + // clang-format on // --- CODE GENERATION MARKER diff --git a/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder.m b/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder.m index fdbb671e28..66ffe96244 100644 --- a/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder.m +++ b/SignalServiceKit/src/Messages/OWSRecoverableDecryptionPlaceholder.m @@ -37,7 +37,11 @@ NS_ASSUME_NONNULL_BEGIN builder.timestamp = envelope.timestamp; builder.senderAddress = sender; - return [super initErrorMessageWithBuilder:builder]; + self = [super initErrorMessageWithBuilder:builder]; + if (self) { + _hiddenUntilTimestamp = [NSDate distantFutureMillisecondTimestamp]; + } + return self; } - (nullable instancetype)initWithCoder:(NSCoder *)coder @@ -51,40 +55,129 @@ NS_ASSUME_NONNULL_BEGIN // `sds_codegen.sh`. // clang-format off + +- (instancetype)initWithGrdbId:(int64_t)grdbId + uniqueId:(NSString *)uniqueId + receivedAtTimestamp:(uint64_t)receivedAtTimestamp + sortId:(uint64_t)sortId + timestamp:(uint64_t)timestamp + uniqueThreadId:(NSString *)uniqueThreadId + attachmentIds:(NSArray *)attachmentIds + body:(nullable NSString *)body + bodyRanges:(nullable MessageBodyRanges *)bodyRanges + contactShare:(nullable OWSContact *)contactShare + expireStartedAt:(uint64_t)expireStartedAt + expiresAt:(uint64_t)expiresAt + expiresInSeconds:(unsigned int)expiresInSeconds + isViewOnceComplete:(BOOL)isViewOnceComplete + isViewOnceMessage:(BOOL)isViewOnceMessage + linkPreview:(nullable OWSLinkPreview *)linkPreview + messageSticker:(nullable MessageSticker *)messageSticker + quotedMessage:(nullable TSQuotedMessage *)quotedMessage + storedShouldStartExpireTimer:(BOOL)storedShouldStartExpireTimer + wasRemotelyDeleted:(BOOL)wasRemotelyDeleted + errorType:(TSErrorMessageType)errorType + read:(BOOL)read + recipientAddress:(nullable SignalServiceAddress *)recipientAddress + sender:(nullable SignalServiceAddress *)sender + wasIdentityVerified:(BOOL)wasIdentityVerified + hiddenUntilTimestamp:(uint64_t)hiddenUntilTimestamp +{ + self = [super initWithGrdbId:grdbId + uniqueId:uniqueId + receivedAtTimestamp:receivedAtTimestamp + sortId:sortId + timestamp:timestamp + uniqueThreadId:uniqueThreadId + attachmentIds:attachmentIds + body:body + bodyRanges:bodyRanges + contactShare:contactShare + expireStartedAt:expireStartedAt + expiresAt:expiresAt + expiresInSeconds:expiresInSeconds + isViewOnceComplete:isViewOnceComplete + isViewOnceMessage:isViewOnceMessage + linkPreview:linkPreview + messageSticker:messageSticker + quotedMessage:quotedMessage + storedShouldStartExpireTimer:storedShouldStartExpireTimer + wasRemotelyDeleted:wasRemotelyDeleted + errorType:errorType + read:read + recipientAddress:recipientAddress + sender:sender + wasIdentityVerified:wasIdentityVerified]; + + if (!self) { + return self; + } + + _hiddenUntilTimestamp = hiddenUntilTimestamp; + + return self; +} + // clang-format on // --- CODE GENERATION MARKER #pragma mark - Methods -- (void)adjustTimestamp:(uint64_t)timestamp +- (NSDate *)expirationDate { - OWSAssert(timestamp > 0); - self.timestamp = timestamp; -} - -- (BOOL)isVisible -{ - // If this interaction has ever been seen or the recovery period has elapsed, we should make - // this visible to the user. NSTimeInterval expirationInterval = [RemoteConfig replaceableInteractionExpiration]; OWSAssertDebug(expirationInterval >= 0); - NSDate *expiration = [self.receivedAtDate dateByAddingTimeInterval:MAX(0, expirationInterval)]; - return [expiration isBeforeNow] || self.wasRead; + if (SSKDebugFlags.fastPlaceholderExpiration.value) { + expirationInterval = MIN(expirationInterval, 5.0); + } + + return [self.receivedAtDate dateByAddingTimeInterval:MAX(0, expirationInterval)]; } - (BOOL)supportsReplacement { - return !self.isVisible; + return [self.expirationDate isAfterNow] && !self.wasRead; } - (NSString *)previewTextWithTransaction:(SDSAnyReadTransaction *)transaction { - NSString *formatString = NSLocalizedString( - @"ERROR_MESSAGE_DECRYPTION_FAILURE", @"Error message for a decryption failure. Embeds {{sender short name}}."); - NSString *senderName = [self.contactsManager shortDisplayNameForAddress:self.sender transaction:transaction]; - return [[NSString alloc] initWithFormat:formatString, senderName]; + NSString *_Nullable senderName = nil; + if (self.sender) { + senderName = [self.contactsManager shortDisplayNameForAddress:self.sender transaction:transaction]; + } + + if (SSKDebugFlags.showFailedDecryptionPlaceholders.value) { + return [[NSString alloc] + initWithFormat:@"Placeholder for timestamp: %llu from sender: %@", self.timestamp, senderName]; + } else if (senderName) { + OWSFailDebug(@"Should not be directly surfaced to user"); + NSString *formatString = NSLocalizedString(@"ERROR_MESSAGE_DECRYPTION_FAILURE", + @"Error message for a decryption failure. Embeds {{sender short name}}."); + return [[NSString alloc] initWithFormat:formatString, senderName]; + } else { + OWSFailDebug(@"Should not be directly surfaced to user"); + return NSLocalizedString( + @"ERROR_MESSAGE_DECRYPTION_FAILURE_UNKNOWN_SENDER", @"Error message for a decryption failure."); + } +} + +- (void)anyDidInsertWithTransaction:(SDSAnyWriteTransaction *)transaction +{ + [super anyDidInsertWithTransaction:transaction]; + [self.messageDecrypter schedulePlaceholderCleanupWithTransaction:transaction]; +} + +#pragma mark - + +- (void)markAsReadAtTimestamp:(uint64_t)readTimestamp + thread:(TSThread *)thread + circumstance:(OWSReceiptCircumstance)circumstance + transaction:(SDSAnyWriteTransaction *)transaction +{ + OWSLogInfo(@"Marking placeholder as read. No longer eligible for inline replacement."); + [super markAsReadAtTimestamp:readTimestamp thread:thread circumstance:circumstance transaction:transaction]; } @end diff --git a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage+SDS.swift index cc7c7b503c..7a7e73a888 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage+SDS.swift @@ -125,7 +125,8 @@ class OWSUnknownContactBlockOfferMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage+SDS.swift b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage+SDS.swift index 3c73565689..17b9bbae5b 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/OWSUnknownProtocolVersionMessage+SDS.swift @@ -125,7 +125,8 @@ class OWSUnknownProtocolVersionMessageSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage+SDS.swift b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage+SDS.swift index f361ca4b29..70f1354840 100644 --- a/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage+SDS.swift +++ b/SignalServiceKit/src/Messages/Payments/OWSOutgoingPaymentMessage+SDS.swift @@ -125,7 +125,8 @@ class OWSOutgoingPaymentMessageSerializer: SDSSerializer { let paymentRequest: Data? = optionalArchive(model.paymentRequest) let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Messages/TSCall+SDS.swift b/SignalServiceKit/src/Messages/TSCall+SDS.swift index 949524a3cf..e58e9bf7b9 100644 --- a/SignalServiceKit/src/Messages/TSCall+SDS.swift +++ b/SignalServiceKit/src/Messages/TSCall+SDS.swift @@ -125,7 +125,8 @@ class TSCallSerializer: SDSSerializer { let paymentRequest: Data? = nil let viewed: Bool? = nil let serverGuid: String? = nil + let hiddenUntilTimestamp: UInt64? = nil - return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid) + return InteractionRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, receivedAtTimestamp: receivedAtTimestamp, timestamp: timestamp, threadUniqueId: threadUniqueId, attachmentIds: attachmentIds, authorId: authorId, authorPhoneNumber: authorPhoneNumber, authorUUID: authorUUID, body: body, callType: callType, configurationDurationSeconds: configurationDurationSeconds, configurationIsEnabled: configurationIsEnabled, contactShare: contactShare, createdByRemoteName: createdByRemoteName, createdInExistingGroup: createdInExistingGroup, customMessage: customMessage, envelopeData: envelopeData, errorType: errorType, expireStartedAt: expireStartedAt, expiresAt: expiresAt, expiresInSeconds: expiresInSeconds, groupMetaMessage: groupMetaMessage, hasLegacyMessageState: hasLegacyMessageState, hasSyncedTranscript: hasSyncedTranscript, isFromLinkedDevice: isFromLinkedDevice, isLocalChange: isLocalChange, isViewOnceComplete: isViewOnceComplete, isViewOnceMessage: isViewOnceMessage, isVoiceMessage: isVoiceMessage, legacyMessageState: legacyMessageState, legacyWasDelivered: legacyWasDelivered, linkPreview: linkPreview, messageId: messageId, messageSticker: messageSticker, messageType: messageType, mostRecentFailureText: mostRecentFailureText, preKeyBundle: preKeyBundle, protocolVersion: protocolVersion, quotedMessage: quotedMessage, read: read, recipientAddress: recipientAddress, recipientAddressStates: recipientAddressStates, sender: sender, serverTimestamp: serverTimestamp, sourceDeviceId: sourceDeviceId, storedMessageState: storedMessageState, storedShouldStartExpireTimer: storedShouldStartExpireTimer, unregisteredAddress: unregisteredAddress, verificationState: verificationState, wasReceivedByUD: wasReceivedByUD, infoMessageUserInfo: infoMessageUserInfo, wasRemotelyDeleted: wasRemotelyDeleted, bodyRanges: bodyRanges, offerType: offerType, serverDeliveryTimestamp: serverDeliveryTimestamp, eraId: eraId, hasEnded: hasEnded, creatorUuid: creatorUuid, joinedMemberUuids: joinedMemberUuids, wasIdentityVerified: wasIdentityVerified, paymentCancellation: paymentCancellation, paymentNotification: paymentNotification, paymentRequest: paymentRequest, viewed: viewed, serverGuid: serverGuid, hiddenUntilTimestamp: hiddenUntilTimestamp) } } diff --git a/SignalServiceKit/src/Storage/Database/GRDBSchemaMigrator.swift b/SignalServiceKit/src/Storage/Database/GRDBSchemaMigrator.swift index 0527ed1ea0..98be019c25 100644 --- a/SignalServiceKit/src/Storage/Database/GRDBSchemaMigrator.swift +++ b/SignalServiceKit/src/Storage/Database/GRDBSchemaMigrator.swift @@ -111,6 +111,7 @@ public class GRDBSchemaMigrator: NSObject { case addSendCompletionToMessageSendLog case addExclusiveProcessIdentifierAndHighPriorityToJobRecord case updateMessageSendLogColumnTypes + case addHiddenInteractionColumn // NOTE: Every time we add a migration id, consider // incrementing grdbSchemaVersionLatest. @@ -1398,6 +1399,16 @@ public class GRDBSchemaMigrator: NSObject { } } + migrator.registerMigration(MigrationId.addHiddenInteractionColumn.rawValue) { db in + do { + try db.alter(table: "model_TSInteraction") { (table: TableAlteration) -> Void in + table.add(column: "hiddenUntilTimestamp", .integer) + } + } catch { + owsFail("Error: \(error)") + } + } + // MARK: - Schema Migration Insertion Point } diff --git a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift index daf59104fe..e825678a3a 100644 --- a/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift +++ b/SignalServiceKit/src/Storage/Database/Records/InteractionFinder.swift @@ -39,12 +39,12 @@ protocol InteractionFinderAdapter { func earliestKnownInteractionRowId(transaction: ReadTransaction) -> Int? - func distanceFromLatest(interactionUniqueId: String, transaction: ReadTransaction) throws -> UInt? - func count(transaction: ReadTransaction) -> UInt + func distanceFromLatest(interactionUniqueId: String, includingHiddenInteractions countHidden: Bool, transaction: ReadTransaction) throws -> UInt? + func count(includingHiddenInteractions: Bool, transaction: ReadTransaction) -> UInt func enumerateInteractionIds(transaction: ReadTransaction, block: @escaping (String, UnsafeMutablePointer) throws -> Void) throws func enumerateRecentInteractions(transaction: ReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws - func enumerateInteractions(range: NSRange, transaction: ReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws - func interactionIds(inRange range: NSRange, transaction: ReadTransaction) throws -> [String] + func enumerateInteractions(range: NSRange, includingHiddenInteractions: Bool, transaction: ReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws + func interactionIds(inRange range: NSRange, includingHiddenInteractions: Bool, transaction: ReadTransaction) throws -> [String] func existsOutgoingMessage(transaction: ReadTransaction) -> Bool func outgoingMessageCount(transaction: ReadTransaction) -> UInt @@ -287,20 +287,20 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { } } - public func distanceFromLatest(interactionUniqueId: String, transaction: SDSAnyReadTransaction) throws -> UInt? { + public func distanceFromLatest(interactionUniqueId: String, includingHiddenInteractions countHidden: Bool = true, transaction: SDSAnyReadTransaction) throws -> UInt? { return try Bench(title: "InteractionFinder.distanceFromLatest") { switch transaction.readTransaction { case .grdbRead(let grdbRead): - return try grdbAdapter.distanceFromLatest(interactionUniqueId: interactionUniqueId, transaction: grdbRead) + return try grdbAdapter.distanceFromLatest(interactionUniqueId: interactionUniqueId, includingHiddenInteractions: countHidden, transaction: grdbRead) } } } @objc - public func count(transaction: SDSAnyReadTransaction) -> UInt { + public func count(includingHiddenInteractions includeHidden: Bool = true, transaction: SDSAnyReadTransaction) -> UInt { switch transaction.readTransaction { case .grdbRead(let grdbRead): - return grdbAdapter.count(transaction: grdbRead) + return grdbAdapter.count(includingHiddenInteractions: includeHidden, transaction: grdbRead) } } @@ -351,17 +351,17 @@ public class InteractionFinder: NSObject, InteractionFinderAdapter { } } - public func enumerateInteractions(range: NSRange, transaction: SDSAnyReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws { + public func enumerateInteractions(range: NSRange, includingHiddenInteractions includeHidden: Bool = true, transaction: SDSAnyReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws { switch transaction.readTransaction { case .grdbRead(let grdbRead): - return try grdbAdapter.enumerateInteractions(range: range, transaction: grdbRead, block: block) + return try grdbAdapter.enumerateInteractions(range: range, includingHiddenInteractions: includeHidden, transaction: grdbRead, block: block) } } - public func interactionIds(inRange range: NSRange, transaction: SDSAnyReadTransaction) throws -> [String] { + public func interactionIds(inRange range: NSRange, includingHiddenInteractions includeHidden: Bool = true, transaction: SDSAnyReadTransaction) throws -> [String] { switch transaction.readTransaction { case .grdbRead(let grdbRead): - return try grdbAdapter.interactionIds(inRange: range, transaction: grdbRead) + return try grdbAdapter.interactionIds(inRange: range, includingHiddenInteractions: includeHidden, transaction: grdbRead) } } @@ -849,6 +849,50 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { return interactions } + static func oldestPlaceholderInteraction(transaction: GRDBReadTransaction) -> OWSRecoverableDecryptionPlaceholder? { + let sql = """ + SELECT * + FROM \(InteractionRecord.databaseTableName) + WHERE \(interactionColumn: .recordType) IS \(SDSRecordType.recoverableDecryptionPlaceholder.rawValue) + ORDER BY \(interactionColumn: .id) ASC + LIMIT 1 + """ + let result = TSInteraction.grdbFetchOne(sql: sql, transaction: transaction) + if let result = result as? OWSRecoverableDecryptionPlaceholder { + return result + } else if let result = result { + owsFailDebug("Unexpected type: \(type(of: result))") + return nil + } else { + return nil + } + } + + static func enumeratePlaceholders(transaction: GRDBReadTransaction, block: (OWSRecoverableDecryptionPlaceholder, UnsafeMutablePointer) -> Void) { + let sql = """ + SELECT * + FROM \(InteractionRecord.databaseTableName) + WHERE \(interactionColumn: .recordType) IS \(SDSRecordType.recoverableDecryptionPlaceholder.rawValue) + ORDER BY \(interactionColumn: .id) ASC + """ + do { + let cursor = TSInteraction.grdbFetchCursor(sql: sql, transaction: transaction) + while let result = try cursor.next() { + if let placeholder = result as? OWSRecoverableDecryptionPlaceholder { + var stop: ObjCBool = false + block(placeholder, &stop) + if stop.boolValue { + return + } + } else { + owsFailDebug("Unexpected type: \(type(of: result))") + } + } + } catch { + owsFailDebug("unexpected error \(error)") + } + } + // MARK: - instance methods public func profileUpdateInteractions(afterSortId sortId: UInt64, transaction: GRDBReadTransaction) -> [TSInfoMessage] { @@ -965,11 +1009,14 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { return try? Int.fetchOne(transaction.database, sql: sql, arguments: arguments) } - func distanceFromLatest(interactionUniqueId: String, transaction: GRDBReadTransaction) throws -> UInt? { + func distanceFromLatest(interactionUniqueId: String, includingHiddenInteractions countHidden: Bool = true, transaction: GRDBReadTransaction) throws -> UInt? { + let hiddenInteractionFilterClause = "AND (\(interactionColumn: .hiddenUntilTimestamp) < \(Date().ows_millisecondsSince1970) OR \(interactionColumn: .hiddenUntilTimestamp) IS NULL)" + guard let interactionId = try UInt.fetchOne(transaction.database, sql: """ SELECT id FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .uniqueId) = ? + \(countHidden ? "" : hiddenInteractionFilterClause) """, arguments: [interactionUniqueId]) else { owsFailDebug("failed to find id for interaction \(interactionUniqueId)") return nil @@ -980,6 +1027,7 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? AND \(interactionColumn: .id) >= ? + \(countHidden ? "" : hiddenInteractionFilterClause) ORDER BY \(interactionColumn: .id) DESC """, arguments: [threadUniqueId, interactionId]) else { owsFailDebug("failed to find distance from latest message") @@ -989,13 +1037,15 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { return distanceFromLatest } - func count(transaction: GRDBReadTransaction) -> UInt { + func count(includingHiddenInteractions includeHidden: Bool = true, transaction: GRDBReadTransaction) -> UInt { do { + let hiddenInteractionFilterClause = "AND (\(interactionColumn: .hiddenUntilTimestamp) < \(Date().ows_millisecondsSince1970) OR \(interactionColumn: .hiddenUntilTimestamp) IS NULL)" guard let count = try UInt.fetchOne(transaction.database, sql: """ SELECT COUNT(*) FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? + \(includeHidden ? "" : hiddenInteractionFilterClause) """, arguments: [threadUniqueId]) else { throw OWSAssertionError("count was unexpectedly nil") @@ -1046,11 +1096,13 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { } } - func enumerateInteractions(range: NSRange, transaction: GRDBReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws { + func enumerateInteractions(range: NSRange, includingHiddenInteractions includeHidden: Bool = true, transaction: GRDBReadTransaction, block: @escaping (TSInteraction, UnsafeMutablePointer) -> Void) throws { + let hiddenInteractionFilterClause = "AND (\(interactionColumn: .hiddenUntilTimestamp) < \(Date().ows_millisecondsSince1970) OR \(interactionColumn: .hiddenUntilTimestamp) IS NULL)" let sql = """ SELECT * FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? + \(includeHidden ? "" : hiddenInteractionFilterClause) ORDER BY \(interactionColumn: .id) LIMIT \(range.length) OFFSET \(range.location) @@ -1069,11 +1121,14 @@ public class GRDBInteractionFinder: NSObject, InteractionFinderAdapter { } } - func interactionIds(inRange range: NSRange, transaction: GRDBReadTransaction) throws -> [String] { + func interactionIds(inRange range: NSRange, includingHiddenInteractions includeHidden: Bool = true, transaction: GRDBReadTransaction) throws -> [String] { + let hiddenInteractionFilterClause = "AND (\(interactionColumn: .hiddenUntilTimestamp) < \(Date().ows_millisecondsSince1970) OR \(interactionColumn: .hiddenUntilTimestamp) IS NULL)" + let sql = """ SELECT \(interactionColumn: .uniqueId) FROM \(InteractionRecord.databaseTableName) WHERE \(interactionColumn: .threadUniqueId) = ? + \(includeHidden ? "" : hiddenInteractionFilterClause) ORDER BY \(interactionColumn: .id) LIMIT \(range.length) OFFSET \(range.location) diff --git a/SignalServiceKit/src/Util/Date+SSK.swift b/SignalServiceKit/src/Util/Date+SSK.swift index 180d258d15..f49c5bc299 100644 --- a/SignalServiceKit/src/Util/Date+SSK.swift +++ b/SignalServiceKit/src/Util/Date+SSK.swift @@ -32,6 +32,14 @@ public extension NSDate { var ows_millisecondsSince1970: UInt64 { return NSDate.ows_millisecondsSince1970(for: self as Date) } + + static var distantFutureForMillisecondTimestamp: Date { + Date.distantFutureForMillisecondTimestamp + } + + static var distantFutureMillisecondTimestamp: UInt64 { + Date.distantFutureMillisecondTimestamp + } } public extension Date { @@ -63,6 +71,10 @@ public extension Date { return result } + static var distantFutureMillisecondTimestamp: UInt64 { + distantFutureForMillisecondTimestamp.ows_millisecondsSince1970 + } + func isBefore(_ date: Date) -> Bool { (self as NSDate).is(before: date) } diff --git a/SignalServiceKit/src/Util/FeatureFlags.swift b/SignalServiceKit/src/Util/FeatureFlags.swift index 473868cd00..d2541d20ca 100644 --- a/SignalServiceKit/src/Util/FeatureFlags.swift +++ b/SignalServiceKit/src/Util/FeatureFlags.swift @@ -501,9 +501,22 @@ public class DebugFlags: BaseFlags { @objc public static let delayedMessageResend = TestableFlag(false, - title: LocalizationNotNeeded("Delayed message resend"), + title: LocalizationNotNeeded("Sender Key: Delayed message resend"), details: LocalizationNotNeeded("Waits 10s before responding to a resend request.")) + @objc + public static let showFailedDecryptionPlaceholders = TestableFlag(false, + title: LocalizationNotNeeded("Sender Key: Show failed decryption placeholders"), + details: LocalizationNotNeeded("Shows placeholder interactions in the conversation list.")) + + @objc + public static let fastPlaceholderExpiration = TestableFlag(false, + title: LocalizationNotNeeded("Sender Key: Early placeholder expiration"), + details: LocalizationNotNeeded("Shortens the valid window for message resend+recovery."), + toggleHandler: { _ in + databaseStorage.read { messageDecrypter.schedulePlaceholderCleanup(transaction: $0)} + }) + public static func buildFlagMap() -> [String: Any] { BaseFlags.buildFlagMap(for: DebugFlags.self) { (key: String) -> Any? in DebugFlags.value(forKey: key) @@ -586,16 +599,19 @@ public class TestableFlag: NSObject { private let flag: AtomicBool public let title: String public let details: String + public let toggleHandler: ((Bool) -> Void)? fileprivate init(_ defaultValue: Bool, title: String, details: String, - affectsCapabilities: Bool = false) { + affectsCapabilities: Bool = false, + toggleHandler: ((Bool) -> Void)? = nil) { self.defaultValue = defaultValue self.title = title self.details = details self.affectsCapabilities = affectsCapabilities self.flag = AtomicBool(defaultValue) + self.toggleHandler = toggleHandler super.init()