Introduce Thread view model

// FREEBIE
This commit is contained in:
Michael Kirk 2018-04-21 11:12:58 -04:00
parent 1fb1b5bbe2
commit 5f2b38c50b
10 changed files with 155 additions and 51 deletions

View File

@ -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 = "<group>"; };
4539B5851F79348F007141FF /* PushRegistrationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushRegistrationManager.swift; sourceTree = "<group>"; };
453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = "<group>"; };
4542DF51208B82E9007B4E76 /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; };
45464DBB1DFA041F001D3FD6 /* DataChannelMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataChannelMessage.swift; sourceTree = "<group>"; };
454A84032059C787008B8C75 /* MediaTileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaTileViewController.swift; sourceTree = "<group>"; };
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 = "<group>";
@ -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 */,

View File

@ -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<String>) {
// 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
}
}

View File

@ -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<NSString *> *)blockedPhoneNumberSet
transaction:(YapDatabaseReadTransaction *)transaction;
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet;
@end

View File

@ -12,7 +12,6 @@
#import <SignalServiceKit/OWSMessageManager.h>
#import <SignalServiceKit/TSContactThread.h>
#import <SignalServiceKit/TSGroupThread.h>
#import <SignalServiceKit/TSThread.h>
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<NSLayoutConstraint *> *viewConstraints;
@ -143,16 +142,14 @@ NS_ASSUME_NONNULL_BEGIN
return NSStringFromClass(self.class);
}
- (void)configureWithThread:(TSThread *)thread
contactsManager:(OWSContactsManager *)contactsManager
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet
transaction:(YapDatabaseReadTransaction *)transaction
- (void)configureWithThread:(ThreadModel *)thread
contactsManager:(OWSContactsManager *)contactsManager
blockedPhoneNumberSet:(NSSet<NSString *> *)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<NSString *> *)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;

View File

@ -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];
}

View File

@ -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.

View File

@ -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<OWSReadTracking> 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)]) {

View File

@ -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];

View File

@ -15,7 +15,6 @@ NS_ASSUME_NONNULL_BEGIN
- (NSUInteger)unreadMessagesCount;
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread;
- (NSUInteger)unreadMessagesInThread:(TSThread *)thread;
- (void)updateApplicationBadgeCount;

View File

@ -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