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