diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index fee9f747f..c8d531f1e 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -169,6 +169,7 @@ B8269D2925C7A4B400488AB4 /* InputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D2825C7A4B400488AB4 /* InputView.swift */; }; B8269D3325C7A8C600488AB4 /* InputViewButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D3225C7A8C600488AB4 /* InputViewButton.swift */; }; B8269D3D25C7B34D00488AB4 /* InputTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D3C25C7B34D00488AB4 /* InputTextView.swift */; }; + B82A0C3826B9098200C1BCE3 /* MessageInvalidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82A0C3726B9098200C1BCE3 /* MessageInvalidator.swift */; }; B82B40882399EB0E00A248E7 /* LandingVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B40872399EB0E00A248E7 /* LandingVC.swift */; }; B82B408A2399EC0600A248E7 /* FakeChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B40892399EC0600A248E7 /* FakeChatView.swift */; }; B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408B239A068800A248E7 /* RegisterVC.swift */; }; @@ -1149,6 +1150,7 @@ B8269D2825C7A4B400488AB4 /* InputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputView.swift; sourceTree = ""; }; B8269D3225C7A8C600488AB4 /* InputViewButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputViewButton.swift; sourceTree = ""; }; B8269D3C25C7B34D00488AB4 /* InputTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputTextView.swift; sourceTree = ""; }; + B82A0C3726B9098200C1BCE3 /* MessageInvalidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageInvalidator.swift; sourceTree = ""; }; B82B40872399EB0E00A248E7 /* LandingVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LandingVC.swift; sourceTree = ""; }; B82B40892399EC0600A248E7 /* FakeChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeChatView.swift; sourceTree = ""; }; B82B408B239A068800A248E7 /* RegisterVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterVC.swift; sourceTree = ""; }; @@ -3115,6 +3117,7 @@ C33FDBC1255A581700E217F9 /* General.swift */, B8AE760925ABFB00001A84D2 /* GeneralUtilities.h */, B8AE760A25ABFB5A001A84D2 /* GeneralUtilities.m */, + B82A0C3726B9098200C1BCE3 /* MessageInvalidator.swift */, C3A71D0A2558989C0043A11F /* MessageWrapper.swift */, C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */, C3A71D4825589FF20043A11F /* NSData+messagePadding.m */, @@ -4746,6 +4749,7 @@ C32C5C01256DC9A0003C73A2 /* OWSIdentityManager.m in Sources */, C32C59C4256DB41F003C73A2 /* TSContactThread.m in Sources */, C32C5AB0256DBE8F003C73A2 /* TSOutgoingMessage.m in Sources */, + B82A0C3826B9098200C1BCE3 /* MessageInvalidator.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Session/Conversations/ConversationVC.swift b/Session/Conversations/ConversationVC.swift index 6d8b152ed..67039536c 100644 --- a/Session/Conversations/ConversationVC.swift +++ b/Session/Conversations/ConversationVC.swift @@ -368,9 +368,9 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat shouldScrollToBottom = self.isCloseToBottom } case .update: - // FIXME: This is called many times when a message is inserted, leading to bad performance + shouldScrollToBottom = self.isCloseToBottom // Check this * before * reloading the row + print("[Test] UPDATE") self.messagesTableView.reloadRows(at: [ IndexPath(row: Int(update.oldIndex), section: 0) ], with: .none) - shouldScrollToBottom = self.isCloseToBottom default: preconditionFailure() } } diff --git a/Session/Conversations/ConversationViewModel.m b/Session/Conversations/ConversationViewModel.m index 55e1baa18..588711011 100644 --- a/Session/Conversations/ConversationViewModel.m +++ b/Session/Conversations/ConversationViewModel.m @@ -822,9 +822,13 @@ NS_ASSUME_NONNULL_BEGIN OWSFailDebug(@"Can't find holdover view item."); return [self.delegate conversationViewModelDidUpdate:ConversationUpdate.reloadUpdate]; } - if (!viewItem.hasCachedLayoutState) { - [updatedItemSet addObject:itemId]; - [updatedNeighborItemSet addObject:itemId]; + + if ([viewItem.interaction isKindOfClass:TSMessage.class]) { + TSMessage *message = (TSMessage *)viewItem.interaction; + if ([MessageInvalidator isInvalidated:message]) { + [updatedItemSet addObject:itemId]; + [updatedNeighborItemSet addObject:itemId]; + } } } @@ -864,6 +868,10 @@ NS_ASSUME_NONNULL_BEGIN oldViewItemCount:oldItemIdList.count updatedNeighborItemSet:updatedNeighborItemSet]; + for (NSString *itemID in updatedItemSet) { + [MessageInvalidator markAsUpdated:itemID]; + } + return [self.delegate conversationViewModelDidUpdate:[ConversationUpdate diffUpdateWithUpdateItems:updateItems shouldAnimateUpdates:shouldAnimateUpdates]]; diff --git a/Session/Conversations/Settings/OWSConversationSettingsViewController.m b/Session/Conversations/Settings/OWSConversationSettingsViewController.m index 3d303ca9d..82b8166b5 100644 --- a/Session/Conversations/Settings/OWSConversationSettingsViewController.m +++ b/Session/Conversations/Settings/OWSConversationSettingsViewController.m @@ -1002,7 +1002,7 @@ CGFloat kIconViewLength = 24; UISwitch *uiSwitch = (UISwitch *)sender; BOOL isEnabled = uiSwitch.isOn; [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [(TSGroupThread *)self.thread setisOnlyNotifyingForMentions:isEnabled withTransaction:transaction]; + [(TSGroupThread *)self.thread setIsOnlyNotifyingForMentions:isEnabled withTransaction:transaction]; }]; } diff --git a/Session/Conversations/Views & Modals/DownloadAttachmentModal.swift b/Session/Conversations/Views & Modals/DownloadAttachmentModal.swift index 402c32032..1c8c88842 100644 --- a/Session/Conversations/Views & Modals/DownloadAttachmentModal.swift +++ b/Session/Conversations/Views & Modals/DownloadAttachmentModal.swift @@ -70,7 +70,7 @@ final class DownloadAttachmentModal : Modal { contact.isTrusted = true Storage.write(with: { transaction in Storage.shared.setContact(contact, using: transaction) - message.touch(with: transaction) + MessageInvalidator.invalidate(message, with: transaction) }, completion: { Storage.shared.resumeAttachmentDownloadJobsIfNeeded(for: message.uniqueThreadId) }) diff --git a/SessionMessagingKit/Database/Storage+Messaging.swift b/SessionMessagingKit/Database/Storage+Messaging.swift index 21e8d40e5..0a6a84f99 100644 --- a/SessionMessagingKit/Database/Storage+Messaging.swift +++ b/SessionMessagingKit/Database/Storage+Messaging.swift @@ -67,7 +67,7 @@ extension Storage { pointer.state = state pointer.save(with: transaction) guard let tsMessage = TSMessage.fetch(uniqueId: tsMessageID, transaction: transaction) else { return } - tsMessage.touch(with: transaction) + MessageInvalidator.invalidate(tsMessage, with: transaction) } /// Also touches the associated message. @@ -75,7 +75,7 @@ extension Storage { let transaction = transaction as! YapDatabaseReadWriteTransaction stream.save(with: transaction) guard let tsMessage = TSMessage.fetch(uniqueId: tsMessageID, transaction: transaction) else { return } - tsMessage.touch(with: transaction) + MessageInvalidator.invalidate(tsMessage, with: transaction) } private static let receivedMessageTimestampsCollection = "ReceivedMessageTimestampsCollection" diff --git a/SessionMessagingKit/Jobs/AttachmentUploadJob.swift b/SessionMessagingKit/Jobs/AttachmentUploadJob.swift index e3f9a1df6..401824b69 100644 --- a/SessionMessagingKit/Jobs/AttachmentUploadJob.swift +++ b/SessionMessagingKit/Jobs/AttachmentUploadJob.swift @@ -118,12 +118,14 @@ public final class AttachmentUploadJob : NSObject, Job, NSCoding { // NSObject/N delegate?.handleJobSucceeded(self) SNMessagingKitConfiguration.shared.storage.resumeMessageSendJobIfNeeded(messageSendJobID) Storage.shared.write(with: { transaction in - var interaction: TSInteraction? + var message: TSMessage? let transaction = transaction as! YapDatabaseReadWriteTransaction TSDatabaseSecondaryIndexes.enumerateMessages(withTimestamp: self.message.sentTimestamp!, with: { _, key, _ in - interaction = TSInteraction.fetch(uniqueId: key, transaction: transaction) + message = TSMessage.fetch(uniqueId: key, transaction: transaction) }, using: transaction) - interaction?.touch(with: transaction) // To refresh the associated message cell and hide the loader + if let message = message { + MessageInvalidator.invalidate(message, with: transaction) + } }, completion: { }) } diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender.swift b/SessionMessagingKit/Sending & Receiving/MessageSender.swift index 456ba6e23..8b4cfdff0 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender.swift @@ -340,6 +340,7 @@ public final class MessageSender : NSObject { recipients.forEach { recipient in tsMessage.update(withSentRecipient: recipient, wasSentByUD: true, transaction: transaction) } + MessageInvalidator.invalidate(tsMessage, with: transaction) // Start the disappearing messages timer if needed OWSDisappearingMessagesJob.shared().startAnyExpiration(for: tsMessage, expirationStartedAt: NSDate.millisecondTimestamp(), transaction: transaction) } diff --git a/SessionMessagingKit/Threads/TSGroupThread.h b/SessionMessagingKit/Threads/TSGroupThread.h index 0599d0461..09f551854 100644 --- a/SessionMessagingKit/Threads/TSGroupThread.h +++ b/SessionMessagingKit/Threads/TSGroupThread.h @@ -45,7 +45,7 @@ extern NSString *const TSGroupThread_NotificationKey_UniqueId; transaction:(YapDatabaseReadWriteTransaction *)transaction; - (void)setGroupModel:(TSGroupModel *)newGroupModel withTransaction:(YapDatabaseReadWriteTransaction *)transaction; -- (void)setisOnlyNotifyingForMentions:(BOOL)isOnlyNotifyingForMentions withTransaction:(YapDatabaseReadWriteTransaction *)transaction; +- (void)setIsOnlyNotifyingForMentions:(BOOL)isOnlyNotifyingForMentions withTransaction:(YapDatabaseReadWriteTransaction *)transaction; - (void)leaveGroupWithSneakyTransaction; - (void)leaveGroupWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; diff --git a/SessionMessagingKit/Threads/TSGroupThread.m b/SessionMessagingKit/Threads/TSGroupThread.m index f05d42732..382f6ed5e 100644 --- a/SessionMessagingKit/Threads/TSGroupThread.m +++ b/SessionMessagingKit/Threads/TSGroupThread.m @@ -208,7 +208,7 @@ NSString *const TSGroupThread_NotificationKey_UniqueId = @"TSGroupThread_Notific }]; } -- (void)setisOnlyNotifyingForMentions:(BOOL)isOnlyNotifyingForMentions withTransaction:(YapDatabaseReadWriteTransaction *)transaction +- (void)setIsOnlyNotifyingForMentions:(BOOL)isOnlyNotifyingForMentions withTransaction:(YapDatabaseReadWriteTransaction *)transaction { self.isOnlyNotifyingForMentions = isOnlyNotifyingForMentions; diff --git a/SessionMessagingKit/Utilities/MessageInvalidator.swift b/SessionMessagingKit/Utilities/MessageInvalidator.swift new file mode 100644 index 000000000..83619e091 --- /dev/null +++ b/SessionMessagingKit/Utilities/MessageInvalidator.swift @@ -0,0 +1,27 @@ + +/// A message is invalidated when it needs to be re-rendered in the UI. Examples of when this happens include: +/// +/// • When the sent or read status of a message is updated. +/// • When an attachment is uploaded or downloaded. +@objc public final class MessageInvalidator : NSObject { + private static var invalidatedMessages: Set = [] + + @objc public static let shared = MessageInvalidator() + + private override init() { } + + @objc public static func invalidate(_ message: TSMessage, with transaction: YapDatabaseReadWriteTransaction) { + guard let id = message.uniqueId else { return } + invalidatedMessages.insert(id) + message.touch(with: transaction) + } + + @objc public static func isInvalidated(_ message: TSMessage) -> Bool { + guard let id = message.uniqueId else { return false } + return invalidatedMessages.contains(id) + } + + @objc public static func markAsUpdated(_ id: String) { + invalidatedMessages.remove(id) + } +}