From 5671fd2520e07f4da4164930aa87604ec6602684 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Wed, 3 Oct 2018 15:41:43 -0600 Subject: [PATCH] Revert "Revert 'new sort id'." This reverts commit ebae75af00679a15490474a7166607fb3faca76a. --- Signal.xcodeproj/project.pbxproj | 4 + .../Cells/OWSMessageHeaderView.m | 2 +- .../ConversationViewController.m | 43 +++--- .../DebugUI/DebugUIDiskUsage.m | 2 +- .../ViewControllers/DebugUI/DebugUIMessages.m | 34 ++++- .../DebugUI/DebugUIProfile.swift | 1 + .../MediaGalleryViewController.swift | 4 +- .../MessageDetailViewController.swift | 2 +- .../ViewControllers/NewGroupViewController.m | 1 + .../OWSConversationSettingsViewController.m | 1 + Signal/src/call/CallService.swift | 29 ++-- .../Models/OWSContactOffersInteraction.h | 11 ++ .../Models/OWSContactOffersInteraction.m | 8 ++ .../Models/TSUnreadIndicatorInteraction.h | 2 +- .../Models/TSUnreadIndicatorInteraction.m | 3 + SignalMessaging/SignalMessaging.h | 1 - .../ViewModels/OWSQuotedReplyModel.m | 1 + .../ViewModels/ThreadViewModel.swift | 6 +- .../migrations/OWS110SortIdMigration.swift | 82 +++++++++++ .../migrations/OWSDatabaseMigrationRunner.m | 1 + SignalMessaging/profiles/OWSProfileManager.m | 1 + .../utils/ConversationSearcher.swift | 35 +++-- SignalMessaging/utils/OWSUnreadIndicator.h | 15 +- SignalMessaging/utils/OWSUnreadIndicator.m | 15 +- SignalMessaging/utils/ThreadUtil.m | 30 ++-- SignalServiceKit/src/Contacts/TSThread.h | 34 ++--- SignalServiceKit/src/Contacts/TSThread.m | 76 ++++++---- .../src/Devices/OWSReceiptsForSenderMessage.m | 1 + .../src/Devices/OWSRecordTranscriptJob.m | 16 +-- .../DeviceSyncing/OWSOutgoingSyncMessage.m | 1 + .../OWSSyncGroupsRequestMessage.m | 1 + ...sappearingConfigurationUpdateInfoMessage.h | 1 + ...sappearingConfigurationUpdateInfoMessage.m | 2 +- ...DisappearingMessagesConfigurationMessage.h | 1 + ...DisappearingMessagesConfigurationMessage.m | 1 + .../Interactions/OWSDynamicOutgoingMessage.h | 1 + .../Interactions/OWSDynamicOutgoingMessage.m | 1 + .../Interactions/OWSEndSessionMessage.h | 1 + .../OWSVerificationStateChangeMessage.h | 2 +- .../OWSVerificationStateChangeMessage.m | 2 +- .../Messages/Interactions/TSErrorMessage.h | 4 - .../Messages/Interactions/TSErrorMessage.m | 13 +- .../TSErrorMessage_privateConstructor.h | 11 +- .../src/Messages/Interactions/TSInfoMessage.m | 2 + .../src/Messages/Interactions/TSInteraction.h | 10 +- .../src/Messages/Interactions/TSInteraction.m | 82 +++++++++-- .../src/Messages/Interactions/TSMessage.m | 22 +-- .../Messages/Interactions/TSOutgoingMessage.h | 1 + .../Messages/Interactions/TSOutgoingMessage.m | 2 + .../Messages/Interactions/TSQuotedMessage.m | 3 +- ...SInvalidIdentityKeyReceivingErrorMessage.h | 4 +- ...SInvalidIdentityKeyReceivingErrorMessage.m | 15 +- .../TSInvalidIdentityKeySendingErrorMessage.h | 6 +- .../TSInvalidIdentityKeySendingErrorMessage.m | 24 +--- .../Messages/OWSAddToContactsOfferMessage.h | 10 +- .../Messages/OWSAddToContactsOfferMessage.m | 11 +- .../OWSAddToProfileWhitelistOfferMessage.h | 8 +- .../OWSAddToProfileWhitelistOfferMessage.m | 9 +- .../src/Messages/OWSDisappearingMessagesJob.h | 26 +--- .../src/Messages/OWSDisappearingMessagesJob.m | 46 +++--- .../src/Messages/OWSIdentityManager.m | 4 + .../src/Messages/OWSMessageManager.m | 45 +++--- .../src/Messages/OWSMessageSender.m | 6 +- .../src/Messages/OWSOutgoingCallMessage.m | 3 +- .../src/Messages/OWSOutgoingNullMessage.m | 1 + .../src/Messages/OWSReadReceiptManager.h | 2 +- .../src/Messages/OWSReadReceiptManager.m | 42 +++--- .../src/Messages/OWSReadTracking.h | 2 +- .../OWSUnknownContactBlockOfferMessage.h | 10 +- .../OWSUnknownContactBlockOfferMessage.m | 25 +--- .../src/Storage/IncrementingIdFinder.swift | 27 ++++ .../src/Storage/OWSPrimaryStorage.m | 1 + SignalServiceKit/src/Storage/TSDatabaseView.h | 4 + SignalServiceKit/src/Storage/TSDatabaseView.m | 133 +++++++++++++----- .../src/Storage/TSYapDatabaseObject.m | 2 + 75 files changed, 692 insertions(+), 399 deletions(-) create mode 100644 SignalMessaging/environment/migrations/OWS110SortIdMigration.swift create mode 100644 SignalServiceKit/src/Storage/IncrementingIdFinder.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 0d5a7ce78..0375a76c9 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -469,6 +469,7 @@ 4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF4C0920F55BBA005DA313 /* MenuActionsViewController.swift */; }; 4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB5F26820F7D060004D1B42 /* MessageActions.swift */; }; 4CB93DC22180FF07004B9764 /* ProximityMonitoringManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB93DC12180FF07004B9764 /* ProximityMonitoringManager.swift */; }; + 4CBBCA6321714B4500EEB37D /* OWS110SortIdMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CBBCA6221714B4500EEB37D /* OWS110SortIdMigration.swift */; }; 4CC0B59C20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC0B59B20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift */; }; 4CC1ECF9211A47CE00CC13BE /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CC1ECF8211A47CD00CC13BE /* StoreKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 4CC1ECFB211A553000CC13BE /* AppUpdateNag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC1ECFA211A553000CC13BE /* AppUpdateNag.swift */; }; @@ -1190,6 +1191,7 @@ 4CA5F792211E1F06008C2708 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = ""; }; 4CB5F26820F7D060004D1B42 /* MessageActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageActions.swift; sourceTree = ""; }; 4CB93DC12180FF07004B9764 /* ProximityMonitoringManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProximityMonitoringManager.swift; sourceTree = ""; }; + 4CBBCA6221714B4500EEB37D /* OWS110SortIdMigration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWS110SortIdMigration.swift; sourceTree = ""; }; 4CC0B59B20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationConfigurationSyncOperation.swift; sourceTree = ""; }; 4CC1ECF8211A47CD00CC13BE /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; 4CC1ECFA211A553000CC13BE /* AppUpdateNag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateNag.swift; sourceTree = ""; }; @@ -1676,6 +1678,7 @@ 4598198D204E2F28009414F2 /* OWS108CallLoggingPreference.m */, 34D5872E208E2C4100D2255A /* OWS109OutgoingMessageState.h */, 34D5872D208E2C4100D2255A /* OWS109OutgoingMessageState.m */, + 4CBBCA6221714B4500EEB37D /* OWS110SortIdMigration.swift */, 349EA07B2162AEA700F7B17F /* OWS111UDAttributesMigration.swift */, 34B6A908218B8824007C4606 /* OWS112TypingIndicatorsMigration.swift */, 4C7537882193779700DF5E37 /* OWS113MultiAttachmentMediaMessages.swift */, @@ -3311,6 +3314,7 @@ 34BEDB1721C80BCA007B0EAE /* OWSAnyTouchGestureRecognizer.m in Sources */, 34B6A909218B8824007C4606 /* OWS112TypingIndicatorsMigration.swift in Sources */, 346129B51FD1F7E800532771 /* OWSProfileManager.m in Sources */, + 4CBBCA6321714B4500EEB37D /* OWS110SortIdMigration.swift in Sources */, 342950832124C9750000B063 /* OWSTextView.m in Sources */, 452EC6E1205FF5DC000E787C /* Bench.swift in Sources */, 342950882124CB0A0000B063 /* OWSSearchBar.m in Sources */, diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageHeaderView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageHeaderView.m index f2a37d0c3..884b6e664 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageHeaderView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageHeaderView.m @@ -126,7 +126,7 @@ const CGFloat OWSMessageHeaderViewDateHeaderVMargin = 23; { OWSAssertDebug(viewItem); - NSDate *date = viewItem.interaction.dateForSorting; + NSDate *date = viewItem.interaction.receivedAtDate; NSString *dateString = [DateUtil formatDateForConversationDateBreaks:date].localizedUppercaseString; // Update cell to reflect changes in dynamic text. diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 3284f28a3..38c6ec500 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -170,7 +170,7 @@ typedef enum : NSUInteger { @property (nonatomic) BOOL showLoadMoreHeader; @property (nonatomic) UILabel *loadMoreHeader; -@property (nonatomic) uint64_t lastVisibleTimestamp; +@property (nonatomic) uint64_t lastVisibleSortId; @property (nonatomic) BOOL isUserScrolling; @@ -692,7 +692,7 @@ typedef enum : NSUInteger { [self scrollToDefaultPosition]; } - [self updateLastVisibleTimestamp]; + [self updateLastVisibleSortId]; if (!self.viewHasEverAppeared) { NSTimeInterval appearenceDuration = CACurrentMediaTime() - self.viewControllerCreatedAt; @@ -1806,10 +1806,14 @@ typedef enum : NSUInteger { // DEPRECATED: we're no longer creating these incoming SN error's per message, // but there will be some legacy ones in the wild, behind which await // as-of-yet-undecrypted messages +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if ([errorMessage isKindOfClass:[TSInvalidIdentityKeyReceivingErrorMessage class]]) { // Deliberately crash if the user fails to explicitly accept the new identity // key. In practice we haven't been creating these messages in over a year. [errorMessage throws_acceptNewIdentityKey]; +#pragma clang diagnostic pop + } }]; [actionSheetController addAction:acceptSafetyNumberAction]; @@ -2430,7 +2434,7 @@ typedef enum : NSUInteger { id lastViewItem = [self.viewItems lastObject]; OWSAssertDebug(lastViewItem); - if (lastViewItem.interaction.timestampForSorting > self.lastVisibleTimestamp) { + if (lastViewItem.interaction.sortId > self.lastVisibleSortId) { shouldShowScrollDownButton = YES; } else if (isScrolledUp) { shouldShowScrollDownButton = YES; @@ -2529,7 +2533,6 @@ typedef enum : NSUInteger { OWSAssertIsOnMainThread(); OWSAssertDebug(message); - [self updateLastVisibleTimestamp:message.timestampForSorting]; self.lastMessageSentDate = [NSDate new]; [self.conversationViewModel clearUnreadMessagesIndicator]; self.inputToolbar.quotedReply = nil; @@ -3268,8 +3271,8 @@ typedef enum : NSUInteger { id _Nullable lastVisibleViewItem = [self.viewItems lastObject]; if (lastVisibleViewItem) { - uint64_t lastVisibleTimestamp = lastVisibleViewItem.interaction.timestampForSorting; - self.lastVisibleTimestamp = MAX(self.lastVisibleTimestamp, lastVisibleTimestamp); + uint64_t lastVisibleSortId = lastVisibleViewItem.interaction.sortId; + self.lastVisibleSortId = MAX(self.lastVisibleSortId, lastVisibleSortId); } self.scrollDownButton.hidden = YES; @@ -3277,12 +3280,12 @@ typedef enum : NSUInteger { self.hasUnreadMessages = NO; } -- (void)updateLastVisibleTimestamp +- (void)updateLastVisibleSortId { id _Nullable lastVisibleViewItem = [self lastVisibleViewItem]; if (lastVisibleViewItem) { - uint64_t lastVisibleTimestamp = lastVisibleViewItem.interaction.timestampForSorting; - self.lastVisibleTimestamp = MAX(self.lastVisibleTimestamp, lastVisibleTimestamp); + uint64_t lastVisibleSortId = lastVisibleViewItem.interaction.sortId; + self.lastVisibleSortId = MAX(self.lastVisibleSortId, lastVisibleSortId); } [self ensureScrollDownButton]; @@ -3295,15 +3298,6 @@ typedef enum : NSUInteger { self.hasUnreadMessages = numberOfUnreadMessages > 0; } -- (void)updateLastVisibleTimestamp:(uint64_t)timestamp -{ - OWSAssertDebug(timestamp > 0); - - self.lastVisibleTimestamp = MAX(self.lastVisibleTimestamp, timestamp); - - [self ensureScrollDownButton]; -} - - (void)markVisibleMessagesAsRead { if (self.presentedViewController) { @@ -3319,15 +3313,16 @@ typedef enum : NSUInteger { return; } - [self updateLastVisibleTimestamp]; + [self updateLastVisibleSortId]; - uint64_t lastVisibleTimestamp = self.lastVisibleTimestamp; + uint64_t lastVisibleSortId = self.lastVisibleSortId; - if (lastVisibleTimestamp == 0) { + if (lastVisibleSortId == 0) { // No visible messages yet. New Thread. return; } - [OWSReadReceiptManager.sharedManager markAsReadLocallyBeforeTimestamp:lastVisibleTimestamp thread:self.thread]; + + [OWSReadReceiptManager.sharedManager markAsReadLocallyBeforeSortId:self.lastVisibleSortId thread:self.thread]; } - (void)updateGroupModelTo:(TSGroupModel *)newGroupModel successCompletion:(void (^_Nullable)(void))successCompletion @@ -3763,7 +3758,7 @@ typedef enum : NSUInteger { - (void)scrollViewDidScroll:(UIScrollView *)scrollView { - [self updateLastVisibleTimestamp]; + [self updateLastVisibleSortId]; __weak ConversationViewController *weakSelf = self; dispatch_async(dispatch_get_main_queue(), ^{ @@ -4192,7 +4187,7 @@ typedef enum : NSUInteger { { OWSAssertIsOnMainThread(); - [self updateLastVisibleTimestamp]; + [self updateLastVisibleSortId]; self.conversationStyle.viewWidth = self.collectionView.width; [self.collectionView.collectionViewLayout invalidateLayout]; } diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIDiskUsage.m b/Signal/src/ViewControllers/DebugUI/DebugUIDiskUsage.m index b1a4ff97a..05f8dd5f4 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIDiskUsage.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIDiskUsage.m @@ -94,7 +94,7 @@ NS_ASSUME_NONNULL_BEGIN NSUInteger index, BOOL *stop) { NSTimeInterval ageSeconds - = fabs(interaction.dateForSorting.timeIntervalSinceNow); + = fabs(interaction.receivedAtDate.timeIntervalSinceNow); if (ageSeconds < maxAgeSeconds) { *stop = YES; return; diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index cdbc69fb5..7322d68d0 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -3452,6 +3452,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId enabled:YES durationSeconds:(uint32_t)[durationSeconds intValue]]; + // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread @@ -3466,6 +3467,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId enabled:YES durationSeconds:(uint32_t)[durationSeconds intValue]]; + // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread @@ -3480,6 +3482,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId enabled:YES durationSeconds:(uint32_t)[durationSeconds intValue]]; + // MJK TODO - remove senderTimestamp [result addObject:[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread @@ -3492,6 +3495,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId enabled:NO durationSeconds:0]; + // MJK TODO - remove senderTimestamp [result addObject:[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread @@ -3502,44 +3506,55 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [result addObject:[TSInfoMessage userNotRegisteredMessageInThread:thread recipientId:@"+19174054215"]]; + // MJK - should be safe to remove this senderTimestamp [result addObject:[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageType:TSInfoMessageTypeSessionDidEnd]]; // TODO: customMessage? + // MJK - should be safe to remove this senderTimestamp [result addObject:[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageType:TSInfoMessageTypeGroupUpdate]]; // TODO: customMessage? + // MJK - should be safe to remove this senderTimestamp [result addObject:[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageType:TSInfoMessageTypeGroupQuit]]; + // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread recipientId:@"+19174054215" verificationState:OWSVerificationStateDefault isLocalChange:YES]]; + + // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread recipientId:@"+19174054215" verificationState:OWSVerificationStateVerified isLocalChange:YES]]; + // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread recipientId:@"+19174054215" verificationState:OWSVerificationStateNoLongerVerified isLocalChange:YES]]; + + // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread recipientId:@"+19174054215" verificationState:OWSVerificationStateDefault isLocalChange:NO]]; + // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread recipientId:@"+19174054215" verificationState:OWSVerificationStateVerified isLocalChange:NO]]; + // MJK - should be safe to remove this senderTimestamp [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread @@ -3555,13 +3570,17 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac withTransaction:transaction]]; [result addObject:[TSErrorMessage corruptedMessageWithEnvelope:[self createEnvelopeForThread:thread] withTransaction:transaction]]; - - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" TSInvalidIdentityKeyReceivingErrorMessage *_Nullable blockingSNChangeMessage = [TSInvalidIdentityKeyReceivingErrorMessage untrustedKeyWithEnvelope:[self createEnvelopeForThread:thread] withTransaction:transaction]; +#pragma clang diagnostic pop + OWSAssertDebug(blockingSNChangeMessage); [result addObject:blockingSNChangeMessage]; + // MJK TODO - should be safe to remove this senderTimestamp [result addObject:[[TSErrorMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread failedMessageType:TSErrorMessageNonBlockingIdentityChange @@ -3724,9 +3743,10 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac OWSLogInfo(@"sendFakeMessages: %lu", (unsigned long)counter); for (NSUInteger i = 0; i < counter; i++) { - NSString *randomText = [self randomText]; + NSString *randomText = [[self randomText] stringByAppendingFormat:@" (sequence: %lu)", (unsigned long)i + 1]; switch (arc4random_uniform(isTextOnly ? 2 : 4)) { case 0: { + // MJK - should be safe to remove this senderTimestamp TSIncomingMessage *message = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread @@ -3768,6 +3788,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac attachmentType:TSAttachmentTypeDefault]; pointer.state = TSAttachmentPointerStateFailed; [pointer saveWithTransaction:transaction]; + // MJK - should be safe to remove this senderTimestamp TSIncomingMessage *message = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread @@ -4165,6 +4186,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac OWSDisappearingMessagesConfiguration *configuration = [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId transaction:initialTransaction]; + // MJK TODO - remove senderTimestamp TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread @@ -4232,6 +4254,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac for (NSNumber *timestamp in timestamps) { NSString *randomText = [self randomText]; { + // Legit usage of SenderTimestamp to backdate incoming sent messages for Debug TSIncomingMessage *message = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:timestamp.unsignedLongLongValue inThread:thread @@ -4247,6 +4270,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [message markAsReadNowWithSendReadReceipt:NO transaction:transaction]; } { + // MJK TODO - this might be the one place we actually use senderTimestamp TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:timestamp.unsignedLongLongValue inThread:thread @@ -4273,6 +4297,8 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac + (void)createDisappearingMessagesWhichFailedToStartInThread:(TSThread *)thread { uint64_t now = [NSDate ows_millisecondTimeStamp]; + + // MJK TODO - should be safe to remove this senderTimestamp TSIncomingMessage *message = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:now inThread:thread @@ -4503,6 +4529,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac [attachmentIds addObject:attachmentId]; } + // MJK TODO - remove senderTimestamp TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread @@ -4592,6 +4619,7 @@ typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transac // uint64_t millisAgo = (uint64_t)(((double)arc4random() / ((double)0xffffffff)) * yearsMillis); // uint64_t timestamp = [NSDate ows_millisecondTimeStamp] - millisAgo; + // MJK TODO - should be safe to remove this senderTimestamp TSIncomingMessage *message = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIProfile.swift b/Signal/src/ViewControllers/DebugUI/DebugUIProfile.swift index 5377de584..126d54d0c 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIProfile.swift +++ b/Signal/src/ViewControllers/DebugUI/DebugUIProfile.swift @@ -46,6 +46,7 @@ class DebugUIProfile: DebugUIPage { OWSTableItem(title: "Send Profile Key Message") { [weak self] in guard let strongSelf = self else { return } + // MJK TODO - should be safe to remove this senderTimestamp let message = OWSProfileKeyMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: aThread) strongSelf.messageSender.sendPromise(message: message).done { Logger.info("Successfully sent profile key message to thread: \(String(describing: aThread))") diff --git a/Signal/src/ViewControllers/MediaGalleryViewController.swift b/Signal/src/ViewControllers/MediaGalleryViewController.swift index 3f4876b54..ddaa0b42e 100644 --- a/Signal/src/ViewControllers/MediaGalleryViewController.swift +++ b/Signal/src/ViewControllers/MediaGalleryViewController.swift @@ -55,7 +55,7 @@ public class MediaGalleryItem: Equatable, Hashable { self.captionForDisplay = attachmentStream.caption?.filterForDisplay self.galleryDate = GalleryDate(message: message) self.albumIndex = message.attachmentIds.index(of: attachmentStream.uniqueId!) - self.orderingKey = MediaGalleryItemOrderingKey(messageSortKey: message.timestampForSorting(), attachmentSortKey: albumIndex) + self.orderingKey = MediaGalleryItemOrderingKey(messageSortKey: message.sortId, attachmentSortKey: albumIndex) } var isVideo: Bool { @@ -120,7 +120,7 @@ public struct GalleryDate: Hashable, Comparable, Equatable { let month: Int init(message: TSMessage) { - let date = message.dateForSorting() + let date = message.receivedAtDate() self.year = Calendar.current.component(.year, from: date) self.month = Calendar.current.component(.month, from: date) diff --git a/Signal/src/ViewControllers/MessageDetailViewController.swift b/Signal/src/ViewControllers/MessageDetailViewController.swift index 5073ffd5d..107614773 100644 --- a/Signal/src/ViewControllers/MessageDetailViewController.swift +++ b/Signal/src/ViewControllers/MessageDetailViewController.swift @@ -324,7 +324,7 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele if message is TSIncomingMessage { rows.append(valueRow(name: NSLocalizedString("MESSAGE_METADATA_VIEW_RECEIVED_DATE_TIME", comment: "Label for the 'received date & time' field of the 'message metadata' view."), - value: DateUtil.formatPastTimestampRelativeToNow(message.timestampForSorting()))) + value: DateUtil.formatPastTimestampRelativeToNow(message.receivedAtTimestamp))) } rows += addAttachmentMetadataRows() diff --git a/Signal/src/ViewControllers/NewGroupViewController.m b/Signal/src/ViewControllers/NewGroupViewController.m index b3823f532..fcbacb08a 100644 --- a/Signal/src/ViewControllers/NewGroupViewController.m +++ b/Signal/src/ViewControllers/NewGroupViewController.m @@ -462,6 +462,7 @@ NS_ASSUME_NONNULL_BEGIN // Add an error message to the new group indicating // that group creation didn't succeed. + // MJK TODO should be safe to remove senderTimestamp and just save immediately TSErrorMessage *errorMessage = [[TSErrorMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread failedMessageType:TSErrorMessageGroupCreationFailed]; diff --git a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m index e144c74a8..c14772998 100644 --- a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m +++ b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m @@ -938,6 +938,7 @@ const CGFloat kIconViewLength = 24; if (self.disappearingMessagesConfiguration.dictionaryValueDidChange) { [self.editingDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { [self.disappearingMessagesConfiguration saveWithTransaction:transaction]; + // MJK TODO - should be safe to remove this senderTimestamp OWSDisappearingConfigurationUpdateInfoMessage *infoMessage = [[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] diff --git a/Signal/src/call/CallService.swift b/Signal/src/call/CallService.swift index ef7dda9c2..1d54f7274 100644 --- a/Signal/src/call/CallService.swift +++ b/Signal/src/call/CallService.swift @@ -303,29 +303,25 @@ private class SignalCallData: NSObject { deinit { NotificationCenter.default.removeObserver(self) } - + // MARK: - Dependencies - - private var contactsManager : OWSContactsManager - { + + private var contactsManager: OWSContactsManager { return Environment.shared.contactsManager } - - private var messageSender : MessageSender - { + + private var messageSender: MessageSender { return SSKEnvironment.shared.messageSender } - - private var accountManager : AccountManager - { + + private var accountManager: AccountManager { return AppEnvironment.shared.accountManager } - - private var notificationsAdapter : CallNotificationsAdapter - { + + private var notificationsAdapter: CallNotificationsAdapter { return AppEnvironment.shared.callNotificationsAdapter } - + // MARK: - Notifications @objc func didEnterBackground() { @@ -370,6 +366,7 @@ private class SignalCallData: NSObject { let callData = SignalCallData(call: call) self.callData = callData + // MJK TODO remove this timestamp param let callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(), withCallNumber: call.remotePhoneNumber, callType: RPRecentCallTypeOutgoingIncomplete, in: call.thread) callRecord.save() call.callRecord = callRecord @@ -531,6 +528,7 @@ private class SignalCallData: NSObject { callRecord.updateCallType(RPRecentCallTypeIncomingMissed) } } else { + // MJK TODO remove this timestamp param call.callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(), withCallNumber: call.thread.contactIdentifier(), callType: RPRecentCallTypeIncomingMissed, @@ -617,6 +615,7 @@ private class SignalCallData: NSObject { self.notificationsAdapter.presentMissedCallBecauseOfNoLongerVerifiedIdentity(call: newCall, callerName: callerName) } + // MJK TODO remove this timestamp param let callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(), withCallNumber: thread.contactIdentifier(), callType: RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity, @@ -1038,6 +1037,7 @@ private class SignalCallData: NSObject { Logger.info("\(call.identifiersForLogs).") + // MJK TODO remove this timestamp param let callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(), withCallNumber: call.remotePhoneNumber, callType: RPRecentCallTypeIncomingIncomplete, in: call.thread) callRecord.save() call.callRecord = callRecord @@ -1127,6 +1127,7 @@ private class SignalCallData: NSObject { owsFailDebug("Not expecting callrecord to already be set") callRecord.updateCallType(RPRecentCallTypeIncomingDeclined) } else { + // MJK TODO remove this timestamp param let callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(), withCallNumber: call.remotePhoneNumber, callType: RPRecentCallTypeIncomingDeclined, in: call.thread) callRecord.save() call.callRecord = callRecord diff --git a/SignalMessaging/Models/OWSContactOffersInteraction.h b/SignalMessaging/Models/OWSContactOffersInteraction.h index f0bf70509..9ed7d704b 100644 --- a/SignalMessaging/Models/OWSContactOffersInteraction.h +++ b/SignalMessaging/Models/OWSContactOffersInteraction.h @@ -4,6 +4,8 @@ #import +@class YapDatabaseReadWriteTransaction; + NS_ASSUME_NONNULL_BEGIN @interface OWSContactOffersInteraction : TSInteraction @@ -11,6 +13,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) BOOL hasBlockOffer; @property (nonatomic, readonly) BOOL hasAddToContactsOffer; @property (nonatomic, readonly) BOOL hasAddToProfileWhitelistOffer; + +// TODO - remove this recipientId param +// it's redundant with the interaction's TSContactThread @property (nonatomic, readonly) NSString *recipientId; @property (nonatomic, readonly) NSString *beforeInteractionId; @@ -18,6 +23,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; +// MJK TODO should be safe to remove this timestamp param - (instancetype)initInteractionWithUniqueId:(NSString *)uniqueId timestamp:(uint64_t)timestamp thread:(TSThread *)thread @@ -27,6 +33,11 @@ NS_ASSUME_NONNULL_BEGIN recipientId:(NSString *)recipientId beforeInteractionId:(NSString *)beforeInteractionId NS_DESIGNATED_INITIALIZER; +- (void)updateHasBlockOffer:(BOOL)hasBlockOffer + hasAddToContactsOffer:(BOOL)hasAddToContactsOffer + hasAddToProfileWhitelistOffer:(BOOL)hasAddToProfileWhitelistOffer + transaction:(YapDatabaseReadWriteTransaction *)transaction; + @end NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/Models/OWSContactOffersInteraction.m b/SignalMessaging/Models/OWSContactOffersInteraction.m index 7d3ca110d..f9bcbd150 100644 --- a/SignalMessaging/Models/OWSContactOffersInteraction.m +++ b/SignalMessaging/Models/OWSContactOffersInteraction.m @@ -6,6 +6,14 @@ NS_ASSUME_NONNULL_BEGIN +@interface OWSContactOffersInteraction () + +@property (nonatomic) BOOL hasBlockOffer; +@property (nonatomic) BOOL hasAddToContactsOffer; +@property (nonatomic) BOOL hasAddToProfileWhitelistOffer; + +@end + @implementation OWSContactOffersInteraction - (instancetype)initWithCoder:(NSCoder *)coder diff --git a/SignalMessaging/Models/TSUnreadIndicatorInteraction.h b/SignalMessaging/Models/TSUnreadIndicatorInteraction.h index e1972b783..f4134e4bb 100644 --- a/SignalMessaging/Models/TSUnreadIndicatorInteraction.h +++ b/SignalMessaging/Models/TSUnreadIndicatorInteraction.h @@ -7,7 +7,7 @@ NS_ASSUME_NONNULL_BEGIN // This class is vestigial. -@interface TSUnreadIndicatorInteraction : TSInteraction +__attribute__((deprecated)) @interface TSUnreadIndicatorInteraction : TSInteraction - (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; diff --git a/SignalMessaging/Models/TSUnreadIndicatorInteraction.m b/SignalMessaging/Models/TSUnreadIndicatorInteraction.m index afd976ef0..e9d96b4c9 100644 --- a/SignalMessaging/Models/TSUnreadIndicatorInteraction.m +++ b/SignalMessaging/Models/TSUnreadIndicatorInteraction.m @@ -6,7 +6,10 @@ NS_ASSUME_NONNULL_BEGIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation TSUnreadIndicatorInteraction +#pragma clang diagnostic pop - (instancetype)initWithCoder:(NSCoder *)coder { diff --git a/SignalMessaging/SignalMessaging.h b/SignalMessaging/SignalMessaging.h index 52818fac6..a98fc65c7 100644 --- a/SignalMessaging/SignalMessaging.h +++ b/SignalMessaging/SignalMessaging.h @@ -51,7 +51,6 @@ FOUNDATION_EXPORT const unsigned char SignalMessagingVersionString[]; #import #import #import -#import #import #import #import diff --git a/SignalMessaging/ViewModels/OWSQuotedReplyModel.m b/SignalMessaging/ViewModels/OWSQuotedReplyModel.m index ec31ccb10..b2436a2f2 100644 --- a/SignalMessaging/ViewModels/OWSQuotedReplyModel.m +++ b/SignalMessaging/ViewModels/OWSQuotedReplyModel.m @@ -241,6 +241,7 @@ NS_ASSUME_NONNULL_BEGIN { NSArray *attachments = self.attachmentStream ? @[ self.attachmentStream ] : @[]; + // Legit usage of senderTimestamp to reference existing message return [[TSQuotedMessage alloc] initWithTimestamp:self.timestamp authorId:self.authorId body:self.body diff --git a/SignalMessaging/ViewModels/ThreadViewModel.swift b/SignalMessaging/ViewModels/ThreadViewModel.swift index 36cbcd0df..16f8fa16a 100644 --- a/SignalMessaging/ViewModels/ThreadViewModel.swift +++ b/SignalMessaging/ViewModels/ThreadViewModel.swift @@ -25,12 +25,14 @@ public class ThreadViewModel: NSObject { @objc public init(thread: TSThread, transaction: YapDatabaseReadTransaction) { self.threadRecord = thread - self.lastMessageDate = thread.lastMessageDate() + self.isGroupThread = thread.isGroupThread() self.name = thread.name() self.isMuted = thread.isMuted self.lastMessageText = thread.lastMessageText(transaction: transaction) - self.lastMessageForInbox = thread.lastInteractionForInbox(transaction: transaction) + let lastInteraction = thread.lastInteractionForInbox(transaction: transaction) + self.lastMessageForInbox = lastInteraction + self.lastMessageDate = lastInteraction?.receivedAtDate() ?? thread.creationDate if let contactThread = thread as? TSContactThread { self.contactIdentifier = contactThread.contactIdentifier() diff --git a/SignalMessaging/environment/migrations/OWS110SortIdMigration.swift b/SignalMessaging/environment/migrations/OWS110SortIdMigration.swift new file mode 100644 index 000000000..ef35b5665 --- /dev/null +++ b/SignalMessaging/environment/migrations/OWS110SortIdMigration.swift @@ -0,0 +1,82 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +class OWS110SortIdMigration: OWSDatabaseMigration { + // increment a similar constant for each migration. + @objc + class func migrationId() -> String { + return "110" + } + + override public func runUp(completion: @escaping OWSDatabaseMigrationCompletion) { + Logger.debug("") + BenchAsync(title: "Sort Migration") { completeBenchmark in + self.doMigration { + completeBenchmark() + completion() + } + } + } + + private func doMigration(completion: @escaping OWSDatabaseMigrationCompletion) { + // TODO batch this? + self.dbReadWriteConnection().readWrite { transaction in + + var archivedThreads: [TSThread] = [] + + // get archived threads before migration + TSThread.enumerateCollectionObjects(with: transaction) { (object, _) in + guard let thread = object as? TSThread else { + owsFailDebug("unexpected object: \(type(of: object))") + return + } + + if thread.isArchivedByLegacyTimestampForSorting { + archivedThreads.append(thread) + } + } + + guard let legacySorting: YapDatabaseAutoViewTransaction = transaction.extension(TSMessageDatabaseViewExtensionName_Legacy) as? YapDatabaseAutoViewTransaction else { + owsFailDebug("legacySorting was unexpectedly nil") + return + } + + let totalCount: UInt = legacySorting.numberOfItemsInAllGroups() + var completedCount: UInt = 0 + legacySorting.enumerateGroups { group, _ in + autoreleasepool { + legacySorting.enumerateKeysAndObjects(inGroup: group) { (_, _, object, _, _) in + autoreleasepool { + guard let interaction = object as? TSInteraction else { + owsFailDebug("unexpected object: \(type(of: object))") + return + } + + interaction.saveNextSortId(transaction: transaction) + + completedCount += 1 + + if completedCount % 100 == 0 { + // Legit usage of legacy sorting for migration to new sorting + Logger.info("thread: \(interaction.uniqueThreadId), timestampForLegacySorting:\(interaction.timestampForLegacySorting()), sortId: \(interaction.sortId) totalCount: \(totalCount), completedcount: \(completedCount)") + } + } + } + } + } + + Logger.info("re-archiving \(archivedThreads.count) threads which were previously archived") + for archivedThread in archivedThreads { + archivedThread.archiveThread(with: transaction) + } + + self.save(with: transaction) + } + + completion() + } + +} diff --git a/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m b/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m index 479532818..a19510996 100644 --- a/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m +++ b/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m @@ -32,6 +32,7 @@ NS_ASSUME_NONNULL_BEGIN [[OWS107LegacySounds alloc] init], [[OWS108CallLoggingPreference alloc] init], [[OWS109OutgoingMessageState alloc] init], + [OWS110SortIdMigration new], [[OWS111UDAttributesMigration alloc] init], [[OWS112TypingIndicatorsMigration alloc] init], [[OWS113MultiAttachmentMediaMessages alloc] init], diff --git a/SignalMessaging/profiles/OWSProfileManager.m b/SignalMessaging/profiles/OWSProfileManager.m index 445efd0db..78d1805f8 100644 --- a/SignalMessaging/profiles/OWSProfileManager.m +++ b/SignalMessaging/profiles/OWSProfileManager.m @@ -1494,6 +1494,7 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error); return; } + // MJK TODO - should be safe to remove this senderTimestamp OWSProfileKeyMessage *message = [[OWSProfileKeyMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread]; [OWSProfileManager.sharedManager addThreadToProfileWhitelist:thread]; diff --git a/SignalMessaging/utils/ConversationSearcher.swift b/SignalMessaging/utils/ConversationSearcher.swift index 8669715c9..8f7d5a9d3 100644 --- a/SignalMessaging/utils/ConversationSearcher.swift +++ b/SignalMessaging/utils/ConversationSearcher.swift @@ -5,7 +5,21 @@ import Foundation import SignalServiceKit -public class ConversationSearchResult: Comparable { +public typealias MessageSortKey = UInt64 +public struct ConversationSortKey: Comparable { + let creationDate: Date + let lastMessageReceivedAtDate: Date? + + // MARK: Comparable + + public static func < (lhs: ConversationSortKey, rhs: ConversationSortKey) -> Bool { + let lhsDate = lhs.lastMessageReceivedAtDate ?? lhs.creationDate + let rhsDate = rhs.lastMessageReceivedAtDate ?? rhs.creationDate + return lhsDate < rhsDate + } +} + +public class ConversationSearchResult: Comparable where SortKey: Comparable { public let thread: ThreadViewModel public let messageId: String? @@ -13,9 +27,9 @@ public class ConversationSearchResult: Comparable { public let snippet: String? - private let sortKey: UInt64 + private let sortKey: SortKey - init(thread: ThreadViewModel, sortKey: UInt64, messageId: String? = nil, messageDate: Date? = nil, snippet: String? = nil) { + init(thread: ThreadViewModel, sortKey: SortKey, messageId: String? = nil, messageDate: Date? = nil, snippet: String? = nil) { self.thread = thread self.sortKey = sortKey self.messageId = messageId @@ -65,11 +79,11 @@ public class ContactSearchResult: Comparable { public class SearchResultSet { public let searchText: String - public let conversations: [ConversationSearchResult] + public let conversations: [ConversationSearchResult] public let contacts: [ContactSearchResult] - public let messages: [ConversationSearchResult] + public let messages: [ConversationSearchResult] - public init(searchText: String, conversations: [ConversationSearchResult], contacts: [ContactSearchResult], messages: [ConversationSearchResult]) { + public init(searchText: String, conversations: [ConversationSearchResult], contacts: [ContactSearchResult], messages: [ConversationSearchResult]) { self.searchText = searchText self.conversations = conversations self.contacts = contacts @@ -101,9 +115,9 @@ public class ConversationSearcher: NSObject { transaction: YapDatabaseReadTransaction, contactsManager: ContactsManagerProtocol) -> SearchResultSet { - var conversations: [ConversationSearchResult] = [] + var conversations: [ConversationSearchResult] = [] var contacts: [ContactSearchResult] = [] - var messages: [ConversationSearchResult] = [] + var messages: [ConversationSearchResult] = [] var existingConversationRecipientIds: Set = Set() @@ -111,7 +125,8 @@ public class ConversationSearcher: NSObject { if let thread = match as? TSThread { let threadViewModel = ThreadViewModel(thread: thread, transaction: transaction) - let sortKey = NSDate.ows_millisecondsSince1970(for: threadViewModel.lastMessageDate) + let sortKey = ConversationSortKey(creationDate: thread.creationDate, + lastMessageReceivedAtDate: thread.lastInteractionForInbox(transaction: transaction)?.receivedAtDate()) let searchResult = ConversationSearchResult(thread: threadViewModel, sortKey: sortKey) if let contactThread = thread as? TSContactThread { @@ -124,7 +139,7 @@ public class ConversationSearcher: NSObject { let thread = message.thread(with: transaction) let threadViewModel = ThreadViewModel(thread: thread, transaction: transaction) - let sortKey = message.timestamp + let sortKey = message.sortId let searchResult = ConversationSearchResult(thread: threadViewModel, sortKey: sortKey, messageId: message.uniqueId, diff --git a/SignalMessaging/utils/OWSUnreadIndicator.h b/SignalMessaging/utils/OWSUnreadIndicator.h index b2d5ba9c0..ee6f7b37e 100644 --- a/SignalMessaging/utils/OWSUnreadIndicator.h +++ b/SignalMessaging/utils/OWSUnreadIndicator.h @@ -6,13 +6,11 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSUnreadIndicator : NSObject -@property (nonatomic, readonly) uint64_t timestamp; - @property (nonatomic, readonly) BOOL hasMoreUnseenMessages; @property (nonatomic, readonly) NSUInteger missingUnseenSafetyNumberChangeCount; -// The timestamp of the oldest unseen message. +// The sortId of the oldest unseen message. // // Once we enter messages view, we mark all messages read, so we need // a snapshot of what the first unread message was when we entered the @@ -20,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN // repeatedly. The unread indicator should continue to show up until // it has been cleared, at which point hideUnreadMessagesIndicator is // YES in ensureDynamicInteractionsForThread:... -@property (nonatomic, readonly) uint64_t firstUnseenInteractionTimestamp; +@property (nonatomic, readonly) uint64_t firstUnseenSortId; // The index of the unseen indicator, counting from the _end_ of the conversation // history. @@ -32,11 +30,10 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; -- (instancetype)initUnreadIndicatorWithTimestamp:(uint64_t)timestamp - hasMoreUnseenMessages:(BOOL)hasMoreUnseenMessages - missingUnseenSafetyNumberChangeCount:(NSUInteger)missingUnseenSafetyNumberChangeCount - unreadIndicatorPosition:(NSInteger)unreadIndicatorPosition - firstUnseenInteractionTimestamp:(uint64_t)firstUnseenInteractionTimestamp NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithFirstUnseenSortId:(uint64_t)firstUnseenSortId + hasMoreUnseenMessages:(BOOL)hasMoreUnseenMessages + missingUnseenSafetyNumberChangeCount:(NSUInteger)missingUnseenSafetyNumberChangeCount + unreadIndicatorPosition:(NSInteger)unreadIndicatorPosition NS_DESIGNATED_INITIALIZER; @end diff --git a/SignalMessaging/utils/OWSUnreadIndicator.m b/SignalMessaging/utils/OWSUnreadIndicator.m index a62190245..3cc384df3 100644 --- a/SignalMessaging/utils/OWSUnreadIndicator.m +++ b/SignalMessaging/utils/OWSUnreadIndicator.m @@ -8,11 +8,10 @@ NS_ASSUME_NONNULL_BEGIN @implementation OWSUnreadIndicator -- (instancetype)initUnreadIndicatorWithTimestamp:(uint64_t)timestamp - hasMoreUnseenMessages:(BOOL)hasMoreUnseenMessages - missingUnseenSafetyNumberChangeCount:(NSUInteger)missingUnseenSafetyNumberChangeCount - unreadIndicatorPosition:(NSInteger)unreadIndicatorPosition - firstUnseenInteractionTimestamp:(uint64_t)firstUnseenInteractionTimestamp +- (instancetype)initWithFirstUnseenSortId:(uint64_t)firstUnseenSortId + hasMoreUnseenMessages:(BOOL)hasMoreUnseenMessages + missingUnseenSafetyNumberChangeCount:(NSUInteger)missingUnseenSafetyNumberChangeCount + unreadIndicatorPosition:(NSInteger)unreadIndicatorPosition { self = [super init]; @@ -20,11 +19,10 @@ NS_ASSUME_NONNULL_BEGIN return self; } - _timestamp = timestamp; + _firstUnseenSortId = firstUnseenSortId; _hasMoreUnseenMessages = hasMoreUnseenMessages; _missingUnseenSafetyNumberChangeCount = missingUnseenSafetyNumberChangeCount; _unreadIndicatorPosition = unreadIndicatorPosition; - _firstUnseenInteractionTimestamp = firstUnseenInteractionTimestamp; return self; } @@ -40,7 +38,8 @@ NS_ASSUME_NONNULL_BEGIN } OWSUnreadIndicator *other = object; - return (self.timestamp == other.timestamp && self.hasMoreUnseenMessages == other.hasMoreUnseenMessages + return (self.firstUnseenSortId == other.firstUnseenSortId + && self.hasMoreUnseenMessages == other.hasMoreUnseenMessages && self.missingUnseenSafetyNumberChangeCount == other.missingUnseenSafetyNumberChangeCount && self.unreadIndicatorPosition == other.unreadIndicatorPosition); } diff --git a/SignalMessaging/utils/ThreadUtil.m b/SignalMessaging/utils/ThreadUtil.m index 622c4a46d..8370a5fc4 100644 --- a/SignalMessaging/utils/ThreadUtil.m +++ b/SignalMessaging/utils/ThreadUtil.m @@ -235,6 +235,7 @@ NS_ASSUME_NONNULL_BEGIN uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0); BOOL isVoiceMessage = (attachments.count == 1 && attachments.firstObject.isVoiceMessage); + // MJK TODO - remove senderTimestamp TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread @@ -291,6 +292,7 @@ NS_ASSUME_NONNULL_BEGIN [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId]; uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0); + // MJK TODO - remove senderTimestamp TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread @@ -372,14 +374,14 @@ NS_ASSUME_NONNULL_BEGIN // have been marked as read. // // IFF this variable is non-null, there are unseen messages in the thread. - NSNumber *_Nullable firstUnseenInteractionTimestamp = nil; + NSNumber *_Nullable firstUnseenSortId = nil; if (lastUnreadIndicator) { - firstUnseenInteractionTimestamp = @(lastUnreadIndicator.firstUnseenInteractionTimestamp); + firstUnseenSortId = @(lastUnreadIndicator.firstUnseenSortId); } else { TSInteraction *_Nullable firstUnseenInteraction = [[TSDatabaseView unseenDatabaseViewExtension:transaction] firstObjectInGroup:thread.uniqueId]; if (firstUnseenInteraction) { - firstUnseenInteractionTimestamp = @(firstUnseenInteraction.timestampForSorting); + firstUnseenSortId = @(firstUnseenInteraction.sortId); } } @@ -421,7 +423,7 @@ NS_ASSUME_NONNULL_BEGIN if (hideUnreadMessagesIndicator) { return; } - if (!firstUnseenInteractionTimestamp) { + if (!firstUnseenSortId) { // If there are no unseen interactions, don't show an unread indicator. return; } @@ -489,13 +491,12 @@ NS_ASSUME_NONNULL_BEGIN if (hasMoreUnseenMessages) { NSMutableSet *missingUnseenSafetyNumberChanges = [NSMutableSet set]; for (TSInvalidIdentityKeyErrorMessage *safetyNumberChange in blockingSafetyNumberChanges) { - BOOL isUnseen - = safetyNumberChange.timestampForSorting >= firstUnseenInteractionTimestamp.unsignedLongLongValue; + BOOL isUnseen = safetyNumberChange.sortId >= firstUnseenSortId.unsignedLongLongValue; if (!isUnseen) { continue; } - BOOL isMissing - = safetyNumberChange.timestampForSorting < interactionAfterUnreadIndicator.timestampForSorting; + + BOOL isMissing = safetyNumberChange.sortId < interactionAfterUnreadIndicator.sortId; if (!isMissing) { continue; } @@ -521,13 +522,12 @@ NS_ASSUME_NONNULL_BEGIN NSInteger unreadIndicatorPosition = visibleUnseenMessageCount; - dynamicInteractions.unreadIndicator = [[OWSUnreadIndicator alloc] - initUnreadIndicatorWithTimestamp:interactionAfterUnreadIndicator.timestampForSorting - hasMoreUnseenMessages:hasMoreUnseenMessages - missingUnseenSafetyNumberChangeCount:missingUnseenSafetyNumberChangeCount - unreadIndicatorPosition:unreadIndicatorPosition - firstUnseenInteractionTimestamp:firstUnseenInteractionTimestamp.unsignedLongLongValue]; - OWSLogInfo(@"Creating Unread Indicator: %llu", dynamicInteractions.unreadIndicator.timestamp); + dynamicInteractions.unreadIndicator = + [[OWSUnreadIndicator alloc] initWithFirstUnseenSortId:firstUnseenSortId.unsignedLongLongValue + hasMoreUnseenMessages:hasMoreUnseenMessages + missingUnseenSafetyNumberChangeCount:missingUnseenSafetyNumberChangeCount + unreadIndicatorPosition:unreadIndicatorPosition]; + OWSLogInfo(@"Creating Unread Indicator: %llu", dynamicInteractions.unreadIndicator.firstUnseenSortId); } + (nullable NSNumber *)focusMessagePositionForThread:(TSThread *)thread diff --git a/SignalServiceKit/src/Contacts/TSThread.h b/SignalServiceKit/src/Contacts/TSThread.h index 1b0228c0f..0b2b3dd32 100644 --- a/SignalServiceKit/src/Contacts/TSThread.h +++ b/SignalServiceKit/src/Contacts/TSThread.h @@ -32,8 +32,9 @@ extern ConversationColorName const kConversationColorName_Default; */ @interface TSThread : TSYapDatabaseObject -// YES IFF this thread has ever had a message. @property (nonatomic) BOOL shouldThreadBeVisible; +@property (nonatomic, readonly) NSDate *creationDate; +@property (nonatomic, readonly) BOOL isArchivedByLegacyTimestampForSorting; /** * Whether the object is a group thread or not. @@ -77,7 +78,10 @@ extern ConversationColorName const kConversationColorName_Default; /** * Get all messages in the thread we weren't able to decrypt */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" - (NSArray *)receivedMessagesForInvalidKey:(NSData *)key; +#pragma clang diagnostic pop - (NSUInteger)unreadMessageCountWithTransaction:(YapDatabaseReadTransaction *)transaction NS_SWIFT_NAME(unreadMessageCount(transaction:)); @@ -86,14 +90,6 @@ extern ConversationColorName const kConversationColorName_Default; - (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; -/** - * Returns the latest date of a message in the thread or the thread creation date if there are no messages in that - *thread. - * - * @return The date of the last message or thread creation date. - */ -- (NSDate *)lastMessageDate; - /** * Returns the string that will be displayed typically in a conversations view as a preview of the last message *received in this thread. @@ -117,31 +113,19 @@ extern ConversationColorName const kConversationColorName_Default; #pragma mark Archival /** - * Returns the last date at which a string was archived or nil if the thread was never archived or brought back to the - *inbox. - * - * @return Last archival date. + * @return YES if no new messages have been sent or received since the thread was last archived. */ -- (nullable NSDate *)archivalDate; +- (BOOL)isArchivedWithTransaction:(YapDatabaseReadTransaction *)transaction; /** - * Archives a thread with the current date. + * Archives a thread * * @param transaction Database transaction. */ - (void)archiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; /** - * Archives a thread with the reference date. This is currently only used for migrating older data that has already - * been archived. - * - * @param transaction Database transaction. - * @param date Date at which the thread was archived. - */ -- (void)archiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction referenceDate:(NSDate *)date; - -/** - * Unarchives a thread that was archived previously. + * Unarchives a thread * * @param transaction Database transaction. */ diff --git a/SignalServiceKit/src/Contacts/TSThread.m b/SignalServiceKit/src/Contacts/TSThread.m index e07890428..69d7361f2 100644 --- a/SignalServiceKit/src/Contacts/TSThread.m +++ b/SignalServiceKit/src/Contacts/TSThread.m @@ -15,6 +15,7 @@ #import #import #import +#import #import NS_ASSUME_NONNULL_BEGIN @@ -37,10 +38,8 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa @interface TSThread () @property (nonatomic) NSDate *creationDate; -@property (nonatomic, copy, nullable) NSDate *archivalDate; @property (nonatomic) NSString *conversationColorName; -@property (nonatomic, nullable) NSDate *lastMessageDate; - +@property (nonatomic) NSNumber *archivedAsOfMessageSortId; @property (nonatomic, copy, nullable) NSString *messageDraft; @property (atomic, nullable) NSDate *mutedUntilDate; @@ -59,8 +58,6 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa self = [super initWithUniqueId:uniqueId]; if (self) { - _archivalDate = nil; - _lastMessageDate = nil; _creationDate = [NSDate date]; _messageDraft = nil; @@ -122,6 +119,12 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa _conversationColorName = mappedColorName; } + // MJK TODO - keep these legacy properties around for a while, just in case... + NSDate *_Nullable lastMessageDate = [coder decodeObjectOfClass:NSDate.class forKey:@"lastMessageDate"]; + NSDate *_Nullable archivalDate = [coder decodeObjectOfClass:NSDate.class forKey:@"archivalDate"]; + _isArchivedByLegacyTimestampForSorting = + [self.class legacyIsArchivedWithLastMessageDate:lastMessageDate archivalDate:archivalDate]; + return self; } @@ -250,6 +253,8 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa return [interactions copy]; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" - (NSArray *)receivedMessagesForInvalidKey:(NSData *)key { NSMutableArray *errorMessages = [NSMutableArray new]; @@ -268,6 +273,7 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa return [errorMessages copy]; } +#pragma clang diagnostic pop - (NSUInteger)numberOfInteractions { @@ -343,14 +349,6 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa return last; } -- (NSDate *)lastMessageDate { - if (_lastMessageDate) { - return _lastMessageDate; - } else { - return _creationDate; - } -} - - (NSString *)lastMessageTextWithTransaction:(YapDatabaseReadTransaction *)transaction { TSInteraction *interaction = [self lastInteractionForInboxWithTransaction:transaction]; @@ -398,10 +396,10 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa self.shouldThreadBeVisible = YES; + // MJK FIXME - reconcile this NSDate *lastMessageDate = [lastMessage dateForSorting]; if (!_lastMessageDate || [lastMessageDate timeIntervalSinceDate:self.lastMessageDate] > 0) { _lastMessageDate = lastMessageDate; - [self saveWithTransaction:transaction]; } } @@ -426,30 +424,54 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa } } -#pragma mark Archival +#pragma mark - Archival -- (nullable NSDate *)archivalDate +- (BOOL)isArchivedWithTransaction:(YapDatabaseReadTransaction *)transaction; { - return _archivalDate; + if (!self.archivedAsOfMessageSortId) { + return NO; + } + + TSInteraction *_Nullable latestInteraction = [self lastInteractionForInboxWithTransaction:transaction]; + uint64_t latestSortIdForInbox = latestInteraction ? latestInteraction.sortId : 0; + return self.archivedAsOfMessageSortId.unsignedLongLongValue >= latestSortIdForInbox; } -- (void)archiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { - [self archiveThreadWithTransaction:transaction referenceDate:[NSDate date]]; ++ (BOOL)legacyIsArchivedWithLastMessageDate:(nullable NSDate *)lastMessageDate + archivalDate:(nullable NSDate *)archivalDate +{ + if (!archivalDate) { + return NO; + } + + if (!lastMessageDate) { + return YES; + } + + return [archivalDate compare:lastMessageDate] != NSOrderedAscending; } -- (void)archiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction referenceDate:(NSDate *)date { +- (void)archiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction +{ + [self applyChangeToSelfAndLatestCopy:transaction + changeBlock:^(TSThread *thread) { + uint64_t latestId = [SSKIncrementingIdFinder previousIdWithKey:TSInteraction.collection + transaction:transaction]; + thread.archivedAsOfMessageSortId = @(latestId); + }]; + [self markAllAsReadWithTransaction:transaction]; - _archivalDate = date; - - [self saveWithTransaction:transaction]; } -- (void)unarchiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { - _archivalDate = nil; - [self saveWithTransaction:transaction]; +- (void)unarchiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction +{ + [self applyChangeToSelfAndLatestCopy:transaction + changeBlock:^(TSThread *thread) { + thread.archivedAsOfMessageSortId = nil; + }]; } -#pragma mark Drafts +#pragma mark - Drafts - (NSString *)currentDraftWithTransaction:(YapDatabaseReadTransaction *)transaction { TSThread *thread = [TSThread fetchObjectWithUniqueID:self.uniqueId transaction:transaction]; diff --git a/SignalServiceKit/src/Devices/OWSReceiptsForSenderMessage.m b/SignalServiceKit/src/Devices/OWSReceiptsForSenderMessage.m index 34ca2e276..f188ba47f 100644 --- a/SignalServiceKit/src/Devices/OWSReceiptsForSenderMessage.m +++ b/SignalServiceKit/src/Devices/OWSReceiptsForSenderMessage.m @@ -41,6 +41,7 @@ NS_ASSUME_NONNULL_BEGIN messageTimestamps:(NSArray *)messageTimestamps receiptType:(SSKProtoReceiptMessageType)receiptType { + // MJK TODO - remove senderTimestamp self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:nil diff --git a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m index 1eadb40f8..9faeb559c 100644 --- a/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m +++ b/SignalServiceKit/src/Devices/OWSRecordTranscriptJob.m @@ -88,6 +88,8 @@ NS_ASSUME_NONNULL_BEGIN if (transcript.isEndSessionMessage) { OWSLogInfo(@"EndSession was sent to recipient: %@.", transcript.recipientId); [self.primaryStorage deleteAllSessionsForContact:transcript.recipientId protocolContext:transaction]; + + // MJK TODO - we don't use this timestamp, safe to remove [[[TSInfoMessage alloc] initWithTimestamp:transcript.timestamp inThread:transcript.thread messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction]; @@ -145,12 +147,13 @@ NS_ASSUME_NONNULL_BEGIN } } + [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithDisappearingDuration:outgoingMessage.expiresInSeconds + thread:transcript.thread + createdByRemoteRecipientId:nil + createdInExistingGroup:NO + transaction:transaction]; + if (transcript.isExpirationTimerUpdate) { - [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithConfigurationForMessage:outgoingMessage - contactsManager:self.contactsManager - transaction:transaction]; - - // early return to avoid saving an empty incoming message. OWSAssertDebug(transcript.body.length == 0); OWSAssertDebug(outgoingMessage.attachmentIds.count == 0); @@ -167,9 +170,6 @@ NS_ASSUME_NONNULL_BEGIN [outgoingMessage updateWithWasSentFromLinkedDeviceWithUDRecipientIds:transcript.udRecipientIds nonUdRecipientIds:transcript.nonUdRecipientIds transaction:transaction]; - [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithConfigurationForMessage:outgoingMessage - contactsManager:self.contactsManager - transaction:transaction]; [[OWSDisappearingMessagesJob sharedJob] startAnyExpirationForMessage:outgoingMessage expirationStartedAt:transcript.expirationStartedAt transaction:transaction]; diff --git a/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSyncMessage.m b/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSyncMessage.m index 6deb70e36..31dd15e21 100644 --- a/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSyncMessage.m +++ b/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSyncMessage.m @@ -19,6 +19,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init { + // MJK TODO - remove SenderTimestamp self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:nil messageBody:nil diff --git a/SignalServiceKit/src/Messages/DeviceSyncing/OWSSyncGroupsRequestMessage.m b/SignalServiceKit/src/Messages/DeviceSyncing/OWSSyncGroupsRequestMessage.m index f4c2f7555..76a17d52a 100644 --- a/SignalServiceKit/src/Messages/DeviceSyncing/OWSSyncGroupsRequestMessage.m +++ b/SignalServiceKit/src/Messages/DeviceSyncing/OWSSyncGroupsRequestMessage.m @@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithThread:(nullable TSThread *)thread groupId:(NSData *)groupId { + // MJK TODO - remove senderTimestamp self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:nil diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h index 52b2bb373..b530124fa 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.h @@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN /** * @param remoteName is nil when created by the local user */ +// MJK TODO - can we remove sendertimestamp here - (instancetype)initWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread configuration:(OWSDisappearingMessagesConfiguration *)configuration diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m index 7302bd0c5..5ae75e8d1 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m @@ -74,7 +74,7 @@ NS_ASSUME_NONNULL_BEGIN return [NSString stringWithFormat:infoFormat, self.createdByRemoteName]; } } else { - // Changed by local request + // Changed by localNumber on this device or via synced transcript if (self.configurationIsEnabled && self.configurationDurationSeconds > 0) { NSString *infoFormat = NSLocalizedString(@"YOU_UPDATED_DISAPPEARING_MESSAGES_CONFIGURATION", @"Info message embedding a {{time amount}}, see the *_TIME_AMOUNT strings for context."); diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.h index 37c9a3be8..e28eadc85 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.h @@ -10,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSDisappearingMessagesConfigurationMessage : TSOutgoingMessage +// MJK TODO - remove senderTimestamp - (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp inThread:(nullable TSThread *)thread messageBody:(nullable NSString *)body diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.m index 3c3744636..6f94c85c1 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingMessagesConfigurationMessage.m @@ -26,6 +26,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration thread:(TSThread *)thread { + // MJK TODO - remove sender timestamp self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:nil diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.h index 035da057d..58d5f80d6 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.h @@ -11,6 +11,7 @@ NS_ASSUME_NONNULL_BEGIN typedef NSData *_Nonnull (^DynamicOutgoingMessageBlock)(SignalRecipient *); +/// This class is only used in debug tools @interface OWSDynamicOutgoingMessage : TSOutgoingMessage - (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.m index 452a1f1c7..c24349c4e 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSDynamicOutgoingMessage.m @@ -23,6 +23,7 @@ NS_ASSUME_NONNULL_BEGIN return [self initWithPlainTextDataBlock:block timestamp:[NSDate ows_millisecondTimeStamp] thread:thread]; } +// MJK TODO can we remove sender timestamp? - (instancetype)initWithPlainTextDataBlock:(DynamicOutgoingMessageBlock)block timestamp:(uint64_t)timestamp thread:(nullable TSThread *)thread diff --git a/SignalServiceKit/src/Messages/Interactions/OWSEndSessionMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSEndSessionMessage.h index c3ca4a255..a30812012 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSEndSessionMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSEndSessionMessage.h @@ -20,6 +20,7 @@ NS_SWIFT_NAME(EndSessionMessage) quotedMessage:(nullable TSQuotedMessage *)quotedMessage contactShare:(nullable OWSContact *)contactShare NS_UNAVAILABLE; +// MJK TODO can we remove the sender timestamp? - (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(nullable TSThread *)thread NS_DESIGNATED_INITIALIZER; - (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; diff --git a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h index 2f1a3f75a..775bd14c4 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSRecipientIdentity.h" diff --git a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m index 31c33e803..ca57a6212 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSVerificationStateChangeMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSVerificationStateChangeMessage.h" diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h index e29a6d6ec..8d6e18b99 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.h @@ -51,10 +51,6 @@ typedef NS_ENUM(int32_t, TSErrorMessageType) { failedMessageType:(TSErrorMessageType)errorMessageType recipientId:(nullable NSString *)recipientId NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithTimestamp:(uint64_t)timestamp - inThread:(nullable TSThread *)thread - failedMessageType:(TSErrorMessageType)errorMessageType; - + (instancetype)corruptedMessageWithEnvelope:(SSKProtoEnvelope *)envelope withTransaction:(YapDatabaseReadWriteTransaction *)transaction; diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m index f5796cc3e..b61170691 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m @@ -91,14 +91,11 @@ NSUInteger TSErrorMessageSchemaVersion = 1; TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; + // Legit usage of senderTimestamp. We don't actually currently surface it in the UI, but it serves as + // a reference to the envelope which we failed to process. return [self initWithTimestamp:envelope.timestamp inThread:contactThread failedMessageType:errorMessageType]; } -- (instancetype)initWithFailedMessageType:(TSErrorMessageType)errorMessageType -{ - return [self initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:nil failedMessageType:errorMessageType]; -} - - (OWSInteractionType)interactionType { return OWSInteractionType_Error; @@ -157,7 +154,10 @@ NSUInteger TSErrorMessageSchemaVersion = 1; + (instancetype)corruptedMessageInUnknownThread { - return [[self alloc] initWithFailedMessageType:TSErrorMessageInvalidMessage]; + // MJK TODO - Seems like we could safely remove this timestamp + return [[self alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + inThread:nil + failedMessageType:TSErrorMessageInvalidMessage]; } + (instancetype)invalidVersionWithEnvelope:(SSKProtoEnvelope *)envelope @@ -185,6 +185,7 @@ NSUInteger TSErrorMessageSchemaVersion = 1; + (instancetype)nonblockingIdentityChangeInThread:(TSThread *)thread recipientId:(NSString *)recipientId { + // MJK TODO - should be safe to remove this senderTimestamp return [[self alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread failedMessageType:TSErrorMessageNonBlockingIdentityChange diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage_privateConstructor.h b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage_privateConstructor.h index e2336d800..77e5a39f4 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage_privateConstructor.h +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage_privateConstructor.h @@ -4,17 +4,16 @@ #import "TSErrorMessage.h" +NS_ASSUME_NONNULL_BEGIN + @interface TSErrorMessage () - (instancetype)initWithTimestamp:(uint64_t)timestamp - inThread:(TSThread *)thread + inThread:(nullable TSThread *)thread failedMessageType:(TSErrorMessageType)errorMessageType NS_DESIGNATED_INITIALIZER; @property (atomic, nullable) NSData *envelopeData; -@property NSDictionary *pendingOutgoingMessage; - -#define TSPendingOutgoingMessageKey @"TSPendingOutgoingMessageKey" -#define TSPendingOutgoingMessageRecipientKey @"TSPendingOutgoingMessageRecipientKey" - @end + +NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m index 3b232380c..33a353e81 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m @@ -48,6 +48,7 @@ NSUInteger TSInfoMessageSchemaVersion = 1; inThread:(TSThread *)thread messageType:(TSInfoMessageType)infoMessage { + // MJK TODO - remove senderTimestamp self = [super initMessageWithTimestamp:timestamp inThread:thread messageBody:nil @@ -100,6 +101,7 @@ NSUInteger TSInfoMessageSchemaVersion = 1; OWSAssertDebug(thread); OWSAssertDebug(recipientId); + // MJK TODO - remove senderTimestamp return [[self alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageType:TSInfoMessageUserNotRegistered diff --git a/SignalServiceKit/src/Messages/Interactions/TSInteraction.h b/SignalServiceKit/src/Messages/Interactions/TSInteraction.h index 58a109bc6..f51cf70fa 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInteraction.h +++ b/SignalServiceKit/src/Messages/Interactions/TSInteraction.h @@ -37,6 +37,9 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value); @property (nonatomic, readonly) NSString *uniqueThreadId; @property (nonatomic, readonly) TSThread *thread; @property (nonatomic, readonly) uint64_t timestamp; +@property (nonatomic, readonly) uint64_t sortId; +@property (nonatomic, readonly) uint64_t receivedAtTimestamp; +- (NSDate *)receivedAtDate; - (OWSInteractionType)interactionType; @@ -58,8 +61,8 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value); filter:(BOOL (^_Nonnull)(TSInteraction *))filter withTransaction:(YapDatabaseReadTransaction *)transaction; -- (NSDate *)dateForSorting; -- (uint64_t)timestampForSorting; +- (NSDate *)dateForLegacySorting; +- (uint64_t)timestampForLegacySorting; - (NSComparisonResult)compareForSorting:(TSInteraction *)other; // "Dynamic" interactions are not messages or static events (like @@ -70,6 +73,9 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value); // unseen message indicators, etc. - (BOOL)isDynamicInteraction; +- (void)saveNextSortIdWithTransaction:(YapDatabaseReadWriteTransaction *)transaction + NS_SWIFT_NAME(saveNextSortId(transaction:)); + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/Interactions/TSInteraction.m b/SignalServiceKit/src/Messages/Interactions/TSInteraction.m index b77dd9025..f534999b2 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInteraction.m +++ b/SignalServiceKit/src/Messages/Interactions/TSInteraction.m @@ -6,6 +6,7 @@ #import "TSDatabaseSecondaryIndexes.h" #import "TSThread.h" #import +#import NS_ASSUME_NONNULL_BEGIN @@ -31,6 +32,12 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value) } } +@interface TSInteraction () + +@property (nonatomic) uint64_t sortId; + +@end + @implementation TSInteraction + (NSArray *)interactionsWithTimestamp:(uint64_t)timestamp @@ -58,7 +65,6 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value) [TSDatabaseSecondaryIndexes enumerateMessagesWithTimestamp:timestamp withBlock:^(NSString *collection, NSString *key, BOOL *stop) { - TSInteraction *interaction = [TSInteraction fetchObjectWithUniqueID:key transaction:transaction]; if (!filter(interaction)) { @@ -105,6 +111,37 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value) _timestamp = timestamp; _uniqueThreadId = thread.uniqueId; + _receivedAtTimestamp = [NSDate ows_millisecondTimeStamp]; + + return self; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)coder +{ + self = [super initWithCoder:coder]; + if (!self) { + return nil; + } + + // Previously the receivedAtTimestamp field lived on TSMessage, but we've moved it up + // to the TSInteraction superclass. + if (_receivedAtTimestamp == 0) { + // Upgrade from the older "TSMessage.receivedAtDate" and "TSMessage.receivedAt" properties if + // necessary. + NSDate *receivedAtDate = [coder decodeObjectForKey:@"receivedAtDate"]; + if (!receivedAtDate) { + receivedAtDate = [coder decodeObjectForKey:@"receivedAt"]; + } + + if (receivedAtDate) { + _receivedAtTimestamp = [NSDate ows_millisecondsSince1970ForDate:receivedAtDate]; + } + + // For TSInteractions which are not TSMessage's, the timestamp *is* the receivedAtTimestamp + if (_receivedAtTimestamp == 0) { + _receivedAtTimestamp = _timestamp; + } + } return self; } @@ -129,26 +166,31 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value) #pragma mark Date operations -- (NSDate *)dateForSorting +- (NSDate *)dateForLegacySorting { - return [NSDate ows_dateWithMillisecondsSince1970:self.timestampForSorting]; + return [NSDate ows_dateWithMillisecondsSince1970:self.timestampForLegacySorting]; } -- (uint64_t)timestampForSorting +- (uint64_t)timestampForLegacySorting { return self.timestamp; } +- (NSDate *)receivedAtDate +{ + return [NSDate ows_dateWithMillisecondsSince1970:self.receivedAtTimestamp]; +} + - (NSComparisonResult)compareForSorting:(TSInteraction *)other { OWSAssertDebug(other); - uint64_t timestamp1 = self.timestampForSorting; - uint64_t timestamp2 = other.timestampForSorting; + uint64_t sortId1 = self.sortId; + uint64_t sortId2 = self.sortId; - if (timestamp1 > timestamp2) { + if (sortId1 > sortId2) { return NSOrderedDescending; - } else if (timestamp1 < timestamp2) { + } else if (sortId1 < sortId2) { return NSOrderedAscending; } else { return NSOrderedSame; @@ -170,15 +212,21 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value) (unsigned long)self.timestamp]; } -- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { +- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction +{ + // MJK can we remove this? We can't trust the legacy order of this id field. Any reason not to use UUID like for + // other objects? if (!self.uniqueId) { OWSFailDebug(@"Missing uniqueId."); self.uniqueId = [NSUUID new].UUIDString; } + if (self.sortId == 0) { + self.sortId = [SSKIncrementingIdFinder nextIdWithKey:[TSInteraction collection] transaction:transaction]; + } [super saveWithTransaction:transaction]; - TSThread *fetchedThread = [TSThread fetchObjectWithUniqueID:self.uniqueThreadId transaction:transaction]; + TSThread *fetchedThread = [self threadWithTransaction:transaction]; [fetchedThread updateWithLastMessage:self transaction:transaction]; } @@ -195,6 +243,20 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value) return NO; } +#pragma mark - sorting migration + +- (void)saveNextSortIdWithTransaction:(YapDatabaseReadWriteTransaction *)transaction +{ + if (self.sortId != 0) { + // This could happen if something else in our startup process saved the interaction + // e.g. another migration ran. + // During the migration, since we're enumerating the interactions in the proper order, + // we want to ignore any previously assigned sortId + self.sortId = 0; + } + [self saveWithTransaction:transaction]; +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index 546c9b795..26a5f6c53 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -47,13 +47,6 @@ static const NSUInteger OWSMessageSchemaVersion = 4; */ @property (nonatomic, readonly) NSUInteger schemaVersion; -// The timestamp property is populated by the envelope, -// which is created by the sender. -// -// We typically want to order messages locally by when -// they were received & decrypted, not by when they were sent. -@property (nonatomic) uint64_t receivedAtTimestamp; - @end #pragma mark - @@ -82,7 +75,6 @@ static const NSUInteger OWSMessageSchemaVersion = 4; _expiresInSeconds = expiresInSeconds; _expireStartedAt = expireStartedAt; [self updateExpiresAt]; - _receivedAtTimestamp = [NSDate ows_millisecondTimeStamp]; _quotedMessage = quotedMessage; _contactShare = contactShare; @@ -135,18 +127,6 @@ static const NSUInteger OWSMessageSchemaVersion = 4; _attachmentIds = [NSMutableArray new]; } - if (_receivedAtTimestamp == 0) { - // Upgrade from the older "receivedAtDate" and "receivedAt" properties if - // necessary. - NSDate *receivedAtDate = [coder decodeObjectForKey:@"receivedAtDate"]; - if (!receivedAtDate) { - receivedAtDate = [coder decodeObjectForKey:@"receivedAt"]; - } - if (receivedAtDate) { - _receivedAtTimestamp = [NSDate ows_millisecondsSince1970ForDate:receivedAtDate]; - } - } - _schemaVersion = OWSMessageSchemaVersion; return self; @@ -382,7 +362,7 @@ static const NSUInteger OWSMessageSchemaVersion = 4; return self.expiresInSeconds > 0; } -- (uint64_t)timestampForSorting +- (uint64_t)timestampForLegacySorting { if ([self shouldUseReceiptDateForSorting] && self.receivedAtTimestamp > 0) { return self.receivedAtTimestamp; diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h index cdd8e3f64..af0531dc6 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h @@ -79,6 +79,7 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { quotedMessage:(nullable TSQuotedMessage *)quotedMessage contactShare:(nullable OWSContact *)contactShare NS_UNAVAILABLE; +// MJK TODO - Can we remove the sender timestamp param? - (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp inThread:(nullable TSThread *)thread messageBody:(nullable NSString *)body diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m index 49a245129..15640f5f7 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m @@ -257,6 +257,7 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt [attachmentIds addObject:attachmentId]; } + // MJK TODO remove SenderTimestamp? return [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:body @@ -273,6 +274,7 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage expiresInSeconds:(uint32_t)expiresInSeconds; { + // MJK TODO remove SenderTimestamp? return [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:nil diff --git a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m index b814c40e6..d4b89d10c 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSQuotedMessage.m @@ -208,6 +208,7 @@ NS_ASSUME_NONNULL_BEGIN return nil; } + // Legit usage of senderTimestamp - this class references the message it is quoting by it's sender timestamp return [[TSQuotedMessage alloc] initWithTimestamp:timestamp authorId:authorId body:body @@ -229,7 +230,6 @@ NS_ASSUME_NONNULL_BEGIN NSArray *quotedMessages = (NSArray *)[TSInteraction interactionsWithTimestamp:timestamp filter:^BOOL(TSInteraction *interaction) { - if (![threadId isEqual:interaction.uniqueThreadId]) { return NO; } @@ -243,7 +243,6 @@ NS_ASSUME_NONNULL_BEGIN // ignore other interaction types return NO; } - } withTransaction:transaction]; diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h index 8aca7e224..9bb4fe456 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.h @@ -10,10 +10,12 @@ NS_ASSUME_NONNULL_BEGIN // DEPRECATED - we no longer create new instances of this class (as of mid-2017); However, existing instances may // exist, so we should keep this class around to honor their old behavior. -@interface TSInvalidIdentityKeyReceivingErrorMessage : TSInvalidIdentityKeyErrorMessage +__attribute__((deprecated)) @interface TSInvalidIdentityKeyReceivingErrorMessage : TSInvalidIdentityKeyErrorMessage +#ifdef DEBUG + (nullable instancetype)untrustedKeyWithEnvelope:(SSKProtoEnvelope *)envelope withTransaction:(YapDatabaseReadWriteTransaction *)transaction; +#endif @end diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m index 7fa210fa9..07571df55 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeyReceivingErrorMessage.m @@ -20,8 +20,7 @@ NS_ASSUME_NONNULL_BEGIN -/// TODO we can eventually deprecate this, since incoming messages are now always decrypted. -@interface TSInvalidIdentityKeyReceivingErrorMessage () +__attribute__((deprecated)) @interface TSInvalidIdentityKeyReceivingErrorMessage() @property (nonatomic, readonly, copy) NSString *authorId; @@ -34,15 +33,20 @@ NS_ASSUME_NONNULL_BEGIN @synthesize envelopeData = _envelopeData; +#ifdef DEBUG +// We no longer create these messages, but they might exist on legacy clients so it's useful to be able to +// create them with the debug UI + (nullable instancetype)untrustedKeyWithEnvelope:(SSKProtoEnvelope *)envelope withTransaction:(YapDatabaseReadWriteTransaction *)transaction { TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; + + // Legit usage of senderTimestamp, references message which failed to decrypt TSInvalidIdentityKeyReceivingErrorMessage *errorMessage = - [[self alloc] initForUnknownIdentityKeyWithTimestamp:envelope.timestamp - inThread:contactThread - incomingEnvelope:envelope]; + [[self alloc] initForUnknownIdentityKeyWithTimestamp:envelope.timestamp + inThread:contactThread + incomingEnvelope:envelope]; return errorMessage; } @@ -66,6 +70,7 @@ NS_ASSUME_NONNULL_BEGIN return self; } +#endif - (nullable SSKProtoEnvelope *)envelope { diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h index 08efb2214..8b86e43c7 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "TSInvalidIdentityKeyErrorMessage.h" @@ -13,7 +13,9 @@ NS_ASSUME_NONNULL_BEGIN extern NSString *TSInvalidPreKeyBundleKey; extern NSString *TSInvalidRecipientKey; -@interface TSInvalidIdentityKeySendingErrorMessage : TSInvalidIdentityKeyErrorMessage +// DEPRECATED - we no longer create new instances of this class (as of mid-2017); However, existing instances may +// exist, so we should keep this class around to honor their old behavior. +__attribute__((deprecated)) @interface TSInvalidIdentityKeySendingErrorMessage : TSInvalidIdentityKeyErrorMessage @property (nonatomic, readonly) NSString *messageId; diff --git a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m index 3b5a47b2d..b15c582ee 100644 --- a/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m +++ b/SignalServiceKit/src/Messages/InvalidKeyMessages/TSInvalidIdentityKeySendingErrorMessage.m @@ -24,26 +24,12 @@ NSString *TSInvalidRecipientKey = @"TSInvalidRecipientKey"; @end +// DEPRECATED - we no longer create new instances of this class (as of mid-2017); However, existing instances may +// exist, so we should keep this class around to honor their old behavior. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation TSInvalidIdentityKeySendingErrorMessage - -- (instancetype)initWithOutgoingMessage:(TSOutgoingMessage *)message - inThread:(TSThread *)thread - forRecipient:(NSString *)recipientId - preKeyBundle:(PreKeyBundle *)preKeyBundle -{ - // We want the error message to appear after the message. - self = [super initWithTimestamp:message.timestamp + 1 - inThread:thread - failedMessageType:TSErrorMessageWrongTrustedIdentityKey - recipientId:recipientId]; - - if (self) { - _messageId = message.uniqueId; - _preKeyBundle = preKeyBundle; - } - - return self; -} +#pragma clang diagnostic pop - (void)throws_acceptNewIdentityKey { diff --git a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.h b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.h index aec3bc036..15a2af9d9 100644 --- a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.h +++ b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.h @@ -1,14 +1,18 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "TSInfoMessage.h" NS_ASSUME_NONNULL_BEGIN -@interface OWSAddToContactsOfferMessage : TSInfoMessage +// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors +// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) +__attribute__((deprecated)) @interface OWSAddToContactsOfferMessage : TSInfoMessage -+ (instancetype)addToContactsOfferMessage:(uint64_t)timestamp thread:(TSThread *)thread contactId:(NSString *)contactId; ++ (instancetype)addToContactsOfferMessageWithTimestamp:(uint64_t)timestamp + thread:(TSThread *)thread + contactId:(NSString *)contactId; @property (nonatomic, readonly) NSString *contactId; diff --git a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.m b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.m index b09686a0b..af1b648c8 100644 --- a/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.m +++ b/SignalServiceKit/src/Messages/OWSAddToContactsOfferMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSAddToContactsOfferMessage.h" @@ -14,9 +14,16 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - +// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors +// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation OWSAddToContactsOfferMessage +#pragma clang diagnostic pop -+ (instancetype)addToContactsOfferMessage:(uint64_t)timestamp thread:(TSThread *)thread contactId:(NSString *)contactId ++ (instancetype)addToContactsOfferMessageWithTimestamp:(uint64_t)timestamp + thread:(TSThread *)thread + contactId:(NSString *)contactId { return [[OWSAddToContactsOfferMessage alloc] initWithTimestamp:timestamp thread:thread contactId:contactId]; } diff --git a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.h b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.h index 0e003ca7a..368c93085 100644 --- a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.h +++ b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.h @@ -1,14 +1,16 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "TSInfoMessage.h" NS_ASSUME_NONNULL_BEGIN -@interface OWSAddToProfileWhitelistOfferMessage : TSInfoMessage +// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors +// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) +__attribute__((deprecated)) @interface OWSAddToProfileWhitelistOfferMessage : TSInfoMessage -+ (instancetype)addToProfileWhitelistOfferMessage:(uint64_t)timestamp thread:(TSThread *)thread; ++ (instancetype)addToProfileWhitelistOfferMessageWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread; @property (nonatomic, readonly) NSString *contactId; diff --git a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.m b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.m index 7a59d80ae..0ca611409 100644 --- a/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.m +++ b/SignalServiceKit/src/Messages/OWSAddToProfileWhitelistOfferMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSAddToProfileWhitelistOfferMessage.h" @@ -7,9 +7,14 @@ NS_ASSUME_NONNULL_BEGIN +// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors +// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation OWSAddToProfileWhitelistOfferMessage +#pragma clang diagnostic pop -+ (instancetype)addToProfileWhitelistOfferMessage:(uint64_t)timestamp thread:(TSThread *)thread ++ (instancetype)addToProfileWhitelistOfferMessageWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread { return [[OWSAddToProfileWhitelistOfferMessage alloc] initWithTimestamp:timestamp diff --git a/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.h b/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.h index bbcd54260..48e4cf95f 100644 --- a/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.h +++ b/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.h @@ -23,41 +23,23 @@ NS_ASSUME_NONNULL_BEGIN expirationStartedAt:(uint64_t)expirationStartedAt transaction:(YapDatabaseReadWriteTransaction *_Nonnull)transaction; -/** - * Synchronize our disappearing messages settings with that of the given message. Useful so we can - * become eventually consistent with remote senders. - * - * @param message - * Can be an expiring or non expiring message. We match the expiration timer of the message, including disabling - * expiring messages if the message is not an expiring message. - * - * @param contactsManager - * Provides the contact name responsible for any configuration changes in an info message. - */ -- (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message - contactsManager:(id)contactsManager - transaction:(YapDatabaseReadWriteTransaction *)transaction; - /** * Synchronize our disappearing messages settings with that of the given message. Useful so we can * become eventually consistent with remote senders. * * @param duration * Can be 0, indicating a non-expiring message, or greater, indicating an expiring message. We match the expiration - * timer of the message, including disabling expiring messages if the message is not an expiring message. + * timer of the message, including disabling expiring messages if the message is not an expiring message. * - * @param timestampForSorting - * timestampForSorting of the message before which we want to appear. + * @param remoteRecipientId + * nil for outgoing messages, otherwise the recipientId of the sender * - * @param remoteContactName - * nil for outgoing messages, otherwise the name of the sender * @param createdInExistingGroup * YES when being added to a group which already has DM enabled, otherwise NO */ - (void)becomeConsistentWithDisappearingDuration:(uint32_t)duration thread:(TSThread *)thread - appearBeforeTimestamp:(uint64_t)timestampForSorting - createdByRemoteContactName:(nullable NSString *)remoteContactName + createdByRemoteRecipientId:(nullable NSString *)remoteRecipientId createdInExistingGroup:(BOOL)createdInExistingGroup transaction:(YapDatabaseReadWriteTransaction *)transaction; diff --git a/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.m b/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.m index 31665a853..b3c55eaba 100644 --- a/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.m +++ b/SignalServiceKit/src/Messages/OWSDisappearingMessagesJob.m @@ -108,6 +108,15 @@ void AssertIsOnDisappearingMessagesQueue() return queue; } +#pragma mark - Dependencies + +- (id)contactsManager +{ + return SSKEnvironment.shared.contactsManager; +} + +#pragma mark - + - (NSUInteger)deleteExpiredMessages { AssertIsOnDisappearingMessagesQueue(); @@ -191,39 +200,24 @@ void AssertIsOnDisappearingMessagesQueue() }]; } -- (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message - contactsManager:(id)contactsManager - transaction:(YapDatabaseReadWriteTransaction *)transaction -{ - TSThread *thread = [message threadWithTransaction:transaction]; - NSString *remoteContactName = nil; - if ([message isKindOfClass:[TSIncomingMessage class]]) { - TSIncomingMessage *incomingMessage = (TSIncomingMessage *)message; - remoteContactName = - [contactsManager displayNameForPhoneIdentifier:incomingMessage.authorId transaction:transaction]; - } - - [self becomeConsistentWithDisappearingDuration:message.expiresInSeconds - thread:thread - appearBeforeTimestamp:message.timestampForSorting - createdByRemoteContactName:remoteContactName - createdInExistingGroup:NO - transaction:transaction]; -} +#pragma mark - Apply Remote Configuration - (void)becomeConsistentWithDisappearingDuration:(uint32_t)duration thread:(TSThread *)thread - appearBeforeTimestamp:(uint64_t)timestampForSorting - createdByRemoteContactName:(nullable NSString *)remoteContactName + createdByRemoteRecipientId:(nullable NSString *)remoteRecipientId createdInExistingGroup:(BOOL)createdInExistingGroup transaction:(YapDatabaseReadWriteTransaction *)transaction { OWSAssertDebug(thread); - OWSAssertDebug(timestampForSorting > 0); OWSAssertDebug(transaction); OWSBackgroundTask *_Nullable backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__]; + NSString *_Nullable remoteContactName = nil; + if (remoteRecipientId) { + remoteContactName = [self.contactsManager displayNameForPhoneIdentifier:remoteRecipientId]; + } + // Become eventually consistent in the case that the remote changed their settings at the same time. // Also in case remote doesn't support expiring messages OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration = @@ -245,9 +239,9 @@ void AssertIsOnDisappearingMessagesQueue() [disappearingMessagesConfiguration saveWithTransaction:transaction]; - // We want the info message to appear _before_ the message. + // MJK TODO - should be safe to remove this senderTimestamp OWSDisappearingConfigurationUpdateInfoMessage *infoMessage = - [[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:timestampForSorting - 1 + [[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread configuration:disappearingMessagesConfiguration createdByRemoteName:remoteContactName @@ -258,6 +252,8 @@ void AssertIsOnDisappearingMessagesQueue() backgroundTask = nil; } +#pragma mark - + - (void)startIfNecessary { dispatch_async(dispatch_get_main_queue(), ^{ @@ -393,7 +389,7 @@ void AssertIsOnDisappearingMessagesQueue() OWSFailDebug(@"starting old timer for message timestamp: %lu", (unsigned long)message.timestamp); // We don't know when it was actually read, so assume it was read as soon as it was received. - uint64_t readTimeBestGuess = message.timestampForSorting; + uint64_t readTimeBestGuess = message.receivedAtTimestamp; [self startAnyExpirationForMessage:message expirationStartedAt:readTimeBestGuess transaction:transaction]; } transaction:transaction]; diff --git a/SignalServiceKit/src/Messages/OWSIdentityManager.m b/SignalServiceKit/src/Messages/OWSIdentityManager.m index a17d6f442..2f22b1fad 100644 --- a/SignalServiceKit/src/Messages/OWSIdentityManager.m +++ b/SignalServiceKit/src/Messages/OWSIdentityManager.m @@ -541,6 +541,7 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa [messages addObject:[TSErrorMessage nonblockingIdentityChangeInThread:groupThread recipientId:recipientId]]; } + // MJK TODO - why not save immediately, why build up this array? for (TSMessage *message in messages) { [message saveWithTransaction:transaction]; } @@ -862,6 +863,7 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:recipientId transaction:transaction]; OWSAssertDebug(contactThread); + // MJK TODO - should be safe to remove senderTimestamp [messages addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:contactThread recipientId:recipientId @@ -870,6 +872,7 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa for (TSGroupThread *groupThread in [TSGroupThread groupThreadsWithRecipientId:recipientId transaction:transaction]) { + // MJK TODO - should be safe to remove senderTimestamp [messages addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:groupThread @@ -878,6 +881,7 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa isLocalChange:isLocalChange]]; } + // MJK TODO - why not save in-line, vs storing in an array and saving the array? for (TSMessage *message in messages) { [message saveWithTransaction:transaction]; } diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index b3cf77121..9f979a34d 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -978,6 +978,7 @@ NS_ASSUME_NONNULL_BEGIN TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; + // MJK TODO - safe to remove senderTimestamp [[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction]; @@ -1026,6 +1027,8 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(disappearingMessagesConfiguration); [disappearingMessagesConfiguration saveWithTransaction:transaction]; NSString *name = [self.contactsManager displayNameForPhoneIdentifier:envelope.source transaction:transaction]; + + // MJK TODO - safe to remove senderTimestamp OWSDisappearingConfigurationUpdateInfoMessage *message = [[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] thread:thread @@ -1223,8 +1226,6 @@ NS_ASSUME_NONNULL_BEGIN TSGroupThread *newGroupThread = [TSGroupThread getOrCreateThreadWithGroupId:groupId transaction:transaction]; - - uint64_t now = [NSDate ows_millisecondTimeStamp]; TSGroupModel *newGroupModel = [[TSGroupModel alloc] initWithTitle:dataMessage.group.name memberIds:newMemberIds.allObjects image:oldGroupThread.groupModel.groupImage @@ -1234,22 +1235,19 @@ NS_ASSUME_NONNULL_BEGIN newGroupThread.groupModel = newGroupModel; [newGroupThread saveWithTransaction:transaction]; - TSInfoMessage *infoMessage = [[TSInfoMessage alloc] initWithTimestamp:now + [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithDisappearingDuration:dataMessage.expireTimer + thread:newGroupThread + createdByRemoteRecipientId:nil + createdInExistingGroup:NO + transaction:transaction]; + + // MJK TODO - should be safe to remove senderTimestamp + TSInfoMessage *infoMessage = [[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:newGroupThread messageType:TSInfoMessageTypeGroupUpdate customMessage:updateGroupInfo]; [infoMessage saveWithTransaction:transaction]; - if (dataMessage.hasExpireTimer && dataMessage.expireTimer > 0) { - [[OWSDisappearingMessagesJob sharedJob] - becomeConsistentWithDisappearingDuration:dataMessage.expireTimer - thread:newGroupThread - appearBeforeTimestamp:now - createdByRemoteContactName:nil - createdInExistingGroup:YES - transaction:transaction]; - } - return nil; } case SSKProtoGroupContextTypeQuit: { @@ -1265,6 +1263,7 @@ NS_ASSUME_NONNULL_BEGIN [self.contactsManager displayNameForPhoneIdentifier:envelope.source transaction:transaction]; NSString *updateGroupInfo = [NSString stringWithFormat:NSLocalizedString(@"GROUP_MEMBER_LEFT", @""), nameString]; + // MJK TODO - should be safe to remove senderTimestamp [[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:oldGroupThread messageType:TSInfoMessageTypeGroupUpdate @@ -1277,6 +1276,14 @@ NS_ASSUME_NONNULL_BEGIN return nil; } + // MJK FIXME - this `becomeConsistent...` call seems to have been remove in master. + // to where? + [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithDisappearingDuration:dataMessage.expireTimer + thread:oldGroupThread + createdByRemoteRecipientId:envelope.source + createdInExistingGroup:YES + transaction:transaction]; + TSQuotedMessage *_Nullable quotedMessage = [TSQuotedMessage quotedMessageForDataMessage:dataMessage thread:oldGroupThread transaction:transaction]; @@ -1286,6 +1293,7 @@ NS_ASSUME_NONNULL_BEGIN groupId, (unsigned long)timestamp); + // Legit usage of senderTimestamp when creating an incoming group message record TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:timestamp inThread:oldGroupThread @@ -1332,10 +1340,17 @@ NS_ASSUME_NONNULL_BEGIN TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; + [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithDisappearingDuration:dataMessage.expireTimer + thread:thread + createdByRemoteRecipientId:envelope.source + createdInExistingGroup:NO + transaction:transaction]; + TSQuotedMessage *_Nullable quotedMessage = [TSQuotedMessage quotedMessageForDataMessage:dataMessage thread:thread transaction:transaction]; + // Legit usage of senderTimestamp when creating incoming message from received envelope TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:timestamp inThread:thread @@ -1454,10 +1469,6 @@ NS_ASSUME_NONNULL_BEGIN [OWSReadReceiptManager.sharedManager applyEarlyReadReceiptsForIncomingMessage:incomingMessage transaction:transaction]; - [[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithConfigurationForMessage:incomingMessage - contactsManager:self.contactsManager - transaction:transaction]; - // Update thread preview in inbox [thread touchWithTransaction:transaction]; diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 67c472f07..b734a0764 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -711,9 +711,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; OWSAssertDebug(message.recipientIds.count == 1); [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { for (NSString *recipientId in message.sendingRecipientIds) { - [message updateWithReadRecipientId:recipientId - readTimestamp:message.timestampForSorting - transaction:transaction]; + [message updateWithReadRecipientId:recipientId readTimestamp:message.timestamp transaction:transaction]; } }]; successHandler(); @@ -1756,11 +1754,13 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // TODO: Why is this necessary? [message save]; } else if (message.groupMetaMessage == TSGroupMetaMessageQuit) { + // MJK TODO - remove senderTimestamp [[[TSInfoMessage alloc] initWithTimestamp:message.timestamp inThread:thread messageType:TSInfoMessageTypeGroupQuit customMessage:message.customMessage] save]; } else { + // MJK TODO - remove senderTimestamp [[[TSInfoMessage alloc] initWithTimestamp:message.timestamp inThread:thread messageType:TSInfoMessageTypeGroupUpdate diff --git a/SignalServiceKit/src/Messages/OWSOutgoingCallMessage.m b/SignalServiceKit/src/Messages/OWSOutgoingCallMessage.m index a6cf2c965..18ef4c50f 100644 --- a/SignalServiceKit/src/Messages/OWSOutgoingCallMessage.m +++ b/SignalServiceKit/src/Messages/OWSOutgoingCallMessage.m @@ -15,8 +15,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithThread:(TSThread *)thread { - // These records aren't saved, but their timestamp is used in the event - // of a failing message send to insert the error at the appropriate place. + // MJK TODO - safe to remove senderTimestamp self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:nil diff --git a/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.m b/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.m index 686f99330..d8a8f1645 100644 --- a/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.m +++ b/SignalServiceKit/src/Messages/OWSOutgoingNullMessage.m @@ -24,6 +24,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithContactThread:(TSContactThread *)contactThread verificationStateSyncMessage:(OWSVerificationStateSyncMessage *)verificationStateSyncMessage { + // MJK TODO - remove senderTimestamp self = [super initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:contactThread messageBody:nil diff --git a/SignalServiceKit/src/Messages/OWSReadReceiptManager.h b/SignalServiceKit/src/Messages/OWSReadReceiptManager.h index 4280e5e84..9bd2f7f03 100644 --- a/SignalServiceKit/src/Messages/OWSReadReceiptManager.h +++ b/SignalServiceKit/src/Messages/OWSReadReceiptManager.h @@ -71,7 +71,7 @@ extern NSString *const kIncomingMessageMarkedAsReadNotification; // This method can be called from any thread. - (void)messageWasReadLocally:(TSIncomingMessage *)message; -- (void)markAsReadLocallyBeforeTimestamp:(uint64_t)timestamp thread:(TSThread *)thread; +- (void)markAsReadLocallyBeforeSortId:(uint64_t)sortId thread:(TSThread *)thread; #pragma mark - Settings diff --git a/SignalServiceKit/src/Messages/OWSReadReceiptManager.m b/SignalServiceKit/src/Messages/OWSReadReceiptManager.m index 16d6e804c..8f703ca13 100644 --- a/SignalServiceKit/src/Messages/OWSReadReceiptManager.m +++ b/SignalServiceKit/src/Messages/OWSReadReceiptManager.m @@ -245,17 +245,17 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE #pragma mark - Mark as Read Locally -- (void)markAsReadLocallyBeforeTimestamp:(uint64_t)timestamp thread:(TSThread *)thread +- (void)markAsReadLocallyBeforeSortId:(uint64_t)sortId thread:(TSThread *)thread { OWSAssertDebug(thread); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [self markAsReadBeforeTimestamp:timestamp - thread:thread - readTimestamp:[NSDate ows_millisecondTimeStamp] - wasLocal:YES - transaction:transaction]; + [self markAsReadBeforeSortId:sortId + thread:thread + readTimestamp:[NSDate ows_millisecondTimeStamp] + wasLocal:YES + transaction:transaction]; }]; }); } @@ -452,26 +452,23 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE // Always re-mark the message as read to ensure any earlier read time is applied to disappearing messages. [message markAsReadAtTimestamp:readTimestamp sendReadReceipt:NO transaction:transaction]; - // Also mark any messages appearing earlier in the thread as read. - // - // Use `timestampForSorting` which reflects local received order, rather than `timestamp` - // which reflect sender time. - [self markAsReadBeforeTimestamp:message.timestampForSorting - thread:[message threadWithTransaction:transaction] - readTimestamp:readTimestamp - wasLocal:NO - transaction:transaction]; + // Also mark any unread messages appearing earlier in the thread as read as well. + [self markAsReadBeforeSortId:message.sortId + thread:[message threadWithTransaction:transaction] + readTimestamp:readTimestamp + wasLocal:NO + transaction:transaction]; } #pragma mark - Mark As Read -- (void)markAsReadBeforeTimestamp:(uint64_t)timestamp - thread:(TSThread *)thread - readTimestamp:(uint64_t)readTimestamp - wasLocal:(BOOL)wasLocal - transaction:(YapDatabaseReadWriteTransaction *)transaction +- (void)markAsReadBeforeSortId:(uint64_t)sortId + thread:(TSThread *)thread + readTimestamp:(uint64_t)readTimestamp + wasLocal:(BOOL)wasLocal + transaction:(YapDatabaseReadWriteTransaction *)transaction { - OWSAssertDebug(timestamp > 0); + OWSAssertDebug(sortId > 0); OWSAssertDebug(thread); OWSAssertDebug(transaction); @@ -490,8 +487,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE return; } id possiblyRead = (id)object; - - if (possiblyRead.timestampForSorting > timestamp) { + if (possiblyRead.sortId > sortId) { *stop = YES; return; } diff --git a/SignalServiceKit/src/Messages/OWSReadTracking.h b/SignalServiceKit/src/Messages/OWSReadTracking.h index 5686e744f..e3314ea83 100644 --- a/SignalServiceKit/src/Messages/OWSReadTracking.h +++ b/SignalServiceKit/src/Messages/OWSReadTracking.h @@ -16,7 +16,7 @@ @property (nonatomic, readonly, getter=wasRead) BOOL read; @property (nonatomic, readonly) uint64_t expireStartedAt; -@property (nonatomic, readonly) uint64_t timestampForSorting; +@property (nonatomic, readonly) uint64_t sortId; @property (nonatomic, readonly) NSString *uniqueThreadId; diff --git a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.h b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.h index 7d9099dd9..256edd2dd 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.h +++ b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.h @@ -1,16 +1,14 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "TSErrorMessage.h" NS_ASSUME_NONNULL_BEGIN -@interface OWSUnknownContactBlockOfferMessage : TSErrorMessage - -+ (instancetype)unknownContactBlockOfferMessage:(uint64_t)timestamp - thread:(TSThread *)thread - contactId:(NSString *)contactId; +// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors +// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) +__attribute__((deprecated)) @interface OWSUnknownContactBlockOfferMessage : TSErrorMessage @property (nonatomic, readonly) NSString *contactId; diff --git a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.m b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.m index 3c9de57e1..3d11291cf 100644 --- a/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.m +++ b/SignalServiceKit/src/Messages/OWSUnknownContactBlockOfferMessage.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSUnknownContactBlockOfferMessage.h" @@ -14,25 +14,12 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - +// This is a deprecated class, we're keeping it around to avoid YapDB serialization errors +// TODO - remove this class, clean up existing instances, ensure any missed ones don't explode (UnknownDBObject) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation OWSUnknownContactBlockOfferMessage - -+ (instancetype)unknownContactBlockOfferMessage:(uint64_t)timestamp - thread:(TSThread *)thread - contactId:(NSString *)contactId -{ - return [[OWSUnknownContactBlockOfferMessage alloc] initWithTimestamp:timestamp thread:thread contactId:contactId]; -} - -- (instancetype)initWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread contactId:(NSString *)contactId -{ - self = [super initWithTimestamp:timestamp inThread:thread failedMessageType:TSErrorMessageUnknownContactBlockOffer]; - - if (self) { - _contactId = contactId; - } - - return self; -} +#pragma clang diagnostic pop - (BOOL)shouldUseReceiptDateForSorting { diff --git a/SignalServiceKit/src/Storage/IncrementingIdFinder.swift b/SignalServiceKit/src/Storage/IncrementingIdFinder.swift new file mode 100644 index 000000000..d8f06970c --- /dev/null +++ b/SignalServiceKit/src/Storage/IncrementingIdFinder.swift @@ -0,0 +1,27 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +@objc +public class SSKIncrementingIdFinder: NSObject { + + private static let collectionName = "IncrementingIdCollection" + + @objc + public class func previousId(key: String, transaction: YapDatabaseReadTransaction) -> UInt64 { + let previousId: UInt64 = transaction.object(forKey: key, inCollection: collectionName) as? UInt64 ?? 0 + return previousId + } + + @objc + public class func nextId(key: String, transaction: YapDatabaseReadWriteTransaction) -> UInt64 { + let previousId: UInt64 = transaction.object(forKey: key, inCollection: collectionName) as? UInt64 ?? 0 + let nextId: UInt64 = previousId + 1 + + transaction.setObject(nextId, forKey: key, inCollection: collectionName) + Logger.debug("key: \(key) nextId: \(nextId)") + return nextId + } +} diff --git a/SignalServiceKit/src/Storage/OWSPrimaryStorage.m b/SignalServiceKit/src/Storage/OWSPrimaryStorage.m index 8e22c765e..3fa3aba82 100644 --- a/SignalServiceKit/src/Storage/OWSPrimaryStorage.m +++ b/SignalServiceKit/src/Storage/OWSPrimaryStorage.m @@ -188,6 +188,7 @@ void VerifyRegistrationsForPrimaryStorage(OWSStorage *storage) // // All sync registrations must be done before all async registrations, // or the sync registrations will block on the async registrations. + [TSDatabaseView asyncRegisterLegacyThreadInteractionsDatabaseView:self]; [TSDatabaseView asyncRegisterThreadInteractionsDatabaseView:self]; [TSDatabaseView asyncRegisterThreadDatabaseView:self]; [TSDatabaseView asyncRegisterUnreadDatabaseView:self]; diff --git a/SignalServiceKit/src/Storage/TSDatabaseView.h b/SignalServiceKit/src/Storage/TSDatabaseView.h index bb69f1815..d0e126dc0 100644 --- a/SignalServiceKit/src/Storage/TSDatabaseView.h +++ b/SignalServiceKit/src/Storage/TSDatabaseView.h @@ -13,6 +13,8 @@ extern NSString *const TSSecondaryDevicesGroup; extern NSString *const TSThreadDatabaseViewExtensionName; extern NSString *const TSMessageDatabaseViewExtensionName; +extern NSString *const TSMessageDatabaseViewExtensionName_Legacy; + extern NSString *const TSUnreadDatabaseViewExtensionName; extern NSString *const TSUnseenDatabaseViewExtensionName; extern NSString *const TSThreadOutgoingMessageDatabaseViewExtensionName; @@ -45,6 +47,8 @@ extern NSString *const TSLazyRestoreAttachmentsDatabaseViewExtensionName; + (void)asyncRegisterThreadDatabaseView:(OWSStorage *)storage; + (void)asyncRegisterThreadInteractionsDatabaseView:(OWSStorage *)storage; ++ (void)asyncRegisterLegacyThreadInteractionsDatabaseView:(OWSStorage *)storage; + + (void)asyncRegisterThreadOutgoingMessagesDatabaseView:(OWSStorage *)storage; // Instances of OWSReadTracking for wasRead is NO and shouldAffectUnreadCounts is YES. diff --git a/SignalServiceKit/src/Storage/TSDatabaseView.m b/SignalServiceKit/src/Storage/TSDatabaseView.m index af7d7d1db..ebc9b5a60 100644 --- a/SignalServiceKit/src/Storage/TSDatabaseView.m +++ b/SignalServiceKit/src/Storage/TSDatabaseView.m @@ -24,7 +24,18 @@ NSString *const TSSecondaryDevicesGroup = @"TSSecondaryDevicesGroup"; // YAPDB BUG: when changing from non-persistent to persistent view, we had to rename TSThreadDatabaseViewExtensionName // -> TSThreadDatabaseViewExtensionName2 to work around https://github.com/yapstudios/YapDatabase/issues/324 NSString *const TSThreadDatabaseViewExtensionName = @"TSThreadDatabaseViewExtensionName2"; -NSString *const TSMessageDatabaseViewExtensionName = @"TSMessageDatabaseViewExtensionName"; + +// We sort interactions by a monotonically increasing counter. +// +// Previously we sorted the interactions database by local timestamp, which was problematic if the local clock changed. +// We need to maintain the legacy extension for purposes of migration. +// +// The "Legacy" sorting extension name constant has the same value as always, so that it won't need to be rebuilt, while +// the "Modern" sorting extension name constant has the same symbol name as we've always used for sorting interactions, +// so that the callsites won't need to change. +NSString *const TSMessageDatabaseViewExtensionName = @"TSMessageDatabaseViewExtensionName_Monotonic"; +NSString *const TSMessageDatabaseViewExtensionName_Legacy = @"TSMessageDatabaseViewExtensionName"; + NSString *const TSThreadOutgoingMessageDatabaseViewExtensionName = @"TSThreadOutgoingMessageDatabaseViewExtensionName"; NSString *const TSUnreadDatabaseViewExtensionName = @"TSUnreadDatabaseViewExtensionName"; NSString *const TSUnseenDatabaseViewExtensionName = @"TSUnseenDatabaseViewExtensionName"; @@ -100,7 +111,7 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" [self registerMessageDatabaseViewWithName:TSUnreadDatabaseViewExtensionName viewGrouping:viewGrouping - version:@"1" + version:@"2" storage:storage]; } @@ -119,7 +130,7 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" [self registerMessageDatabaseViewWithName:TSUnseenDatabaseViewExtensionName viewGrouping:viewGrouping - version:@"1" + version:@"2" storage:storage]; } @@ -147,10 +158,77 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" [self registerMessageDatabaseViewWithName:TSThreadSpecialMessagesDatabaseViewExtensionName viewGrouping:viewGrouping - version:@"1" + version:@"2" storage:storage]; } ++ (void)asyncRegisterLegacyThreadInteractionsDatabaseView:(OWSStorage *)storage +{ + OWSAssertIsOnMainThread(); + OWSAssert(storage); + + YapDatabaseView *existingView = [storage registeredExtension:TSMessageDatabaseViewExtensionName_Legacy]; + if (existingView) { + OWSFailDebug(@"Registered database view twice: %@", TSMessageDatabaseViewExtensionName_Legacy); + return; + } + + YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *( + YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) { + if (![object isKindOfClass:[TSInteraction class]]) { + OWSFailDebug(@"%@ Unexpected entity %@ in collection: %@", self.logTag, [object class], collection); + return nil; + } + TSInteraction *interaction = (TSInteraction *)object; + + return interaction.uniqueThreadId; + }]; + + YapDatabaseViewSorting *viewSorting = + [YapDatabaseViewSorting withObjectBlock:^NSComparisonResult(YapDatabaseReadTransaction *transaction, + NSString *group, + NSString *collection1, + NSString *key1, + id object1, + NSString *collection2, + NSString *key2, + id object2) { + if (![object1 isKindOfClass:[TSInteraction class]]) { + OWSFailDebug(@"%@ Unexpected entity %@ in collection: %@", self.logTag, [object1 class], collection1); + return NSOrderedSame; + } + if (![object2 isKindOfClass:[TSInteraction class]]) { + OWSFailDebug(@"%@ Unexpected entity %@ in collection: %@", self.logTag, [object2 class], collection2); + return NSOrderedSame; + } + TSInteraction *interaction1 = (TSInteraction *)object1; + TSInteraction *interaction2 = (TSInteraction *)object2; + + // Legit usage of timestampForLegacySorting since we're registering the + // legacy extension + uint64_t timestamp1 = interaction1.timestampForLegacySorting; + uint64_t timestamp2 = interaction2.timestampForLegacySorting; + + if (timestamp1 > timestamp2) { + return NSOrderedDescending; + } else if (timestamp1 < timestamp2) { + return NSOrderedAscending; + } else { + return NSOrderedSame; + } + }]; + + YapDatabaseViewOptions *options = [YapDatabaseViewOptions new]; + options.isPersistent = YES; + options.allowedCollections = + [[YapWhitelistBlacklist alloc] initWithWhitelist:[NSSet setWithObject:[TSInteraction collection]]]; + + YapDatabaseView *view = + [[YapDatabaseAutoView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:@"1" options:options]; + + [storage asyncRegisterExtension:view withName:TSMessageDatabaseViewExtensionName_Legacy]; +} + + (void)asyncRegisterThreadInteractionsDatabaseView:(OWSStorage *)storage { YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *( @@ -166,7 +244,7 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" [self registerMessageDatabaseViewWithName:TSMessageDatabaseViewExtensionName viewGrouping:viewGrouping - version:@"1" + version:@"2" storage:storage]; } @@ -182,7 +260,7 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" [self registerMessageDatabaseViewWithName:TSThreadOutgoingMessageDatabaseViewExtensionName viewGrouping:viewGrouping - version:@"2" + version:@"3" storage:storage]; } @@ -213,13 +291,7 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" } } - if (thread.archivalDate) { - return ([self threadShouldBeInInbox:thread]) ? TSInboxGroup : TSArchiveGroup; - } else if (thread.archivalDate) { - return TSArchiveGroup; - } else { - return TSInboxGroup; - } + return [thread isArchivedWithTransaction:transaction] ? TSArchiveGroup : TSInboxGroup; }]; YapDatabaseViewSorting *viewSorting = [self threadSorting]; @@ -235,29 +307,6 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" [storage asyncRegisterExtension:databaseView withName:TSThreadDatabaseViewExtensionName]; } -/** - * Determines whether a thread belongs to the archive or inbox - * - * @param thread TSThread - * - * @return Inbox if true, Archive if false - */ - -+ (BOOL)threadShouldBeInInbox:(TSThread *)thread { - NSDate *lastMessageDate = thread.lastMessageDate; - NSDate *archivalDate = thread.archivalDate; - if (lastMessageDate && archivalDate) { // this is what is called - return ([lastMessageDate timeIntervalSinceDate:archivalDate] > 0) - ? YES - : NO; // if there hasn't been a new message since the archive date, it's in the archive. an issue is - // that empty threads are always given with a lastmessage date of the present on every launch - } else if (archivalDate) { - return NO; - } - - return YES; -} - + (YapDatabaseViewSorting *)threadSorting { return [YapDatabaseViewSorting withObjectBlock:^NSComparisonResult(YapDatabaseReadTransaction *transaction, NSString *group, @@ -278,7 +327,16 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" TSThread *thread1 = (TSThread *)object1; TSThread *thread2 = (TSThread *)object2; if ([group isEqualToString:TSArchiveGroup] || [group isEqualToString:TSInboxGroup]) { - return [thread1.lastMessageDate compare:thread2.lastMessageDate]; + + TSInteraction *_Nullable lastInteractionForInbox1 = + [thread1 lastInteractionForInboxWithTransaction:transaction]; + NSDate *date1 = lastInteractionForInbox1 ? lastInteractionForInbox1.receivedAtDate : thread1.creationDate; + + TSInteraction *_Nullable lastInteractionForInbox2 = + [thread2 lastInteractionForInboxWithTransaction:transaction]; + NSDate *date2 = lastInteractionForInbox2 ? lastInteractionForInbox2.receivedAtDate : thread2.creationDate; + + return [date1 compare:date2]; } return NSOrderedSame; @@ -425,6 +483,7 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" return result; } +// MJK TODO - dynamic interactions + (id)threadOutgoingMessageDatabaseView:(YapDatabaseReadTransaction *)transaction { OWSAssertDebug(transaction); diff --git a/SignalServiceKit/src/Storage/TSYapDatabaseObject.m b/SignalServiceKit/src/Storage/TSYapDatabaseObject.m index 2e3f4b556..488607ab3 100644 --- a/SignalServiceKit/src/Storage/TSYapDatabaseObject.m +++ b/SignalServiceKit/src/Storage/TSYapDatabaseObject.m @@ -218,6 +218,8 @@ NS_ASSUME_NONNULL_BEGIN } } +#pragma mark Reload + - (void)reload { [self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {