diff --git a/SignalMessaging/environment/migrations/OWS110SortIdMigration.swift b/SignalMessaging/environment/migrations/OWS110SortIdMigration.swift index cefb8dc76..c61681788 100644 --- a/SignalMessaging/environment/migrations/OWS110SortIdMigration.swift +++ b/SignalMessaging/environment/migrations/OWS110SortIdMigration.swift @@ -59,9 +59,6 @@ class OWS110SortIdMigration: OWSDatabaseMigration { Logger.info("re-archiving \(archivedThreads.count) threads which were previously archived") for archivedThread in archivedThreads { - // latestMessageSortId will have been modified by saving all - // the interactions above, make sure we get the latest value. - archivedThread.reload(with: transaction) archivedThread.archiveThread(with: transaction) } } diff --git a/SignalMessaging/utils/ConversationSearcher.swift b/SignalMessaging/utils/ConversationSearcher.swift index a19dd7828..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 = thread.latestMessageSortId + 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 { diff --git a/SignalServiceKit/src/Contacts/TSThread.h b/SignalServiceKit/src/Contacts/TSThread.h index 5f1d1ed36..db41b93ee 100644 --- a/SignalServiceKit/src/Contacts/TSThread.h +++ b/SignalServiceKit/src/Contacts/TSThread.h @@ -72,12 +72,6 @@ NS_ASSUME_NONNULL_BEGIN - (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; -/** - * @return the latest sortId of a message in the thread or 0 if there are no messages in that - * thread. - */ -@property (nonatomic, readonly) uint64_t latestMessageSortId; - /** * Returns the string that will be displayed typically in a conversations view as a preview of the last message *received in this thread. @@ -103,7 +97,7 @@ NS_ASSUME_NONNULL_BEGIN /** * @return YES if no new messages have been sent or received since the thread was last archived. */ -- (BOOL)isArchived; +- (BOOL)isArchivedWithTransaction:(YapDatabaseReadTransaction *)transaction; /** * Archives a thread diff --git a/SignalServiceKit/src/Contacts/TSThread.m b/SignalServiceKit/src/Contacts/TSThread.m index cf8cc7b17..8b2c1c7c5 100644 --- a/SignalServiceKit/src/Contacts/TSThread.m +++ b/SignalServiceKit/src/Contacts/TSThread.m @@ -15,6 +15,7 @@ #import "TSInteraction.h" #import "TSInvalidIdentityKeyReceivingErrorMessage.h" #import "TSOutgoingMessage.h" +#import #import NS_ASSUME_NONNULL_BEGIN @@ -24,8 +25,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) NSDate *creationDate; @property (nonatomic, nullable) NSString *conversationColorName; -@property (nonatomic) uint64_t latestMessageSortId; -@property (nonatomic) uint64_t archivedAsOfMessageSortId; +@property (nonatomic) NSNumber *archivedAsOfMessageSortId; @property (nonatomic, copy, nullable) NSString *messageDraft; @property (atomic, nullable) NSDate *mutedUntilDate; @@ -350,9 +350,8 @@ NS_ASSUME_NONNULL_BEGIN return; } - self.hasEverHadMessage = YES; - if (lastMessage.sortId > self.latestMessageSortId) { - self.latestMessageSortId = lastMessage.sortId; + if (!self.hasEverHadMessage) { + self.hasEverHadMessage = YES; [self saveWithTransaction:transaction]; } } @@ -379,9 +378,15 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Archival -- (BOOL)isArchived +- (BOOL)isArchivedWithTransaction:(YapDatabaseReadTransaction *)transaction; { - return self.archivedAsOfMessageSortId >= self.latestMessageSortId; + if (!self.archivedAsOfMessageSortId) { + return NO; + } + + TSInteraction *_Nullable latestInteraction = [self lastInteractionForInboxWithTransaction:transaction]; + uint64_t latestSortIdForInbox = latestInteraction ? latestInteraction.sortId : 0; + return self.archivedAsOfMessageSortId.unsignedLongLongValue >= latestSortIdForInbox; } + (BOOL)legacyIsArchivedWithLastMessageDate:(nullable NSDate *)lastMessageDate @@ -402,7 +407,9 @@ NS_ASSUME_NONNULL_BEGIN { [self applyChangeToSelfAndLatestCopy:transaction changeBlock:^(TSThread *thread) { - thread.archivedAsOfMessageSortId = self.latestMessageSortId; + uint64_t latestId = [SSKIncrementingIdFinder previousIdWithKey:TSInteraction.collection + transaction:transaction]; + thread.archivedAsOfMessageSortId = @(latestId); }]; [self markAllAsReadWithTransaction:transaction]; @@ -412,7 +419,7 @@ NS_ASSUME_NONNULL_BEGIN { [self applyChangeToSelfAndLatestCopy:transaction changeBlock:^(TSThread *thread) { - thread.archivedAsOfMessageSortId = 0; + thread.archivedAsOfMessageSortId = nil; }]; } diff --git a/SignalServiceKit/src/Storage/IncrementingIdFinder.swift b/SignalServiceKit/src/Storage/IncrementingIdFinder.swift index a3936e428..d8f06970c 100644 --- a/SignalServiceKit/src/Storage/IncrementingIdFinder.swift +++ b/SignalServiceKit/src/Storage/IncrementingIdFinder.swift @@ -9,6 +9,12 @@ 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 diff --git a/SignalServiceKit/src/Storage/TSDatabaseView.m b/SignalServiceKit/src/Storage/TSDatabaseView.m index 4e1b4435e..1d6419d6e 100644 --- a/SignalServiceKit/src/Storage/TSDatabaseView.m +++ b/SignalServiceKit/src/Storage/TSDatabaseView.m @@ -293,7 +293,7 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" } } - return thread.isArchived ? TSArchiveGroup : TSInboxGroup; + return [thread isArchivedWithTransaction:transaction] ? TSArchiveGroup : TSInboxGroup; }]; YapDatabaseViewSorting *viewSorting = [self threadSorting]; @@ -304,7 +304,7 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" [[YapWhitelistBlacklist alloc] initWithWhitelist:[NSSet setWithObject:[TSThread collection]]]; YapDatabaseView *databaseView = - [[YapDatabaseAutoView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:@"3" options:options]; + [[YapDatabaseAutoView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:@"4" options:options]; [storage asyncRegisterExtension:databaseView withName:TSThreadDatabaseViewExtensionName]; } @@ -329,11 +329,16 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup" TSThread *thread1 = (TSThread *)object1; TSThread *thread2 = (TSThread *)object2; if ([group isEqualToString:TSArchiveGroup] || [group isEqualToString:TSInboxGroup]) { - if (thread1.latestMessageSortId == 0 && thread2.latestMessageSortId == 0) { - return [thread1.creationDate compare:thread2.creationDate]; - } - return [@(thread1.latestMessageSortId) compare:@(thread2.latestMessageSortId)]; + 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;