From 1fb1b5bbe27c6dd5b4657af743ac342327b3c229 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Sat, 21 Apr 2018 10:25:13 -0400 Subject: [PATCH 01/10] WIP unjank home screen verify `description` isn't used elsewhere (notifications? conversation view?) Probably want to remove overzealous asserts for now, but would be good to work towards leaving them enabled. // FREEBIE --- .../ViewControllers/HomeView/HomeViewCell.h | 4 +- .../ViewControllers/HomeView/HomeViewCell.m | 9 +++- .../HomeView/HomeViewController.m | 33 ++++++++++--- SignalServiceKit/src/Contacts/TSThread.h | 2 +- SignalServiceKit/src/Contacts/TSThread.m | 47 ++++++++++--------- ...sappearingConfigurationUpdateInfoMessage.m | 2 +- .../Messages/Interactions/TSErrorMessage.m | 3 +- .../src/Messages/Interactions/TSInfoMessage.m | 3 +- .../src/Messages/Interactions/TSInteraction.h | 6 +++ .../src/Messages/Interactions/TSInteraction.m | 1 + .../src/Messages/Interactions/TSMessage.h | 3 +- .../src/Messages/Interactions/TSMessage.m | 9 ++-- SignalServiceKit/src/Messages/TSCall.m | 16 ++++++- .../src/Storage/TSYapDatabaseObject.m | 5 +- 14 files changed, 96 insertions(+), 47 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.h b/Signal/src/ViewControllers/HomeView/HomeViewCell.h index 4e51d15fc..f5450b05c 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.h +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.h @@ -6,6 +6,7 @@ NS_ASSUME_NONNULL_BEGIN @class OWSContactsManager; @class TSThread; +@class YapDatabaseReadTransaction; @interface HomeViewCell : UITableViewCell @@ -15,7 +16,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)configureWithThread:(TSThread *)thread contactsManager:(OWSContactsManager *)contactsManager - blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet; + blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet + transaction:(YapDatabaseReadTransaction *)transaction; @end diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index 7281b65b2..4573a734f 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -146,11 +146,13 @@ NS_ASSUME_NONNULL_BEGIN - (void)configureWithThread:(TSThread *)thread contactsManager:(OWSContactsManager *)contactsManager blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet + transaction:(YapDatabaseReadTransaction *)transaction { OWSAssertIsOnMainThread(); OWSAssert(thread); OWSAssert(contactsManager); OWSAssert(blockedPhoneNumberSet); + OWSAssert(transaction); self.thread = thread; self.contactsManager = contactsManager; @@ -171,7 +173,9 @@ NS_ASSUME_NONNULL_BEGIN // changes to the dynamic type settings are reflected. self.snippetLabel.font = [self snippetFont]; self.snippetLabel.attributedText = - [self attributedSnippetForThread:thread blockedPhoneNumberSet:blockedPhoneNumberSet]; + [self attributedSnippetForThread:thread + blockedPhoneNumberSet:blockedPhoneNumberSet + transaction:transaction]; self.dateTimeLabel.text = [self stringForDate:thread.lastMessageDate]; @@ -253,6 +257,7 @@ NS_ASSUME_NONNULL_BEGIN - (NSAttributedString *)attributedSnippetForThread:(TSThread *)thread blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet + transaction:(YapDatabaseReadTransaction *)transaction { OWSAssert(thread); @@ -285,7 +290,7 @@ NS_ASSUME_NONNULL_BEGIN : [UIColor lightGrayColor]), }]]; } - NSString *displayableText = thread.lastMessageLabel.filterStringForDisplay; + NSString *displayableText = [thread lastMessageLabelWithTransaction:transaction]; if (displayableText) { [snippetText appendAttributedString:[[NSAttributedString alloc] initWithString:displayableText diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index fec465118..61d58c6a7 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -395,7 +395,11 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - if ([TSThread numberOfKeysInCollection] > 0) { + __block BOOL hasAnyMessages; + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { + hasAnyMessages = [self hasAnyMessagesWithTransaction:transaction]; + }]; + if (hasAnyMessages) { [self.contactsManager requestSystemContactsOnceWithCompletion:^(NSError *_Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{ [self updateReminderViews]; @@ -490,7 +494,11 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; // If the user hasn't already granted contact access // we don't want to request until they receive a message. - if ([TSThread numberOfKeysInCollection] > 0) { + __block BOOL hasAnyMessages; + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { + hasAnyMessages = [self hasAnyMessagesWithTransaction:transaction]; + }]; + if (hasAnyMessages) { [self.contactsManager requestSystemContactsOnce]; } } @@ -506,11 +514,21 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; self.isAppInBackground = YES; } +- (BOOL)hasAnyMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction +{ + return [TSThread numberOfKeysInCollectionWithTransaction:transaction] > 0; +} + - (void)applicationDidBecomeActive:(NSNotification *)notification { // It's possible a thread was created while we where in the background. But since we don't honor contact // requests unless the app is in the foregrond, we must check again here upon becoming active. - if ([TSThread numberOfKeysInCollection] > 0) { + __block BOOL hasAnyMessages; + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { + hasAnyMessages = [self hasAnyMessagesWithTransaction:transaction]; + }]; + + if (hasAnyMessages) { [self.contactsManager requestSystemContactsOnceWithCompletion:^(NSError *_Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{ [self updateReminderViews]; @@ -584,9 +602,12 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; TSThread *thread = [self threadForIndexPath:indexPath]; - [cell configureWithThread:thread - contactsManager:self.contactsManager - blockedPhoneNumberSet:self.blockedPhoneNumberSet]; + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { + [cell configureWithThread:thread + contactsManager:self.contactsManager + blockedPhoneNumberSet:self.blockedPhoneNumberSet + transaction:transaction]; + }]; if ((unsigned long)indexPath.row == [self.threadMappings numberOfItemsInSection:0] - 1) { cell.separatorInset = UIEdgeInsetsMake(0.f, cell.bounds.size.width, 0.f, 0.f); diff --git a/SignalServiceKit/src/Contacts/TSThread.h b/SignalServiceKit/src/Contacts/TSThread.h index 8ed22a61f..ca952d0d8 100644 --- a/SignalServiceKit/src/Contacts/TSThread.h +++ b/SignalServiceKit/src/Contacts/TSThread.h @@ -90,7 +90,7 @@ NS_ASSUME_NONNULL_BEGIN * * @return Thread preview string. */ -- (NSString *)lastMessageLabel; +- (NSString *)lastMessageLabelWithTransaction:(YapDatabaseReadTransaction *)transaction; /** * Updates the thread's caches of the latest interaction. diff --git a/SignalServiceKit/src/Contacts/TSThread.m b/SignalServiceKit/src/Contacts/TSThread.m index f9cc5fe4c..aa5f60cc0 100644 --- a/SignalServiceKit/src/Contacts/TSThread.m +++ b/SignalServiceKit/src/Contacts/TSThread.m @@ -4,6 +4,7 @@ #import "TSThread.h" #import "NSDate+OWS.h" +#import "NSString+SSK.h" #import "OWSPrimaryStorage.h" #import "OWSReadTracking.h" #import "TSDatabaseView.h" @@ -246,26 +247,24 @@ NS_ASSUME_NONNULL_BEGIN return last; } -- (TSInteraction *)lastInteractionForInbox +- (TSInteraction *)lastInteractionForInboxWithTransaction:(YapDatabaseReadTransaction *)transaction { __block TSInteraction *last = nil; - [OWSPrimaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - [[transaction ext:TSMessageDatabaseViewExtensionName] - enumerateRowsInGroup:self.uniqueId - withOptions:NSEnumerationReverse - usingBlock:^( - NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) { - - OWSAssert([object isKindOfClass:[TSInteraction class]]); - - TSInteraction *interaction = (TSInteraction *)object; - - if ([TSThread shouldInteractionAppearInInbox:interaction]) { - last = interaction; - *stop = YES; - } - }]; - }]; + [[transaction ext:TSMessageDatabaseViewExtensionName] + enumerateRowsInGroup:self.uniqueId + withOptions:NSEnumerationReverse + usingBlock:^( + NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) { + + OWSAssert([object isKindOfClass:[TSInteraction class]]); + + TSInteraction *interaction = (TSInteraction *)object; + + if ([TSThread shouldInteractionAppearInInbox:interaction]) { + last = interaction; + *stop = YES; + } + }]; return last; } @@ -277,12 +276,14 @@ NS_ASSUME_NONNULL_BEGIN } } -- (NSString *)lastMessageLabel { - TSInteraction *interaction = self.lastInteractionForInbox; - if (interaction == nil) { - return @""; +- (NSString *)lastMessageLabelWithTransaction:(YapDatabaseReadTransaction *)transaction +{ + TSInteraction *interaction = [self lastInteractionForInboxWithTransaction:transaction]; + if ([interaction conformsToProtocol:@protocol(OWSPreviewText)]) { + id previewable = (id)interaction; + return [previewable previewTextWithTransaction:transaction].filterStringForDisplay; } else { - return interaction.description; + return @""; } } diff --git a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m index 1391aa271..8ae97209d 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSDisappearingConfigurationUpdateInfoMessage.m @@ -56,7 +56,7 @@ NS_ASSUME_NONNULL_BEGIN return NO; } -- (NSString *)description +-(NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction { if (self.createdByRemoteName) { if (self.configurationIsEnabled && self.configurationDurationSeconds > 0) { diff --git a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m index 363605a73..1f130877a 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSErrorMessage.m @@ -97,7 +97,8 @@ NSUInteger TSErrorMessageSchemaVersion = 1; return OWSInteractionType_Error; } -- (NSString *)description { +- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction +{ switch (_errorType) { case TSErrorMessageNoSession: return NSLocalizedString(@"ERROR_MESSAGE_NO_SESSION", @""); diff --git a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m index 10abeb18b..5e19dd446 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSInfoMessage.m @@ -91,7 +91,8 @@ NSUInteger TSInfoMessageSchemaVersion = 1; return OWSInteractionType_Info; } -- (NSString *)description { +- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction +{ switch (_messageType) { case TSInfoMessageTypeSessionDidEnd: return NSLocalizedString(@"SECURE_SESSION_RESET", nil); diff --git a/SignalServiceKit/src/Messages/Interactions/TSInteraction.h b/SignalServiceKit/src/Messages/Interactions/TSInteraction.h index 67044af1f..a6088803d 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInteraction.h +++ b/SignalServiceKit/src/Messages/Interactions/TSInteraction.h @@ -19,6 +19,12 @@ typedef NS_ENUM(NSInteger, OWSInteractionType) { OWSInteractionType_Offer, }; +@protocol OWSPreviewText + +- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction; + +@end + @interface TSInteraction : TSYapDatabaseObject - (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread; diff --git a/SignalServiceKit/src/Messages/Interactions/TSInteraction.m b/SignalServiceKit/src/Messages/Interactions/TSInteraction.m index b5319d9d0..fc796947e 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInteraction.m +++ b/SignalServiceKit/src/Messages/Interactions/TSInteraction.m @@ -128,6 +128,7 @@ NS_ASSUME_NONNULL_BEGIN } - (NSString *)description { + OWSFail(@"Abstract Method"); return @"Interaction description"; } diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.h b/SignalServiceKit/src/Messages/Interactions/TSMessage.h index f08f13acf..66c359804 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.h @@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN @class TSQuotedMessage; @class YapDatabaseReadWriteTransaction; -@interface TSMessage : TSInteraction +@interface TSMessage : TSInteraction @property (nonatomic, readonly) NSMutableArray *attachmentIds; @property (nonatomic, readonly, nullable) NSString *body; @@ -39,7 +39,6 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)hasAttachments; - (nullable TSAttachment *)attachmentWithTransaction:(YapDatabaseReadTransaction *)transaction; -- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction; - (void)setQuotedMessageThumbnailAttachmentStream:(TSAttachmentStream *)attachmentStream; - (BOOL)shouldStartExpireTimer; diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index 558af4613..da06a35ab 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -265,14 +265,11 @@ static const NSUInteger OWSMessageSchemaVersion = 4; } } -// TODO deprecate this and implement something like previewTextWithTransaction: for all TSInteractions - (NSString *)description { - __block NSString *result; - [self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - result = [self previewTextWithTransaction:transaction]; - }]; - return result; + // TODO verify this isn't exposed in the UI + OWSFail(@"%@ in %s verify this isn't being used anywhere except logging.", self.logTag, __PRETTY_FUNCTION__); + return [super description]; } - (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction diff --git a/SignalServiceKit/src/Messages/TSCall.m b/SignalServiceKit/src/Messages/TSCall.m index 7b37efb3a..cc49de57a 100644 --- a/SignalServiceKit/src/Messages/TSCall.m +++ b/SignalServiceKit/src/Messages/TSCall.m @@ -67,7 +67,13 @@ NSUInteger TSCallCurrentSchemaVersion = 1; return OWSInteractionType_Call; } -- (NSString *)description { +- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction +{ + return [self previewText]; +} + +- (NSString *)previewText +{ switch (_callType) { case RPRecentCallTypeIncoming: return NSLocalizedString(@"INCOMING_CALL", @""); @@ -83,10 +89,16 @@ NSUInteger TSCallCurrentSchemaVersion = 1; return NSLocalizedString(@"INFO_MESSAGE_MISSED_CALL_DUE_TO_CHANGED_IDENITY", @"info message text shown in conversation view"); case RPRecentCallTypeIncomingDeclined: return NSLocalizedString(@"INCOMING_DECLINED_CALL", - @"info message recorded in conversation history when local user declined a call"); + @"info message recorded in conversation history when local user declined a call"); } } +- (NSString *)description +{ + OWSFail(@"%@ in %s verify this isnt exposed in the UI", self.logTag, __PRETTY_FUNCTION__); + return [self previewText]; +} + #pragma mark - OWSReadTracking - (uint64_t)expireStartedAt diff --git a/SignalServiceKit/src/Storage/TSYapDatabaseObject.m b/SignalServiceKit/src/Storage/TSYapDatabaseObject.m index f9eafec5f..a11ac3d4e 100644 --- a/SignalServiceKit/src/Storage/TSYapDatabaseObject.m +++ b/SignalServiceKit/src/Storage/TSYapDatabaseObject.m @@ -109,17 +109,20 @@ NS_ASSUME_NONNULL_BEGIN + (YapDatabaseConnection *)dbReadConnection { + OWSAssert(![NSThread isMainThread]); + // We use TSYapDatabaseObject's dbReadWriteConnection (not OWSPrimaryStorage's // dbReadConnection) for consistency, since we tend to [TSYapDatabaseObject // save] and want to write to the same connection we read from. To get true // consistency, we'd want to update entities by reading & writing from within // the same transaction, but that'll be a big refactor. - return self.dbReadWriteConnection; } + (YapDatabaseConnection *)dbReadWriteConnection { + OWSAssert(![NSThread isMainThread]); + // Use a dedicated connection for model reads & writes. static YapDatabaseConnection *dbReadWriteConnection = nil; static dispatch_once_t onceToken; From 5f2b38c50b88a71f57b13c87b38effef29a2c9e2 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Sat, 21 Apr 2018 11:12:58 -0400 Subject: [PATCH 02/10] Introduce Thread view model // FREEBIE --- Signal.xcodeproj/project.pbxproj | 4 + Signal/src/Models/ThreadModel.swift | 101 ++++++++++++++++++ .../ViewControllers/HomeView/HomeViewCell.h | 7 +- .../ViewControllers/HomeView/HomeViewCell.m | 31 +++--- .../HomeView/HomeViewController.m | 22 ++-- SignalServiceKit/src/Contacts/TSThread.h | 8 +- SignalServiceKit/src/Contacts/TSThread.m | 23 ++-- .../src/Contacts/Threads/TSContactThread.m | 1 + .../src/Messages/OWSMessageUtils.h | 1 - .../src/Messages/OWSMessageUtils.m | 8 -- 10 files changed, 155 insertions(+), 51 deletions(-) create mode 100644 Signal/src/Models/ThreadModel.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 8e4efe097..c8baf2705 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -302,6 +302,7 @@ 45360B901F9527DA00FA666C /* SearcherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45360B8F1F9527DA00FA666C /* SearcherTest.swift */; }; 45360B911F952AA900FA666C /* MarqueeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */; }; 4539B5861F79348F007141FF /* PushRegistrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4539B5851F79348F007141FF /* PushRegistrationManager.swift */; }; + 4542DF52208B82E9007B4E76 /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4542DF51208B82E9007B4E76 /* ThreadModel.swift */; }; 45464DBC1DFA041F001D3FD6 /* DataChannelMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45464DBB1DFA041F001D3FD6 /* DataChannelMessage.swift */; }; 454A84042059C787008B8C75 /* MediaTileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 454A84032059C787008B8C75 /* MediaTileViewController.swift */; }; 454A965A1FD6017E008D2A0E /* SignalAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D913491F62D4A500722898 /* SignalAttachment.swift */; }; @@ -919,6 +920,7 @@ 45360B8F1F9527DA00FA666C /* SearcherTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearcherTest.swift; sourceTree = ""; }; 4539B5851F79348F007141FF /* PushRegistrationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushRegistrationManager.swift; sourceTree = ""; }; 453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = ""; }; + 4542DF51208B82E9007B4E76 /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = ""; }; 45464DBB1DFA041F001D3FD6 /* DataChannelMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataChannelMessage.swift; sourceTree = ""; }; 454A84032059C787008B8C75 /* MediaTileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaTileViewController.swift; sourceTree = ""; }; 454A965E1FD60EA2008D2A0E /* OWSFlatButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSFlatButton.swift; path = SignalMessaging/Views/OWSFlatButton.swift; sourceTree = SOURCE_ROOT; }; @@ -1898,6 +1900,7 @@ 45DF5DF11DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift */, 458E38351D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.h */, 458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */, + 4542DF51208B82E9007B4E76 /* ThreadModel.swift */, ); path = Models; sourceTree = ""; @@ -3199,6 +3202,7 @@ 34D1F0501F7D45A60066283D /* GifPickerCell.swift in Sources */, 34D99C931F2937CC00D284D6 /* OWSAnalytics.swift in Sources */, 340FC8B8204DAC8D007AEB0F /* AddToGroupViewController.m in Sources */, + 4542DF52208B82E9007B4E76 /* ThreadModel.swift in Sources */, 341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */, 340FC8AF204DAC8D007AEB0F /* OWSLinkDeviceViewController.m in Sources */, 34E3EF0D1EFC235B007F6822 /* DebugUIDiskUsage.m in Sources */, diff --git a/Signal/src/Models/ThreadModel.swift b/Signal/src/Models/ThreadModel.swift new file mode 100644 index 000000000..7e5bc685b --- /dev/null +++ b/Signal/src/Models/ThreadModel.swift @@ -0,0 +1,101 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +@objc +public class ThreadModel: NSObject { + let hasUnreadMessages: Bool + let lastMessageDate: Date + let isGroupThread: Bool + let threadRecord: TSThread + let unreadCount: UInt + let contactIdentifier: String? + let name: String + let isMuted: Bool + var isContactThread: Bool { + return !isGroupThread + } + + let lastMessageText: String? + +// func attributedSnippet(blockedPhoneNumberSet: Set) { +// let isBlocked: Bool = { +// guard let contactIdentifier = self.contactIdentifier else { +// return false +// } +// assert(isContactThread) +// return blockedPhoneNumberSet.contains(self.contactIdentifier) +// }() +// +// +// +//// BOOL hasUnreadMessages = thread.hasUnreadMessages; +// +//// NSMutableAttributedString *snippetText = [NSMutableAttributedString new]; +// var snippetText = NSMutableAttributedString() +// if isBlocked { +// // If thread is blocked, don't show a snippet or mute status. +// let append = NSAttributedString(string: NSLocalizedString("HOME_VIEW_BLOCKED_CONTACT_CONVERSATION", +// comment: "A label for conversations with blocked users."), +// attributes: <#T##[String : Any]?#>) +// +//// if (isBlocked) { +//// // If thread is blocked, don't show a snippet or mute status. +//// [snippetText +//// appendAttributedString:[[NSAttributedString alloc] +//// initWithString:NSLocalizedString(@"HOME_VIEW_BLOCKED_CONTACT_CONVERSATION", +//// @"A label for conversations with blocked users.") +//// attributes:@{ +//// NSFontAttributeName : self.snippetFont.ows_mediumWeight, +//// NSForegroundColorAttributeName : [UIColor ows_blackColor], +//// }]]; +//// } else { +//// if ([thread isMuted]) { +//// [snippetText appendAttributedString:[[NSAttributedString alloc] +//// initWithString:@"\ue067 " +//// attributes:@{ +//// NSFontAttributeName : [UIFont ows_elegantIconsFont:9.f], +//// NSForegroundColorAttributeName : (hasUnreadMessages +//// ? [UIColor colorWithWhite:0.1f alpha:1.f] +//// : [UIColor lightGrayColor]), +//// }]]; +//// } +//// NSString *displayableText = thread.lastMessageText; +//// if (displayableText) { +//// [snippetText appendAttributedString:[[NSAttributedString alloc] +//// initWithString:displayableText +//// attributes:@{ +//// NSFontAttributeName : +//// (hasUnreadMessages ? self.snippetFont.ows_mediumWeight +//// : self.snippetFont), +//// NSForegroundColorAttributeName : +//// (hasUnreadMessages ? [UIColor ows_blackColor] +//// : [UIColor lightGrayColor]), +//// }]]; +//// } +//// } +//// +//// return snippetText; +// } +// } + + 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) + + if let contactThread = thread as? TSContactThread { + self.contactIdentifier = contactThread.contactIdentifier() + } else { + self.contactIdentifier = nil + } + + self.unreadCount = thread.unreadMessageCount(transaction: transaction) + self.hasUnreadMessages = unreadCount > 0 + } +} diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.h b/Signal/src/ViewControllers/HomeView/HomeViewCell.h index f5450b05c..8456ed9cb 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.h +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.h @@ -5,7 +5,7 @@ NS_ASSUME_NONNULL_BEGIN @class OWSContactsManager; -@class TSThread; +@class ThreadModel; @class YapDatabaseReadTransaction; @interface HomeViewCell : UITableViewCell @@ -14,10 +14,9 @@ NS_ASSUME_NONNULL_BEGIN + (NSString *)cellReuseIdentifier; -- (void)configureWithThread:(TSThread *)thread +- (void)configureWithThread:(ThreadModel *)thread contactsManager:(OWSContactsManager *)contactsManager - blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet - transaction:(YapDatabaseReadTransaction *)transaction; + blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet; @end diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index 4573a734f..d0974c5bb 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -12,7 +12,6 @@ #import #import #import -#import NS_ASSUME_NONNULL_BEGIN @@ -27,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) UIView *unreadBadge; @property (nonatomic) UILabel *unreadLabel; -@property (nonatomic, nullable) TSThread *thread; +@property (nonatomic, nullable) ThreadModel *thread; @property (nonatomic, nullable) OWSContactsManager *contactsManager; @property (nonatomic, readonly) NSMutableArray *viewConstraints; @@ -143,16 +142,14 @@ NS_ASSUME_NONNULL_BEGIN return NSStringFromClass(self.class); } -- (void)configureWithThread:(TSThread *)thread - contactsManager:(OWSContactsManager *)contactsManager - blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet - transaction:(YapDatabaseReadTransaction *)transaction +- (void)configureWithThread:(ThreadModel *)thread + contactsManager:(OWSContactsManager *)contactsManager + blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet { OWSAssertIsOnMainThread(); OWSAssert(thread); OWSAssert(contactsManager); OWSAssert(blockedPhoneNumberSet); - OWSAssert(transaction); self.thread = thread; self.contactsManager = contactsManager; @@ -173,9 +170,7 @@ NS_ASSUME_NONNULL_BEGIN // changes to the dynamic type settings are reflected. self.snippetLabel.font = [self snippetFont]; self.snippetLabel.attributedText = - [self attributedSnippetForThread:thread - blockedPhoneNumberSet:blockedPhoneNumberSet - transaction:transaction]; + [self attributedSnippetForThread:thread blockedPhoneNumberSet:blockedPhoneNumberSet]; self.dateTimeLabel.text = [self stringForDate:thread.lastMessageDate]; @@ -187,7 +182,7 @@ NS_ASSUME_NONNULL_BEGIN self.dateTimeLabel.font = self.unreadFont; } - NSUInteger unreadCount = [[OWSMessageUtils sharedManager] unreadMessagesInThread:thread]; + NSUInteger unreadCount = thread.unreadCount; if (unreadCount == 0) { [self.viewConstraints addObject:[self.payloadView autoPinTrailingToSuperviewMargin]]; } else { @@ -244,20 +239,20 @@ NS_ASSUME_NONNULL_BEGIN return; } - TSThread *thread = self.thread; + ThreadModel *thread = self.thread; if (thread == nil) { OWSFail(@"%@ thread should not be nil", self.logTag); self.avatarView.image = nil; return; } - self.avatarView.image = - [OWSAvatarBuilder buildImageForThread:thread diameter:self.avatarSize contactsManager:contactsManager]; + self.avatarView.image = [OWSAvatarBuilder buildImageForThread:thread.threadRecord + diameter:self.avatarSize + contactsManager:contactsManager]; } -- (NSAttributedString *)attributedSnippetForThread:(TSThread *)thread +- (NSAttributedString *)attributedSnippetForThread:(ThreadModel *)thread blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet - transaction:(YapDatabaseReadTransaction *)transaction { OWSAssert(thread); @@ -290,7 +285,7 @@ NS_ASSUME_NONNULL_BEGIN : [UIColor lightGrayColor]), }]]; } - NSString *displayableText = [thread lastMessageLabelWithTransaction:transaction]; + NSString *displayableText = thread.lastMessageText; if (displayableText) { [snippetText appendAttributedString:[[NSAttributedString alloc] initWithString:displayableText @@ -452,7 +447,7 @@ NS_ASSUME_NONNULL_BEGIN self.nameLabel.font = self.nameFont; - TSThread *thread = self.thread; + ThreadModel *thread = self.thread; if (thread == nil) { OWSFail(@"%@ thread should not be nil", self.logTag); self.nameLabel.attributedText = nil; diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 61d58c6a7..361f38516 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -600,15 +600,16 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; HomeViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:HomeViewCell.cellReuseIdentifier]; OWSAssert(cell); - TSThread *thread = [self threadForIndexPath:indexPath]; - - [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { - [cell configureWithThread:thread - contactsManager:self.contactsManager - blockedPhoneNumberSet:self.blockedPhoneNumberSet - transaction:transaction]; + TSThread *threadRecord = [self threadForIndexPath:indexPath]; + __block ThreadModel *thread; + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { + thread = [[ThreadModel alloc] initWithThread:threadRecord transaction:transaction]; }]; + [cell configureWithThread:thread + contactsManager:self.contactsManager + blockedPhoneNumberSet:self.blockedPhoneNumberSet]; + if ((unsigned long)indexPath.row == [self.threadMappings numberOfItemsInSection:0] - 1) { cell.separatorInset = UIEdgeInsetsMake(0.f, cell.bounds.size.width, 0.f, 0.f); } @@ -969,7 +970,12 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; // If the user hasn't already granted contact access // we don't want to request until they receive a message. - if ([TSThread numberOfKeysInCollection] > 0) { + __block BOOL hasAnyMessages; + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { + hasAnyMessages = [self hasAnyMessagesWithTransaction:transaction]; + }]; + + if (hasAnyMessages) { [self.contactsManager requestSystemContactsOnce]; } diff --git a/SignalServiceKit/src/Contacts/TSThread.h b/SignalServiceKit/src/Contacts/TSThread.h index ca952d0d8..1b32f81a1 100644 --- a/SignalServiceKit/src/Contacts/TSThread.h +++ b/SignalServiceKit/src/Contacts/TSThread.h @@ -70,7 +70,10 @@ NS_ASSUME_NONNULL_BEGIN * * @return YES if it has unread TSIncomingMessages, NO otherwise. */ -- (BOOL)hasUnreadMessages; +- (BOOL)hasUnreadMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction + NS_SWIFT_NAME(hasUnreadMessages(transaction:)); +- (NSUInteger)unreadMessageCountWithTransaction:(YapDatabaseReadTransaction *)transaction + NS_SWIFT_NAME(unreadMessageCount(transaction:)); - (BOOL)hasSafetyNumbers; @@ -90,7 +93,8 @@ NS_ASSUME_NONNULL_BEGIN * * @return Thread preview string. */ -- (NSString *)lastMessageLabelWithTransaction:(YapDatabaseReadTransaction *)transaction; +- (NSString *)lastMessageTextWithTransaction:(YapDatabaseReadTransaction *)transaction + NS_SWIFT_NAME(lastMessageText(transaction:)); /** * Updates the thread's caches of the latest interaction. diff --git a/SignalServiceKit/src/Contacts/TSThread.m b/SignalServiceKit/src/Contacts/TSThread.m index aa5f60cc0..c9d87fceb 100644 --- a/SignalServiceKit/src/Contacts/TSThread.m +++ b/SignalServiceKit/src/Contacts/TSThread.m @@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy) NSString *messageDraft; @property (atomic, nullable) NSDate *mutedUntilDate; -- (TSInteraction *)lastInteraction; +- (TSInteraction *)lastInteractionWithTranscation:(YapDatabaseReadTransaction *)transaction; @end @@ -200,8 +200,9 @@ NS_ASSUME_NONNULL_BEGIN return count; } -- (BOOL)hasUnreadMessages { - TSInteraction *interaction = self.lastInteraction; +- (BOOL)hasUnreadMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction +{ + TSInteraction *interaction = [self lastInteractionWithTransaction:transaction]; BOOL hasUnread = NO; if ([interaction isKindOfClass:[TSIncomingMessage class]]) { @@ -229,6 +230,11 @@ NS_ASSUME_NONNULL_BEGIN return [messages copy]; } +- (NSUInteger)unreadMessageCountWithTransaction:(YapDatabaseReadTransaction *)transaction +{ + return [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:self.uniqueId]; +} + - (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { for (id message in [self unseenMessagesWithTransaction:transaction]) { @@ -239,12 +245,9 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert([self unseenMessagesWithTransaction:transaction].count < 1); } -- (TSInteraction *) lastInteraction { - __block TSInteraction *last; - [OWSPrimaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - last = [[transaction ext:TSMessageDatabaseViewExtensionName] lastObjectInGroup:self.uniqueId]; - }]; - return last; +- (TSInteraction *)lastInteractionWithTransaction:(YapDatabaseReadTransaction *)transaction +{ + return [[transaction ext:TSMessageDatabaseViewExtensionName] lastObjectInGroup:self.uniqueId]; } - (TSInteraction *)lastInteractionForInboxWithTransaction:(YapDatabaseReadTransaction *)transaction @@ -276,7 +279,7 @@ NS_ASSUME_NONNULL_BEGIN } } -- (NSString *)lastMessageLabelWithTransaction:(YapDatabaseReadTransaction *)transaction +- (NSString *)lastMessageTextWithTransaction:(YapDatabaseReadTransaction *)transaction { TSInteraction *interaction = [self lastInteractionForInboxWithTransaction:transaction]; if ([interaction conformsToProtocol:@protocol(OWSPreviewText)]) { diff --git a/SignalServiceKit/src/Contacts/Threads/TSContactThread.m b/SignalServiceKit/src/Contacts/Threads/TSContactThread.m index 599ea9202..9b4de20ed 100644 --- a/SignalServiceKit/src/Contacts/Threads/TSContactThread.m +++ b/SignalServiceKit/src/Contacts/Threads/TSContactThread.m @@ -114,6 +114,7 @@ NS_ASSUME_NONNULL_BEGIN return !![[OWSIdentityManager sharedManager] identityKeyForRecipientId:self.contactIdentifier]; } +// TODO deprecate this? seems weird to access the displayName in the DB model - (NSString *)name { return [[TextSecureKitEnv sharedEnv].contactsManager displayNameForPhoneIdentifier:self.contactIdentifier]; diff --git a/SignalServiceKit/src/Messages/OWSMessageUtils.h b/SignalServiceKit/src/Messages/OWSMessageUtils.h index 5c136eebe..031e7fc98 100644 --- a/SignalServiceKit/src/Messages/OWSMessageUtils.h +++ b/SignalServiceKit/src/Messages/OWSMessageUtils.h @@ -15,7 +15,6 @@ NS_ASSUME_NONNULL_BEGIN - (NSUInteger)unreadMessagesCount; - (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread; -- (NSUInteger)unreadMessagesInThread:(TSThread *)thread; - (void)updateApplicationBadgeCount; diff --git a/SignalServiceKit/src/Messages/OWSMessageUtils.m b/SignalServiceKit/src/Messages/OWSMessageUtils.m index 79c72df0c..165e11f67 100644 --- a/SignalServiceKit/src/Messages/OWSMessageUtils.m +++ b/SignalServiceKit/src/Messages/OWSMessageUtils.m @@ -95,14 +95,6 @@ NS_ASSUME_NONNULL_BEGIN [CurrentAppContext() setMainAppBadgeNumber:numberOfItems]; } -- (NSUInteger)unreadMessagesInThread:(TSThread *)thread -{ - __block NSUInteger numberOfItems; - [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:thread.uniqueId]; - }]; - return numberOfItems; -} @end From 897d4a925b6784006279ce833e2c663e0ab6cff0 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Sat, 21 Apr 2018 13:00:58 -0400 Subject: [PATCH 03/10] HomeView caches thread models // FREEBIE --- Signal/src/Models/ThreadModel.swift | 61 ------------------- .../HomeView/HomeViewController.m | 31 +++++++--- .../src/Messages/Interactions/TSInteraction.m | 7 ++- .../src/Messages/Interactions/TSMessage.m | 7 --- .../src/Storage/TSYapDatabaseObject.m | 6 +- 5 files changed, 31 insertions(+), 81 deletions(-) diff --git a/Signal/src/Models/ThreadModel.swift b/Signal/src/Models/ThreadModel.swift index 7e5bc685b..1722ac69c 100644 --- a/Signal/src/Models/ThreadModel.swift +++ b/Signal/src/Models/ThreadModel.swift @@ -20,67 +20,6 @@ public class ThreadModel: NSObject { let lastMessageText: String? -// func attributedSnippet(blockedPhoneNumberSet: Set) { -// let isBlocked: Bool = { -// guard let contactIdentifier = self.contactIdentifier else { -// return false -// } -// assert(isContactThread) -// return blockedPhoneNumberSet.contains(self.contactIdentifier) -// }() -// -// -// -//// BOOL hasUnreadMessages = thread.hasUnreadMessages; -// -//// NSMutableAttributedString *snippetText = [NSMutableAttributedString new]; -// var snippetText = NSMutableAttributedString() -// if isBlocked { -// // If thread is blocked, don't show a snippet or mute status. -// let append = NSAttributedString(string: NSLocalizedString("HOME_VIEW_BLOCKED_CONTACT_CONVERSATION", -// comment: "A label for conversations with blocked users."), -// attributes: <#T##[String : Any]?#>) -// -//// if (isBlocked) { -//// // If thread is blocked, don't show a snippet or mute status. -//// [snippetText -//// appendAttributedString:[[NSAttributedString alloc] -//// initWithString:NSLocalizedString(@"HOME_VIEW_BLOCKED_CONTACT_CONVERSATION", -//// @"A label for conversations with blocked users.") -//// attributes:@{ -//// NSFontAttributeName : self.snippetFont.ows_mediumWeight, -//// NSForegroundColorAttributeName : [UIColor ows_blackColor], -//// }]]; -//// } else { -//// if ([thread isMuted]) { -//// [snippetText appendAttributedString:[[NSAttributedString alloc] -//// initWithString:@"\ue067 " -//// attributes:@{ -//// NSFontAttributeName : [UIFont ows_elegantIconsFont:9.f], -//// NSForegroundColorAttributeName : (hasUnreadMessages -//// ? [UIColor colorWithWhite:0.1f alpha:1.f] -//// : [UIColor lightGrayColor]), -//// }]]; -//// } -//// NSString *displayableText = thread.lastMessageText; -//// if (displayableText) { -//// [snippetText appendAttributedString:[[NSAttributedString alloc] -//// initWithString:displayableText -//// attributes:@{ -//// NSFontAttributeName : -//// (hasUnreadMessages ? self.snippetFont.ows_mediumWeight -//// : self.snippetFont), -//// NSForegroundColorAttributeName : -//// (hasUnreadMessages ? [UIColor ows_blackColor] -//// : [UIColor lightGrayColor]), -//// }]]; -//// } -//// } -//// -//// return snippetText; -// } -// } - init(thread: TSThread, transaction: YapDatabaseReadTransaction) { self.threadRecord = thread self.lastMessageDate = thread.lastMessageDate() diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 361f38516..71ed8b6a4 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -47,7 +47,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; @property (nonatomic) UISegmentedControl *segmentedControl; @property (nonatomic) id previewingContext; @property (nonatomic) NSSet *blockedPhoneNumberSet; - +@property (nonatomic, readonly) NSCache *threadModelCache; @property (nonatomic) BOOL isViewVisible; @property (nonatomic) BOOL isAppInBackground; @property (nonatomic) BOOL shouldObserveDBModifications; @@ -108,6 +108,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; _messageSender = [Environment current].messageSender; _blockingManager = [OWSBlockingManager sharedManager]; _blockedPhoneNumberSet = [NSSet setWithArray:[_blockingManager blockedPhoneNumbers]]; + _threadModelCache = [NSCache new]; // Ensure ExperienceUpgradeFinder has been initialized. [ExperienceUpgradeFinder sharedManager]; @@ -595,17 +596,29 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; return (NSInteger)[self.threadMappings numberOfItemsInSection:(NSUInteger)section]; } -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +- (ThreadModel *)threadModelForIndexPath:(NSIndexPath *)indexPath { - HomeViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:HomeViewCell.cellReuseIdentifier]; - OWSAssert(cell); - TSThread *threadRecord = [self threadForIndexPath:indexPath]; - __block ThreadModel *thread; + + ThreadModel *_Nullable cachedThreadModel = [self.threadModelCache objectForKey:threadRecord.uniqueId]; + if (cachedThreadModel) { + return cachedThreadModel; + } + + __block ThreadModel *_Nullable newThreadModel; [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { - thread = [[ThreadModel alloc] initWithThread:threadRecord transaction:transaction]; + newThreadModel = [[ThreadModel alloc] initWithThread:threadRecord transaction:transaction]; }]; + [self.threadModelCache setObject:newThreadModel forKey:threadRecord.uniqueId]; + return newThreadModel; +} +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + HomeViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:HomeViewCell.cellReuseIdentifier]; + OWSAssert(cell); + + ThreadModel *thread = [self threadModelForIndexPath:indexPath]; [cell configureWithThread:thread contactsManager:self.contactsManager blockedPhoneNumberSet:self.blockedPhoneNumberSet]; @@ -1016,6 +1029,10 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; } for (YapDatabaseViewRowChange *rowChange in rowChanges) { + NSString *key = rowChange.collectionKey.key; + OWSAssert(key); + [self.threadModelCache removeObjectForKey:key]; + switch (rowChange.type) { case YapDatabaseViewChangeDelete: { [self.tableView deleteRowsAtIndexPaths:@[ rowChange.indexPath ] diff --git a/SignalServiceKit/src/Messages/Interactions/TSInteraction.m b/SignalServiceKit/src/Messages/Interactions/TSInteraction.m index fc796947e..bd6a53662 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSInteraction.m +++ b/SignalServiceKit/src/Messages/Interactions/TSInteraction.m @@ -127,9 +127,10 @@ NS_ASSUME_NONNULL_BEGIN return OWSInteractionType_Unknown; } -- (NSString *)description { - OWSFail(@"Abstract Method"); - return @"Interaction description"; +- (NSString *)description +{ + return [NSString + stringWithFormat:@"%@ in thread: %@ timestamp: %tu", [super description], self.uniqueThreadId, self.timestamp]; } - (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.m b/SignalServiceKit/src/Messages/Interactions/TSMessage.m index da06a35ab..071aada16 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.m @@ -265,13 +265,6 @@ static const NSUInteger OWSMessageSchemaVersion = 4; } } -- (NSString *)description -{ - // TODO verify this isn't exposed in the UI - OWSFail(@"%@ in %s verify this isn't being used anywhere except logging.", self.logTag, __PRETTY_FUNCTION__); - return [super description]; -} - - (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { [super removeWithTransaction:transaction]; diff --git a/SignalServiceKit/src/Storage/TSYapDatabaseObject.m b/SignalServiceKit/src/Storage/TSYapDatabaseObject.m index a11ac3d4e..260e31d50 100644 --- a/SignalServiceKit/src/Storage/TSYapDatabaseObject.m +++ b/SignalServiceKit/src/Storage/TSYapDatabaseObject.m @@ -109,8 +109,8 @@ NS_ASSUME_NONNULL_BEGIN + (YapDatabaseConnection *)dbReadConnection { - OWSAssert(![NSThread isMainThread]); - + // OWSAssert(![NSThread isMainThread]); + // We use TSYapDatabaseObject's dbReadWriteConnection (not OWSPrimaryStorage's // dbReadConnection) for consistency, since we tend to [TSYapDatabaseObject // save] and want to write to the same connection we read from. To get true @@ -121,7 +121,7 @@ NS_ASSUME_NONNULL_BEGIN + (YapDatabaseConnection *)dbReadWriteConnection { - OWSAssert(![NSThread isMainThread]); + // OWSAssert(![NSThread isMainThread]); // Use a dedicated connection for model reads & writes. static YapDatabaseConnection *dbReadWriteConnection = nil; From 9c81eb44a192699367850ac659cb1da937dab3ab Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Sat, 21 Apr 2018 13:45:45 -0400 Subject: [PATCH 04/10] Replace remaining UI usage of `interaction.description` // FREEBIE --- .../Cells/ConversationViewCell.h | 3 +- .../Cells/ConversationViewCell.m | 4 +- .../Cells/OWSContactOffersCell.m | 2 +- .../ConversationView/Cells/OWSMessageCell.m | 2 +- .../Cells/OWSSystemMessageCell.m | 35 +++++--- .../Cells/OWSUnreadIndicatorCell.m | 2 +- .../ConversationViewController.m | 4 +- .../ViewControllers/DebugUI/DebugUIMessages.m | 8 +- Signal/src/environment/NotificationsManager.m | 90 ++++++++++--------- .../NoopNotificationsManager.swift | 4 +- .../src/Messages/OWSIdentityManager.m | 4 +- .../src/Messages/OWSMessageDecrypter.m | 23 +++-- SignalServiceKit/src/Messages/TSCall.h | 2 +- SignalServiceKit/src/Messages/TSCall.m | 12 +-- .../src/Protocols/NotificationsProtocol.h | 11 ++- 15 files changed, 114 insertions(+), 92 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h b/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h index 0f9a58664..097669b62 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.h @@ -13,6 +13,7 @@ NS_ASSUME_NONNULL_BEGIN @class TSMessage; @class TSOutgoingMessage; @class TSQuotedMessage; +@class YapDatabaseReadTransaction; @protocol ConversationViewCellDelegate @@ -71,7 +72,7 @@ NS_ASSUME_NONNULL_BEGIN // The width of the collection view. @property (nonatomic) int contentWidth; -- (void)loadForDisplay; +- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction; - (CGSize)cellSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.m b/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.m index 6235989d4..e82863d8c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/ConversationViewCell.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "ConversationViewCell.h" @@ -19,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN self.contentWidth = 0; } -- (void)loadForDisplay +- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction { OWSFail(@"%@ This method should be overridden.", self.logTag); } diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSContactOffersCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSContactOffersCell.m index a86655a7b..6402fbbf6 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSContactOffersCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSContactOffersCell.m @@ -85,7 +85,7 @@ NS_ASSUME_NONNULL_BEGIN return NSStringFromClass([self class]); } -- (void)loadForDisplay +- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction { OWSAssert(self.viewItem); OWSAssert([self.viewItem.interaction isKindOfClass:[OWSContactOffersInteraction class]]); diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 49b6b817b..6c19879c3 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -150,7 +150,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Load -- (void)loadForDisplay +- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction { OWSAssert(self.viewItem); OWSAssert(self.viewItem.interaction); diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSSystemMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSSystemMessageCell.m index d1d6a48b9..4381b6633 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSSystemMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSSystemMessageCell.m @@ -73,7 +73,7 @@ NS_ASSUME_NONNULL_BEGIN return NSStringFromClass([self class]); } -- (void)loadForDisplay +- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction { OWSAssert(self.viewItem); @@ -83,7 +83,7 @@ NS_ASSUME_NONNULL_BEGIN self.imageView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; self.imageView.tintColor = [self iconColorForInteraction:interaction]; self.titleLabel.textColor = [self textColor]; - [self applyTitleForInteraction:interaction label:self.titleLabel]; + [self applyTitleForInteraction:interaction label:self.titleLabel transaction:transaction]; [self setNeedsLayout]; } @@ -162,7 +162,9 @@ NS_ASSUME_NONNULL_BEGIN return result; } -- (void)applyTitleForInteraction:(TSInteraction *)interaction label:(UILabel *)label +- (void)applyTitleForInteraction:(TSInteraction *)interaction + label:(UILabel *)label + transaction:(YapDatabaseReadTransaction *)transaction { OWSAssert(interaction); OWSAssert(label); @@ -170,22 +172,24 @@ NS_ASSUME_NONNULL_BEGIN // TODO: Should we move the copy generation into this view? if ([interaction isKindOfClass:[TSErrorMessage class]]) { - label.text = interaction.description; + TSErrorMessage *errorMessage = (TSErrorMessage *)interaction; + label.text = [errorMessage previewTextWithTransaction:transaction]; } else if ([interaction isKindOfClass:[TSInfoMessage class]]) { - if ([interaction isKindOfClass:[OWSVerificationStateChangeMessage class]]) { - OWSVerificationStateChangeMessage *message = (OWSVerificationStateChangeMessage *)interaction; - BOOL isVerified = message.verificationState == OWSVerificationStateVerified; + TSInfoMessage *infoMessage = (TSInfoMessage *)interaction; + if ([infoMessage isKindOfClass:[OWSVerificationStateChangeMessage class]]) { + OWSVerificationStateChangeMessage *verificationMessage = (OWSVerificationStateChangeMessage *)infoMessage; + BOOL isVerified = verificationMessage.verificationState == OWSVerificationStateVerified; NSString *displayName = - [[Environment current].contactsManager displayNameForPhoneIdentifier:message.recipientId]; + [[Environment current].contactsManager displayNameForPhoneIdentifier:verificationMessage.recipientId]; NSString *titleFormat = (isVerified - ? (message.isLocalChange + ? (verificationMessage.isLocalChange ? NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_VERIFIED_LOCAL", @"Format for info message indicating that the verification state was verified on " @"this device. Embeds {{user's name or phone number}}.") : NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_VERIFIED_OTHER_DEVICE", @"Format for info message indicating that the verification state was verified on " @"another device. Embeds {{user's name or phone number}}.")) - : (message.isLocalChange + : (verificationMessage.isLocalChange ? NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_NOT_VERIFIED_LOCAL", @"Format for info message indicating that the verification state was unverified on " @"this device. Embeds {{user's name or phone number}}.") @@ -194,10 +198,11 @@ NS_ASSUME_NONNULL_BEGIN @"another device. Embeds {{user's name or phone number}}."))); label.text = [NSString stringWithFormat:titleFormat, displayName]; } else { - label.text = interaction.description; + label.text = [infoMessage previewTextWithTransaction:transaction]; } } else if ([interaction isKindOfClass:[TSCall class]]) { - label.text = interaction.description; + TSCall *call = (TSCall *)interaction; + label.text = [call previewTextWithTransaction:transaction]; } else { OWSFail(@"Unknown interaction type: %@", [interaction class]); label.text = nil; @@ -266,7 +271,11 @@ NS_ASSUME_NONNULL_BEGIN result.height += self.topVMargin; result.height += self.bottomVMargin; - [self applyTitleForInteraction:interaction label:self.titleLabel]; + // FIXME pass in transaction from the uiDBConnection. + [[TSYapDatabaseObject dbReadConnection] readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { + [self applyTitleForInteraction:interaction label:self.titleLabel transaction:transaction]; + }]; + CGFloat maxTitleWidth = (viewWidth - ([self hMargin] * 2.f + [self hSpacing] + [self iconSize])); CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)]; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSUnreadIndicatorCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSUnreadIndicatorCell.m index 88ca54b48..13ceeb817 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSUnreadIndicatorCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSUnreadIndicatorCell.m @@ -84,7 +84,7 @@ NS_ASSUME_NONNULL_BEGIN return NSStringFromClass([self class]); } -- (void)loadForDisplay +- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction { OWSAssert(self.viewItem); OWSAssert([self.viewItem.interaction isKindOfClass:[TSUnreadIndicatorInteraction class]]); diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 9c7bdb86d..6531cc68f 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -4757,7 +4757,9 @@ typedef enum : NSUInteger { } cell.contentWidth = self.layout.contentWidth; - [cell loadForDisplay]; + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { + [cell loadForDisplayWithTransaction:transaction]; + }]; return cell; } diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index b050f9e8b..ef3ffc1dd 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -3223,7 +3223,7 @@ NS_ASSUME_NONNULL_BEGIN TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:phoneNumber.toE164]; [self sendFakeMessages:messageCount thread:contactThread]; - DDLogError(@"Create fake thread: %@, interactions: %zd", + DDLogError(@"Create fake thread: %@, interactions: %tu", phoneNumber.toE164, contactThread.numberOfInteractions); }]; @@ -3251,7 +3251,7 @@ NS_ASSUME_NONNULL_BEGIN [self sendFakeMessages:batchSize thread:thread isTextOnly:isTextOnly transaction:transaction]; }]; remainder -= batchSize; - DDLogInfo(@"%@ sendFakeMessages %zd / %zd", self.logTag, counter - remainder, counter); + DDLogInfo(@"%@ sendFakeMessages %td / %tu", self.logTag, counter - remainder, counter); } }); } @@ -3263,7 +3263,7 @@ NS_ASSUME_NONNULL_BEGIN isTextOnly:(BOOL)isTextOnly transaction:(YapDatabaseReadWriteTransaction *)transaction { - DDLogInfo(@"%@ sendFakeMessages: %zd", self.logTag, counter); + DDLogInfo(@"%@ sendFakeMessages: %tu", self.logTag, counter); for (NSUInteger i = 0; i < counter; i++) { NSString *randomText = [self randomText]; @@ -3411,7 +3411,7 @@ NS_ASSUME_NONNULL_BEGIN { OWSAssert(thread); - DDLogInfo(@"%@ injectIncomingMessageInThread: %zd", self.logTag, counter); + DDLogInfo(@"%@ injectIncomingMessageInThread: %tu", self.logTag, counter); NSString *randomText = [self randomText]; NSString *text = [[[@(counter) description] stringByAppendingString:@" "] stringByAppendingString:randomText]; diff --git a/Signal/src/environment/NotificationsManager.m b/Signal/src/environment/NotificationsManager.m index a09ab3b65..1bd2f6f74 100644 --- a/Signal/src/environment/NotificationsManager.m +++ b/Signal/src/environment/NotificationsManager.m @@ -18,6 +18,7 @@ #import #import #import +#import @interface NotificationsManager () @@ -200,51 +201,56 @@ #pragma mark - Signal Messages -- (void)notifyUserForErrorMessage:(TSErrorMessage *)message inThread:(TSThread *)thread { +- (void)notifyUserForErrorMessage:(TSErrorMessage *)message + thread:(TSThread *)thread + transaction:(YapDatabaseReadWriteTransaction *)transaction +{ OWSAssert(message); OWSAssert(thread); - dispatch_async(dispatch_get_main_queue(), ^{ - if (thread.isMuted) { - return; - } - - BOOL shouldPlaySound = [self shouldPlaySoundForNotification]; - - NSString *messageDescription = message.description; - - if (([UIApplication sharedApplication].applicationState != UIApplicationStateActive) && messageDescription) { - UILocalNotification *notification = [[UILocalNotification alloc] init]; - notification.userInfo = @{ Signal_Thread_UserInfo_Key : thread.uniqueId }; - if (shouldPlaySound) { - OWSSound sound = [OWSSounds notificationSoundForThread:thread]; - notification.soundName = [OWSSounds filenameForSound:sound]; - } - - NSString *alertBodyString = @""; - - NSString *authorName = [thread name]; - switch (self.notificationPreviewType) { - case NotificationNamePreview: - case NotificationNameNoPreview: - alertBodyString = [NSString stringWithFormat:@"%@: %@", authorName, messageDescription]; - break; - case NotificationNoNameNoPreview: - alertBodyString = messageDescription; - break; - } - notification.alertBody = alertBodyString; - - [[PushManager sharedManager] presentNotification:notification checkForCancel:NO]; - } else { - if (shouldPlaySound && [Environment.preferences soundInForeground]) { - OWSSound sound = [OWSSounds notificationSoundForThread:thread]; - SystemSoundID soundId = [OWSSounds systemSoundIDForSound:sound quiet:YES]; - // Vibrate, respect silent switch, respect "Alert" volume, not media volume. - AudioServicesPlayAlertSound(soundId); - } - } - }); + NSString *messageText = [message previewTextWithTransaction:transaction]; + + [transaction + addCompletionQueue:nil + completionBlock:^() { + if (thread.isMuted) { + return; + } + + BOOL shouldPlaySound = [self shouldPlaySoundForNotification]; + + if (([UIApplication sharedApplication].applicationState != UIApplicationStateActive) && messageText) { + UILocalNotification *notification = [[UILocalNotification alloc] init]; + notification.userInfo = @{ Signal_Thread_UserInfo_Key : thread.uniqueId }; + if (shouldPlaySound) { + OWSSound sound = [OWSSounds notificationSoundForThread:thread]; + notification.soundName = [OWSSounds filenameForSound:sound]; + } + + NSString *alertBodyString = @""; + + NSString *authorName = [thread name]; + switch (self.notificationPreviewType) { + case NotificationNamePreview: + case NotificationNameNoPreview: + alertBodyString = [NSString stringWithFormat:@"%@: %@", authorName, messageText]; + break; + case NotificationNoNameNoPreview: + alertBodyString = messageText; + break; + } + notification.alertBody = alertBodyString; + + [[PushManager sharedManager] presentNotification:notification checkForCancel:NO]; + } else { + if (shouldPlaySound && [Environment.preferences soundInForeground]) { + OWSSound sound = [OWSSounds notificationSoundForThread:thread]; + SystemSoundID soundId = [OWSSounds systemSoundIDForSound:sound quiet:YES]; + // Vibrate, respect silent switch, respect "Alert" volume, not media volume. + AudioServicesPlayAlertSound(soundId); + } + } + }]; } - (void)notifyUserForIncomingMessage:(TSIncomingMessage *)message diff --git a/SignalMessaging/environment/NoopNotificationsManager.swift b/SignalMessaging/environment/NoopNotificationsManager.swift index 865e47c98..a801a7d8d 100644 --- a/SignalMessaging/environment/NoopNotificationsManager.swift +++ b/SignalMessaging/environment/NoopNotificationsManager.swift @@ -7,11 +7,11 @@ import SignalServiceKit @objc public class NoopNotificationsManager: NSObject, NotificationsProtocol { - public func notifyUser(for incomingMessage: TSIncomingMessage!, in thread: TSThread!, contactsManager: ContactsManagerProtocol!, transaction: YapDatabaseReadTransaction!) { + public func notifyUser(for incomingMessage: TSIncomingMessage, in thread: TSThread, contactsManager: ContactsManagerProtocol, transaction: YapDatabaseReadTransaction) { owsFail("\(self.logTag) in \(#function).") } - public func notifyUser(for error: TSErrorMessage!, in thread: TSThread!) { + public func notifyUser(for error: TSErrorMessage, thread: TSThread, transaction: YapDatabaseReadWriteTransaction) { Logger.warn("\(self.logTag) in \(#function), skipping notification for: \(error.description)") } } diff --git a/SignalServiceKit/src/Messages/OWSIdentityManager.m b/SignalServiceKit/src/Messages/OWSIdentityManager.m index ed76aa21c..78e0b60eb 100644 --- a/SignalServiceKit/src/Messages/OWSIdentityManager.m +++ b/SignalServiceKit/src/Messages/OWSIdentityManager.m @@ -545,7 +545,9 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa [message saveWithTransaction:transaction]; } - [[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:contactThread]; + [[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage + thread:contactThread + transaction:transaction]; } - (void)enqueueSyncMessageForVerificationStateForRecipientId:(NSString *)recipientId diff --git a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m index 220b36983..dffbad90e 100644 --- a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m +++ b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m @@ -269,8 +269,10 @@ NS_ASSUME_NONNULL_BEGIN exception.name, exception.reason); - __block TSErrorMessage *errorMessage; + [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + TSErrorMessage *errorMessage; + if ([exception.name isEqualToString:NoSessionException]) { OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorNoSession], envelope); errorMessage = [TSErrorMessage missingSessionWithEnvelope:envelope withTransaction:transaction]; @@ -298,18 +300,21 @@ NS_ASSUME_NONNULL_BEGIN } OWSAssert(errorMessage); - [errorMessage saveWithTransaction:transaction]; + if (errorMessage != nil) { + [errorMessage saveWithTransaction:transaction]; + [self notifyUserForErrorMessage:errorMessage envelope:envelope transaction:transaction]; + } }]; - - if (errorMessage != nil) { - [self notifyForErrorMessage:errorMessage withEnvelope:envelope]; - } } -- (void)notifyForErrorMessage:(TSErrorMessage *)errorMessage withEnvelope:(OWSSignalServiceProtosEnvelope *)envelope +- (void)notifyUserForErrorMessage:(TSErrorMessage *)errorMessage + envelope:(OWSSignalServiceProtosEnvelope *)envelope + transaction:(YapDatabaseReadWriteTransaction *)transaction { - TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source]; - [[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:contactThread]; + TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; + [[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage + thread:contactThread + transaction:transaction]; } @end diff --git a/SignalServiceKit/src/Messages/TSCall.h b/SignalServiceKit/src/Messages/TSCall.h index bcadbaf8b..fdca7d4d1 100644 --- a/SignalServiceKit/src/Messages/TSCall.h +++ b/SignalServiceKit/src/Messages/TSCall.h @@ -20,7 +20,7 @@ typedef enum { RPRecentCallTypeIncomingDeclined } RPRecentCallType; -@interface TSCall : TSInteraction +@interface TSCall : TSInteraction @property (nonatomic, readonly) RPRecentCallType callType; diff --git a/SignalServiceKit/src/Messages/TSCall.m b/SignalServiceKit/src/Messages/TSCall.m index cc49de57a..20d805c09 100644 --- a/SignalServiceKit/src/Messages/TSCall.m +++ b/SignalServiceKit/src/Messages/TSCall.m @@ -69,11 +69,7 @@ NSUInteger TSCallCurrentSchemaVersion = 1; - (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction { - return [self previewText]; -} - -- (NSString *)previewText -{ + // We don't actually use the `transaction` but other sibling classes do. switch (_callType) { case RPRecentCallTypeIncoming: return NSLocalizedString(@"INCOMING_CALL", @""); @@ -93,12 +89,6 @@ NSUInteger TSCallCurrentSchemaVersion = 1; } } -- (NSString *)description -{ - OWSFail(@"%@ in %s verify this isnt exposed in the UI", self.logTag, __PRETTY_FUNCTION__); - return [self previewText]; -} - #pragma mark - OWSReadTracking - (uint64_t)expireStartedAt diff --git a/SignalServiceKit/src/Protocols/NotificationsProtocol.h b/SignalServiceKit/src/Protocols/NotificationsProtocol.h index 183cec078..892c80eed 100644 --- a/SignalServiceKit/src/Protocols/NotificationsProtocol.h +++ b/SignalServiceKit/src/Protocols/NotificationsProtocol.h @@ -1,11 +1,14 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // +NS_ASSUME_NONNULL_BEGIN + @class TSErrorMessage; @class TSIncomingMessage; @class TSThread; @class YapDatabaseReadTransaction; + @protocol ContactsManagerProtocol; @protocol NotificationsProtocol @@ -15,6 +18,10 @@ contactsManager:(id)contactsManager transaction:(YapDatabaseReadTransaction *)transaction; -- (void)notifyUserForErrorMessage:(TSErrorMessage *)error inThread:(TSThread *)thread; +- (void)notifyUserForErrorMessage:(TSErrorMessage *)error + thread:(TSThread *)thread + transaction:(YapDatabaseReadWriteTransaction *)transaction; @end + +NS_ASSUME_NONNULL_END From 113cb254d9b9abaea643408de31f41c855f5607a Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Sat, 21 Apr 2018 15:22:40 -0400 Subject: [PATCH 05/10] fixup cache --- .../HomeView/HomeViewController.m | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 71ed8b6a4..519abfa88 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -156,14 +156,14 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; _blockedPhoneNumberSet = [NSSet setWithArray:[_blockingManager blockedPhoneNumbers]]; - [self.tableView reloadData]; + [self reloadTableViewData]; } - (void)signalAccountsDidChange:(id)notification { OWSAssertIsOnMainThread(); - [self.tableView reloadData]; + [self reloadTableViewData]; } #pragma mark - View Life Cycle @@ -473,6 +473,13 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; } } +- (void)reloadTableViewData +{ + // PERF: come up with a more nuanced cache clearing scheme + [self.threadModelCache removeAllObjects]; + [self.tableView reloadData]; +} + - (void)resetMappings { // If we're entering "active" mode (e.g. view is visible and app is in foreground), @@ -489,7 +496,8 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; }]; } - [[self tableView] reloadData]; + [self reloadTableViewData]; + [self checkIfEmptyView]; [self updateInboxCountLabel]; @@ -926,7 +934,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; [self resetMappings]; - [[self tableView] reloadData]; + [self reloadTableViewData]; [self checkIfEmptyView]; [self updateReminderViews]; } From 90dda0bf9c42919bf5775ec90316f04ce541a382 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Sat, 21 Apr 2018 15:22:49 -0400 Subject: [PATCH 06/10] SEPARATE: Use non-signal contact's full name // FREEBIE --- SignalMessaging/contacts/OWSContactsManager.m | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/SignalMessaging/contacts/OWSContactsManager.m b/SignalMessaging/contacts/OWSContactsManager.m index 5d6ef6deb..96a3dc6b2 100644 --- a/SignalMessaging/contacts/OWSContactsManager.m +++ b/SignalMessaging/contacts/OWSContactsManager.m @@ -371,9 +371,15 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification { OWSAssert(recipientId.length > 0); - SignalAccount *signalAccount = [self signalAccountForRecipientId:recipientId]; + SignalAccount *_Nullable signalAccount = [self signalAccountForRecipientId:recipientId]; if (!signalAccount) { - return nil; + // search system contacts for no-longer-registered signal users, for which there will be no SignalAccount + DDLogDebug(@"%@ no signal account", self.logTag); + Contact *_Nullable nonSignalContact = self.allContactsMap[recipientId]; + if (!nonSignalContact) { + return nil; + } + return nonSignalContact.fullName; } NSString *fullName = signalAccount.contactFullName; From b3cd6a112b927498cf05b50d8d5bb3a76d74fa79 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Sat, 21 Apr 2018 15:23:15 -0400 Subject: [PATCH 07/10] Add OWSJanksUI assertion // FREEBIE --- .../src/Storage/TSYapDatabaseObject.m | 4 ++-- SignalServiceKit/src/Util/OWSAsserts.h | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/SignalServiceKit/src/Storage/TSYapDatabaseObject.m b/SignalServiceKit/src/Storage/TSYapDatabaseObject.m index 260e31d50..4317e8b7a 100644 --- a/SignalServiceKit/src/Storage/TSYapDatabaseObject.m +++ b/SignalServiceKit/src/Storage/TSYapDatabaseObject.m @@ -109,7 +109,7 @@ NS_ASSUME_NONNULL_BEGIN + (YapDatabaseConnection *)dbReadConnection { - // OWSAssert(![NSThread isMainThread]); + OWSJanksUI(); // We use TSYapDatabaseObject's dbReadWriteConnection (not OWSPrimaryStorage's // dbReadConnection) for consistency, since we tend to [TSYapDatabaseObject @@ -121,7 +121,7 @@ NS_ASSUME_NONNULL_BEGIN + (YapDatabaseConnection *)dbReadWriteConnection { - // OWSAssert(![NSThread isMainThread]); + OWSJanksUI(); // Use a dedicated connection for model reads & writes. static YapDatabaseConnection *dbReadWriteConnection = nil; diff --git a/SignalServiceKit/src/Util/OWSAsserts.h b/SignalServiceKit/src/Util/OWSAsserts.h index c9f34a660..84be8e747 100755 --- a/SignalServiceKit/src/Util/OWSAsserts.h +++ b/SignalServiceKit/src/Util/OWSAsserts.h @@ -150,4 +150,26 @@ void SwiftAssertIsOnMainThread(NSString *functionName); userInfo:userInfoParam]; \ } + +// UI JANK +// +// In pursuit of smooth UI, we want to continue moving blocking operations off the main thread. +// Add `OWSJanksUI` in code paths that shouldn't be called on the main thread. +// Because we have pervasively broken this tenant, enabling it by default would be too disruptive +// but it's helpful while unjanking and maybe someday we can have it enabled by default. +//#define DEBUG_UI_JANK 1 + +#ifdef DEBUG +#ifdef DEBUG_UI_JANK +#define OWSJanksUI() \ + do { \ + OWSAssert(![NSThread isMainThread]) \ + } while (NO) +#endif +#endif + +#ifndef OWSJanksUI +#define OWSJanksUI() +#endif + NS_ASSUME_NONNULL_END From 1f63ce02a0ed0aa8124b1151110d8f7dcf4e9a3f Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Sat, 21 Apr 2018 19:17:56 -0400 Subject: [PATCH 08/10] Increase cache, remove dead code, add debug logging // FREEBIE --- .../HomeView/HomeViewController.m | 2 ++ SignalServiceKit/src/Contacts/TSThread.h | 7 ----- SignalServiceKit/src/Contacts/TSThread.m | 29 +++++++------------ 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 519abfa88..bd7012831 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -947,6 +947,8 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; if (!_uiDatabaseConnection) { _uiDatabaseConnection = [OWSPrimaryStorage.sharedManager newDatabaseConnection]; + // default is 250 + _uiDatabaseConnection.objectCacheLimit = 500; [_uiDatabaseConnection beginLongLivedReadTransaction]; } return _uiDatabaseConnection; diff --git a/SignalServiceKit/src/Contacts/TSThread.h b/SignalServiceKit/src/Contacts/TSThread.h index 1b32f81a1..07b795bd9 100644 --- a/SignalServiceKit/src/Contacts/TSThread.h +++ b/SignalServiceKit/src/Contacts/TSThread.h @@ -65,13 +65,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (NSArray *)receivedMessagesForInvalidKey:(NSData *)key; -/** - * Returns whether or not the thread has unread messages. - * - * @return YES if it has unread TSIncomingMessages, NO otherwise. - */ -- (BOOL)hasUnreadMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction - NS_SWIFT_NAME(hasUnreadMessages(transaction:)); - (NSUInteger)unreadMessageCountWithTransaction:(YapDatabaseReadTransaction *)transaction NS_SWIFT_NAME(unreadMessageCount(transaction:)); diff --git a/SignalServiceKit/src/Contacts/TSThread.m b/SignalServiceKit/src/Contacts/TSThread.m index c9d87fceb..074dc7c0d 100644 --- a/SignalServiceKit/src/Contacts/TSThread.m +++ b/SignalServiceKit/src/Contacts/TSThread.m @@ -200,18 +200,6 @@ NS_ASSUME_NONNULL_BEGIN return count; } -- (BOOL)hasUnreadMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction -{ - TSInteraction *interaction = [self lastInteractionWithTransaction:transaction]; - BOOL hasUnread = NO; - - if ([interaction isKindOfClass:[TSIncomingMessage class]]) { - hasUnread = ![(TSIncomingMessage *)interaction wasRead]; - } - - return hasUnread; -} - - (NSArray> *)unseenMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction { NSMutableArray> *messages = [NSMutableArray new]; @@ -245,13 +233,9 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert([self unseenMessagesWithTransaction:transaction].count < 1); } -- (TSInteraction *)lastInteractionWithTransaction:(YapDatabaseReadTransaction *)transaction -{ - return [[transaction ext:TSMessageDatabaseViewExtensionName] lastObjectInGroup:self.uniqueId]; -} - - (TSInteraction *)lastInteractionForInboxWithTransaction:(YapDatabaseReadTransaction *)transaction { + __block NSUInteger missedCount = 0; __block TSInteraction *last = nil; [[transaction ext:TSMessageDatabaseViewExtensionName] enumerateRowsInGroup:self.uniqueId @@ -260,11 +244,20 @@ NS_ASSUME_NONNULL_BEGIN NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) { OWSAssert([object isKindOfClass:[TSInteraction class]]); - + + missedCount++; TSInteraction *interaction = (TSInteraction *)object; if ([TSThread shouldInteractionAppearInInbox:interaction]) { last = interaction; + + // For long ignored threads, with lots of SN changes this can get really slow. + // I see this in development because I have a lot of long forgotten threads with members + // who's test devices are constantly reinstalled. We could add a purpose-built DB view, + // but I think in the real world this is rare to be a hotspot. + if (missedCount > 50) { + DDLogWarn(@"%@ found last interaction for inbox after skipping %tu items", self.logTag, missedCount); + } *stop = YES; } }]; From d1230abdc538ea6dfb3e577d2fc77939f49dbaae Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Sat, 21 Apr 2018 19:18:25 -0400 Subject: [PATCH 09/10] Fix type declaration // FREEBIE --- SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m index a98b39491..0e95c93af 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m @@ -437,7 +437,7 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec [builder setBody:self.body]; } else { OWSFail(@"%@ message body length too long.", self.logTag); - NSMutableString *truncatedBody = [self.body mutableCopy]; + NSString *truncatedBody = self.body; while ([truncatedBody lengthOfBytesUsingEncoding:NSUTF8StringEncoding] > kOversizeTextMessageSizeThreshold) { DDLogError(@"%@ truncating body which is too long: %tu", self.logTag, From f5e810e82ba6dec0023ef97a6b776929f9b2d1f1 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 23 Apr 2018 12:15:21 -0400 Subject: [PATCH 10/10] CR: rename ThreadModel -> ThreadViewModel // FREEBIE --- Signal/src/Models/ThreadModel.swift | 2 +- .../ViewControllers/HomeView/HomeViewCell.h | 4 +-- .../ViewControllers/HomeView/HomeViewCell.m | 10 +++---- .../HomeView/HomeViewController.m | 26 +++++++++---------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Signal/src/Models/ThreadModel.swift b/Signal/src/Models/ThreadModel.swift index 1722ac69c..33d73e657 100644 --- a/Signal/src/Models/ThreadModel.swift +++ b/Signal/src/Models/ThreadModel.swift @@ -5,7 +5,7 @@ import Foundation @objc -public class ThreadModel: NSObject { +public class ThreadViewModel: NSObject { let hasUnreadMessages: Bool let lastMessageDate: Date let isGroupThread: Bool diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.h b/Signal/src/ViewControllers/HomeView/HomeViewCell.h index 8456ed9cb..054b9fcb5 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.h +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.h @@ -5,7 +5,7 @@ NS_ASSUME_NONNULL_BEGIN @class OWSContactsManager; -@class ThreadModel; +@class ThreadViewModel; @class YapDatabaseReadTransaction; @interface HomeViewCell : UITableViewCell @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN + (NSString *)cellReuseIdentifier; -- (void)configureWithThread:(ThreadModel *)thread +- (void)configureWithThread:(ThreadViewModel *)thread contactsManager:(OWSContactsManager *)contactsManager blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet; diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index d0974c5bb..785c4cfc5 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) UIView *unreadBadge; @property (nonatomic) UILabel *unreadLabel; -@property (nonatomic, nullable) ThreadModel *thread; +@property (nonatomic, nullable) ThreadViewModel *thread; @property (nonatomic, nullable) OWSContactsManager *contactsManager; @property (nonatomic, readonly) NSMutableArray *viewConstraints; @@ -142,7 +142,7 @@ NS_ASSUME_NONNULL_BEGIN return NSStringFromClass(self.class); } -- (void)configureWithThread:(ThreadModel *)thread +- (void)configureWithThread:(ThreadViewModel *)thread contactsManager:(OWSContactsManager *)contactsManager blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet { @@ -239,7 +239,7 @@ NS_ASSUME_NONNULL_BEGIN return; } - ThreadModel *thread = self.thread; + ThreadViewModel *thread = self.thread; if (thread == nil) { OWSFail(@"%@ thread should not be nil", self.logTag); self.avatarView.image = nil; @@ -251,7 +251,7 @@ NS_ASSUME_NONNULL_BEGIN contactsManager:contactsManager]; } -- (NSAttributedString *)attributedSnippetForThread:(ThreadModel *)thread +- (NSAttributedString *)attributedSnippetForThread:(ThreadViewModel *)thread blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet { OWSAssert(thread); @@ -447,7 +447,7 @@ NS_ASSUME_NONNULL_BEGIN self.nameLabel.font = self.nameFont; - ThreadModel *thread = self.thread; + ThreadViewModel *thread = self.thread; if (thread == nil) { OWSFail(@"%@ thread should not be nil", self.logTag); self.nameLabel.attributedText = nil; diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index bd7012831..1d507f74c 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -47,7 +47,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; @property (nonatomic) UISegmentedControl *segmentedControl; @property (nonatomic) id previewingContext; @property (nonatomic) NSSet *blockedPhoneNumberSet; -@property (nonatomic, readonly) NSCache *threadModelCache; +@property (nonatomic, readonly) NSCache *threadViewModelCache; @property (nonatomic) BOOL isViewVisible; @property (nonatomic) BOOL isAppInBackground; @property (nonatomic) BOOL shouldObserveDBModifications; @@ -108,7 +108,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; _messageSender = [Environment current].messageSender; _blockingManager = [OWSBlockingManager sharedManager]; _blockedPhoneNumberSet = [NSSet setWithArray:[_blockingManager blockedPhoneNumbers]]; - _threadModelCache = [NSCache new]; + _threadViewModelCache = [NSCache new]; // Ensure ExperienceUpgradeFinder has been initialized. [ExperienceUpgradeFinder sharedManager]; @@ -476,7 +476,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; - (void)reloadTableViewData { // PERF: come up with a more nuanced cache clearing scheme - [self.threadModelCache removeAllObjects]; + [self.threadViewModelCache removeAllObjects]; [self.tableView reloadData]; } @@ -604,21 +604,21 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; return (NSInteger)[self.threadMappings numberOfItemsInSection:(NSUInteger)section]; } -- (ThreadModel *)threadModelForIndexPath:(NSIndexPath *)indexPath +- (ThreadViewModel *)threadViewModelForIndexPath:(NSIndexPath *)indexPath { TSThread *threadRecord = [self threadForIndexPath:indexPath]; - ThreadModel *_Nullable cachedThreadModel = [self.threadModelCache objectForKey:threadRecord.uniqueId]; - if (cachedThreadModel) { - return cachedThreadModel; + ThreadViewModel *_Nullable cachedThreadViewModel = [self.threadViewModelCache objectForKey:threadRecord.uniqueId]; + if (cachedThreadViewModel) { + return cachedThreadViewModel; } - __block ThreadModel *_Nullable newThreadModel; + __block ThreadViewModel *_Nullable newThreadViewModel; [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { - newThreadModel = [[ThreadModel alloc] initWithThread:threadRecord transaction:transaction]; + newThreadViewModel = [[ThreadViewModel alloc] initWithThread:threadRecord transaction:transaction]; }]; - [self.threadModelCache setObject:newThreadModel forKey:threadRecord.uniqueId]; - return newThreadModel; + [self.threadViewModelCache setObject:newThreadViewModel forKey:threadRecord.uniqueId]; + return newThreadViewModel; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath @@ -626,7 +626,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; HomeViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:HomeViewCell.cellReuseIdentifier]; OWSAssert(cell); - ThreadModel *thread = [self threadModelForIndexPath:indexPath]; + ThreadViewModel *thread = [self threadViewModelForIndexPath:indexPath]; [cell configureWithThread:thread contactsManager:self.contactsManager blockedPhoneNumberSet:self.blockedPhoneNumberSet]; @@ -1041,7 +1041,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; for (YapDatabaseViewRowChange *rowChange in rowChanges) { NSString *key = rowChange.collectionKey.key; OWSAssert(key); - [self.threadModelCache removeObjectForKey:key]; + [self.threadViewModelCache removeObjectForKey:key]; switch (rowChange.type) { case YapDatabaseViewChangeDelete: {