diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index df9d0ad23..9dbf6c231 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -7,11 +7,13 @@ // Separate iOS Frameworks from other imports. #import "AppSettingsViewController.h" +#import "ContactTableViewCell.h" #import "ConversationViewItem.h" #import "DateUtil.h" #import "DebugUIPage.h" #import "DebugUITableViewController.h" #import "FingerprintViewController.h" +#import "HomeViewCell.h" #import "HomeViewController.h" #import "MediaDetailViewController.h" #import "NotificationSettingsViewController.h" diff --git a/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift b/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift index 24b1a7c4d..778367716 100644 --- a/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift +++ b/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift @@ -29,8 +29,21 @@ class ConversationSearchViewController: UITableViewController { case messages } + var contactsManager: OWSContactsManager { + return Environment.current().contactsManager + } + + var blockedPhoneNumberSet = Set() + // MARK: View Lifecyle + override public func loadView() { + super.loadView() + + let blockingManager = OWSBlockingManager.shared() + blockedPhoneNumberSet = Set(blockingManager.blockedPhoneNumbers()) + } + override func viewDidLoad() { super.viewDidLoad() @@ -38,9 +51,8 @@ class ConversationSearchViewController: UITableViewController { tableView.estimatedRowHeight = 60 tableView.register(EmptySearchResultCell.self, forCellReuseIdentifier: EmptySearchResultCell.reuseIdentifier) - tableView.register(ConversationSearchResultCell.self, forCellReuseIdentifier: ConversationSearchResultCell.reuseIdentifier) - tableView.register(MessageSearchResultCell.self, forCellReuseIdentifier: MessageSearchResultCell.reuseIdentifier) - tableView.register(ContactSearchResultCell.self, forCellReuseIdentifier: ContactSearchResultCell.reuseIdentifier) + tableView.register(HomeViewCell.self, forCellReuseIdentifier: HomeViewCell.cellReuseIdentifier()) + tableView.register(ContactTableViewCell.self, forCellReuseIdentifier: ContactTableViewCell.reuseIdentifier()) } // MARK: UITableViewDelegate @@ -131,7 +143,7 @@ class ConversationSearchViewController: UITableViewController { cell.configure(searchText: searchText) return cell case .conversations: - guard let cell = tableView.dequeueReusableCell(withIdentifier: ConversationSearchResultCell.reuseIdentifier) as? ConversationSearchResultCell else { + guard let cell = tableView.dequeueReusableCell(withIdentifier: HomeViewCell.cellReuseIdentifier()) as? HomeViewCell else { owsFail("cell was unexpectedly nil") return UITableViewCell() } @@ -140,10 +152,10 @@ class ConversationSearchViewController: UITableViewController { owsFail("searchResult was unexpectedly nil") return UITableViewCell() } - cell.configure(searchResult: searchResult) + cell.configure(withThread: searchResult.thread, contactsManager: contactsManager, blockedPhoneNumber: self.blockedPhoneNumberSet) return cell case .contacts: - guard let cell = tableView.dequeueReusableCell(withIdentifier: ContactSearchResultCell.reuseIdentifier) as? ContactSearchResultCell else { + guard let cell = tableView.dequeueReusableCell(withIdentifier: ContactTableViewCell.reuseIdentifier()) as? ContactTableViewCell else { owsFail("cell was unexpectedly nil") return UITableViewCell() } @@ -152,11 +164,10 @@ class ConversationSearchViewController: UITableViewController { owsFail("searchResult was unexpectedly nil") return UITableViewCell() } - - cell.configure(searchResult: searchResult) + cell.configure(with: searchResult.signalAccount, contactsManager: contactsManager) return cell case .messages: - guard let cell = tableView.dequeueReusableCell(withIdentifier: MessageSearchResultCell.reuseIdentifier) as? MessageSearchResultCell else { + guard let cell = tableView.dequeueReusableCell(withIdentifier: HomeViewCell.cellReuseIdentifier()) as? HomeViewCell else { owsFail("cell was unexpectedly nil") return UITableViewCell() } @@ -166,7 +177,44 @@ class ConversationSearchViewController: UITableViewController { return UITableViewCell() } - cell.configure(searchResult: searchResult) + var overrideSnippet = NSAttributedString() + var overrideTimestamp: NSNumber? + self.uiDatabaseConnection.read { transaction in + guard let messageId = searchResult.messageId else { + owsFail("\(ConversationSearchViewController.logTag) message search result is missing message id") + return + } + guard let message = TSInteraction.fetch(uniqueId: messageId, transaction: transaction) else { + // This could happen if the message has disappeared, etc. + Logger.error("\(ConversationSearchViewController.logTag) could not load message") + return + } + overrideTimestamp = NSNumber(value: message.timestamp) + + guard let snippet = searchResult.snippet else { + owsFail("\(ConversationSearchViewController.logTag) message search result is missing snippet") + return + } + guard let snippetData = snippet.data(using: String.Encoding.utf8) else { + owsFail("\(ConversationSearchViewController.logTag) message search result has invalid snippet") + return + } + do { + let snippetOptions = [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html] + overrideSnippet = try NSAttributedString(data: snippetData, + options: snippetOptions, + documentAttributes: nil) + } catch { + owsFail("\(ConversationSearchViewController.logTag) could not parse message snippet") + } + } + + cell.configure(withThread: searchResult.thread, + contactsManager: contactsManager, + blockedPhoneNumber: self.blockedPhoneNumberSet, + overrideSnippet: overrideSnippet, + overrideTimestamp: overrideTimestamp) + return cell } } @@ -227,154 +275,6 @@ class ConversationSearchViewController: UITableViewController { } } -class ConversationSearchResultCell: UITableViewCell { - static let reuseIdentifier = "ConversationSearchResultCell" - - let nameLabel: UILabel - let snippetLabel: UILabel - let avatarView: AvatarImageView - let avatarWidth: UInt = 40 - - override init(style: UITableViewCellStyle, reuseIdentifier: String?) { - self.nameLabel = UILabel() - self.snippetLabel = UILabel() - self.avatarView = AvatarImageView() - avatarView.autoSetDimensions(to: CGSize(width: CGFloat(avatarWidth), height: CGFloat(avatarWidth))) - - super.init(style: style, reuseIdentifier: reuseIdentifier) - - nameLabel.font = UIFont.ows_dynamicTypeBody.ows_mediumWeight() - snippetLabel.font = UIFont.ows_dynamicTypeFootnote - - let textRows = UIStackView(arrangedSubviews: [nameLabel, snippetLabel]) - textRows.axis = .vertical - - let columns = UIStackView(arrangedSubviews: [avatarView, textRows]) - columns.axis = .horizontal - columns.spacing = 8 - - contentView.addSubview(columns) - columns.autoPinEdgesToSuperviewMargins() - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - var contactsManager: OWSContactsManager { - return Environment.current().contactsManager - } - - func configure(searchResult: ConversationSearchResult) { - self.avatarView.image = OWSAvatarBuilder.buildImage(thread: searchResult.thread.threadRecord, diameter: avatarWidth, contactsManager: self.contactsManager) - self.nameLabel.text = searchResult.thread.name - self.snippetLabel.text = searchResult.snippet - } -} - -class MessageSearchResultCell: UITableViewCell { - static let reuseIdentifier = "MessageSearchResultCell" - - let nameLabel: UILabel - let snippetLabel: UILabel - - override init(style: UITableViewCellStyle, reuseIdentifier: String?) { - self.nameLabel = UILabel() - self.snippetLabel = UILabel() - - super.init(style: style, reuseIdentifier: reuseIdentifier) - - nameLabel.font = UIFont.ows_dynamicTypeBody.ows_mediumWeight() - snippetLabel.font = UIFont.ows_dynamicTypeFootnote - - let textRows = UIStackView(arrangedSubviews: [nameLabel, snippetLabel]) - textRows.axis = .vertical - - contentView.addSubview(textRows) - textRows.autoPinEdgesToSuperviewMargins() - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func configure(searchResult: ConversationSearchResult) { - self.nameLabel.text = searchResult.thread.name - - guard let snippet = searchResult.snippet else { - self.snippetLabel.text = nil - return - } - - guard let encodedString = snippet.data(using: .utf8) else { - self.snippetLabel.text = nil - return - } - - // Bold snippet text - do { - - // FIXME - The snippet marks up the matched search text with tags. - // We can parse this into an attributed string, but it also takes on an undesirable font. - // We want to apply our own font without clobbering bold in the process - maybe by enumerating and inspecting the attributes? Or maybe we can pass in a base font? - let attributedSnippet = try NSMutableAttributedString(data: encodedString, - options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html], - documentAttributes: nil) - attributedSnippet.addAttribute(NSAttributedStringKey.font, value: self.snippetLabel.font, range: NSRange(location: 0, length: attributedSnippet.length)) - - self.snippetLabel.attributedText = attributedSnippet - } catch { - owsFail("failed to generate snippet: \(error)") - } - } -} - -class ContactSearchResultCell: UITableViewCell { - static let reuseIdentifier = "ContactSearchResultCell" - - let nameLabel: UILabel - let snippetLabel: UILabel - let avatarView: AvatarImageView - let avatarWidth: UInt = 40 - - override init(style: UITableViewCellStyle, reuseIdentifier: String?) { - self.nameLabel = UILabel() - self.snippetLabel = UILabel() - self.avatarView = AvatarImageView() - avatarView.autoSetDimensions(to: CGSize(width: CGFloat(avatarWidth), height: CGFloat(avatarWidth))) - - super.init(style: style, reuseIdentifier: reuseIdentifier) - - nameLabel.font = UIFont.ows_dynamicTypeBody.ows_mediumWeight() - snippetLabel.font = UIFont.ows_dynamicTypeFootnote - - let textRows = UIStackView(arrangedSubviews: [nameLabel, snippetLabel]) - textRows.axis = .vertical - - let columns = UIStackView(arrangedSubviews: [avatarView, textRows]) - columns.axis = .horizontal - columns.spacing = 8 - - contentView.addSubview(columns) - columns.autoPinEdgesToSuperviewMargins() - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - var contactsManager: OWSContactsManager { - return Environment.current().contactsManager - } - - func configure(searchResult: ContactSearchResult) { - let avatarBuilder = OWSContactAvatarBuilder.init(signalId: searchResult.recipientId, diameter: avatarWidth, contactsManager: contactsManager) - self.avatarView.image = avatarBuilder.build() - self.nameLabel.text = self.contactsManager.displayName(forPhoneIdentifier: searchResult.recipientId) - self.snippetLabel.text = searchResult.recipientId - } -} - class EmptySearchResultCell: UITableViewCell { static let reuseIdentifier = "EmptySearchResultCell" diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.h b/Signal/src/ViewControllers/HomeView/HomeViewCell.h index 054b9fcb5..c715ba40d 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.h +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.h @@ -10,14 +10,18 @@ NS_ASSUME_NONNULL_BEGIN @interface HomeViewCell : UITableViewCell -+ (CGFloat)rowHeight; - + (NSString *)cellReuseIdentifier; - (void)configureWithThread:(ThreadViewModel *)thread contactsManager:(OWSContactsManager *)contactsManager blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet; +- (void)configureWithThread:(ThreadViewModel *)thread + contactsManager:(OWSContactsManager *)contactsManager + blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet + overrideSnippet:(nullable NSAttributedString *)overrideSnippet + overrideTimestamp:(nullable NSNumber *)overrideTimestamp; + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index 283f35887..16c4dc067 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -59,6 +59,8 @@ NS_ASSUME_NONNULL_BEGIN { OWSAssert(!self.avatarView); + const CGFloat kMinVMargin = 5; + [self setTranslatesAutoresizingMaskIntoConstraints:NO]; self.layoutMargins = UIEdgeInsetsMake(0, self.cellHMargin, 0, self.cellHMargin); self.contentView.layoutMargins = UIEdgeInsetsZero; @@ -127,6 +129,16 @@ NS_ASSUME_NONNULL_BEGIN [self.unreadLabel autoCenterInSuperview]; [self.unreadLabel setContentHuggingHigh]; [self.unreadLabel setCompressionResistanceHigh]; + + // Ensure that the cell's contents never overflow the cell bounds. + // We pin pin to the superview _edge_ and not _margin_ for the purposes + // of overflow, so that changes to the margins do not trip these safe guards. + [self.payloadView autoPinEdgeToSuperviewEdge:ALEdgeTop + withInset:kMinVMargin + relation:NSLayoutRelationGreaterThanOrEqual]; + [self.payloadView autoPinEdgeToSuperviewEdge:ALEdgeBottom + withInset:kMinVMargin + relation:NSLayoutRelationGreaterThanOrEqual]; } + (NSString *)cellReuseIdentifier @@ -147,6 +159,19 @@ NS_ASSUME_NONNULL_BEGIN - (void)configureWithThread:(ThreadViewModel *)thread contactsManager:(OWSContactsManager *)contactsManager blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet +{ + [self configureWithThread:thread + contactsManager:contactsManager + blockedPhoneNumberSet:blockedPhoneNumberSet + overrideSnippet:nil + overrideTimestamp:nil]; +} + +- (void)configureWithThread:(ThreadViewModel *)thread + contactsManager:(OWSContactsManager *)contactsManager + blockedPhoneNumberSet:(NSSet *)blockedPhoneNumberSet + overrideSnippet:(nullable NSAttributedString *)overrideSnippet + overrideTimestamp:(nullable NSNumber *)overrideTimestamp { OWSAssertIsOnMainThread(); OWSAssert(thread); @@ -168,13 +193,22 @@ NS_ASSUME_NONNULL_BEGIN self.payloadView.spacing = 0.f; self.topRowView.spacing = self.topRowHSpacing; + if (overrideSnippet) { + self.snippetLabel.attributedText = overrideSnippet; + } else { + self.snippetLabel.attributedText = + [self attributedSnippetForThread:thread blockedPhoneNumberSet:blockedPhoneNumberSet]; + } // We update the fonts every time this cell is configured to ensure that // changes to the dynamic type settings are reflected. + // + // Note: we apply this font _after_ we set the attributed text to + // override any font attributes. self.snippetLabel.font = [self snippetFont]; - self.snippetLabel.attributedText = - [self attributedSnippetForThread:thread blockedPhoneNumberSet:blockedPhoneNumberSet]; - self.dateTimeLabel.text = [self stringForDate:thread.lastMessageDate]; + self.dateTimeLabel.text = (overrideTimestamp + ? [self stringForDate:[NSDate ows_dateWithMillisecondsSince1970:overrideTimestamp.unsignedLongLongValue]] + : [self stringForDate:thread.lastMessageDate]); if (hasUnreadMessages) { self.dateTimeLabel.textColor = [UIColor ows_blackColor]; @@ -203,19 +237,22 @@ NS_ASSUME_NONNULL_BEGIN // Spec check. Should be 12pts (6pt on each side) when using default font size. OWSAssert(UIFont.ows_dynamicTypeBodyFont.pointSize != 17 || minMargin == 12); - [self.viewConstraints addObject:[self.unreadBadge autoMatchDimension:ALDimensionWidth - toDimension:ALDimensionWidth - ofView:self.unreadLabel - withOffset:minMargin]]; + [self.viewConstraints addObjectsFromArray:@[ + [self.unreadBadge autoMatchDimension:ALDimensionWidth + toDimension:ALDimensionWidth + ofView:self.unreadLabel + withOffset:minMargin], + // badge sizing + [self.unreadBadge autoSetDimension:ALDimensionWidth + toSize:unreadBadgeHeight + relation:NSLayoutRelationGreaterThanOrEqual], + [self.unreadBadge autoSetDimension:ALDimensionHeight toSize:unreadBadgeHeight], + ]]; + }]; + const CGFloat kMinVMargin = 5; [self.viewConstraints addObjectsFromArray:@[ - // badge sizing - [self.unreadBadge autoSetDimension:ALDimensionWidth - toSize:unreadBadgeHeight - relation:NSLayoutRelationGreaterThanOrEqual], - [self.unreadBadge autoSetDimension:ALDimensionHeight toSize:unreadBadgeHeight], - // Horizontally, badge is inserted after the tail of the payloadView, pushing back the date *and* snippet // view [self.payloadView autoPinEdge:ALEdgeTrailing @@ -223,6 +260,12 @@ NS_ASSUME_NONNULL_BEGIN ofView:self.unreadBadge withOffset:-self.topRowHSpacing], [self.unreadBadge autoPinTrailingToSuperviewMargin], + [self.unreadBadge autoPinEdgeToSuperviewEdge:ALEdgeTop + withInset:kMinVMargin + relation:NSLayoutRelationGreaterThanOrEqual], + [self.unreadBadge autoPinEdgeToSuperviewEdge:ALEdgeBottom + withInset:kMinVMargin + relation:NSLayoutRelationGreaterThanOrEqual], // Vertically, badge is positioned vertically by aligning it's label *subview's* baseline. // This allows us a single visual baseline of text across the top row across [name, dateTime, @@ -367,21 +410,6 @@ NS_ASSUME_NONNULL_BEGIN return minValue * alpha; } -+ (CGFloat)rowHeight -{ - // Scale the cell height using size of dynamic "body" type as a reference. - const CGFloat kReferenceFontSizeMin = 17.f; - const CGFloat kReferenceFontSizeMax = 23.f; - CGFloat referenceFontSize = UIFont.ows_dynamicTypeBodyFont.pointSize; - CGFloat alpha = CGFloatClamp01(CGFloatInverseLerp(referenceFontSize, kReferenceFontSizeMin, kReferenceFontSizeMax)); - - const CGFloat kCellHeightMin = 68.f; - const CGFloat kCellHeightMax = 80.f; - CGFloat result = ceil(CGFloatLerp(kCellHeightMin, kCellHeightMax, alpha)); - - return result; -} - - (NSUInteger)cellHMargin { return 16; diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.h b/Signal/src/ViewControllers/HomeView/HomeViewController.h index d86824b5b..821c34a85 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.h +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.h @@ -6,6 +6,8 @@ #import #import +NS_ASSUME_NONNULL_BEGIN + @class TSThread; @interface HomeViewController : OWSViewController @@ -25,3 +27,5 @@ animatePresentation:(BOOL)animatePresentation; @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index fb4d3c048..f4e7ec4aa 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -31,6 +31,8 @@ #import #import +NS_ASSUME_NONNULL_BEGIN + typedef NS_ENUM(NSInteger, HomeViewMode) { HomeViewMode_Archive, HomeViewMode_Inbox, @@ -100,7 +102,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations return self; } -- (instancetype)initWithCoder:(NSCoder *)aDecoder +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { OWSFail(@"Do not load this from the storyboard."); @@ -227,6 +229,8 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations [self.tableView autoPinWidthToSuperview]; [self.tableView autoPinEdgeToSuperviewEdge:ALEdgeBottom]; [self.tableView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:missingContactsPermissionView]; + self.tableView.rowHeight = UITableViewAutomaticDimension; + self.tableView.estimatedRowHeight = 60; UILabel *emptyBoxLabel = [UILabel new]; self.emptyBoxLabel = emptyBoxLabel; @@ -409,8 +413,8 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations [self presentViewController:navigationController animated:YES completion:nil]; } -- (UIViewController *)previewingContext:(id)previewingContext - viewControllerForLocation:(CGPoint)location +- (nullable UIViewController *)previewingContext:(id)previewingContext + viewControllerForLocation:(CGPoint)location { NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location]; @@ -804,11 +808,6 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations return thread; } -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -{ - return HomeViewCell.rowHeight; -} - - (void)pullToRefreshPerformed:(UIRefreshControl *)refreshControl { OWSAssertIsOnMainThread(); @@ -828,7 +827,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations return; } -- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath +- (nullable NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath { if ([self isIndexPathForArchivedConversations:indexPath]) { return @[]; @@ -1370,3 +1369,5 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations } @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/NewContactThreadViewController.m b/Signal/src/ViewControllers/NewContactThreadViewController.m index 886ba2ba8..ad070e939 100644 --- a/Signal/src/ViewControllers/NewContactThreadViewController.m +++ b/Signal/src/ViewControllers/NewContactThreadViewController.m @@ -124,6 +124,9 @@ NS_ASSUME_NONNULL_BEGIN [self.view addSubview:self.tableViewController.view]; [_tableViewController.view autoPinWidthToSuperview]; + self.tableViewController.tableView.rowHeight = UITableViewAutomaticDimension; + self.tableViewController.tableView.estimatedRowHeight = 60; + [_tableViewController.view autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:contactsPermissionReminderView]; [self autoPinViewToBottomOfViewControllerOrKeyboard:self.tableViewController.view]; _tableViewController.tableView.tableHeaderView = searchBar; diff --git a/SignalMessaging/Views/ContactTableViewCell.h b/SignalMessaging/Views/ContactTableViewCell.h index be11ee3ca..b62dcadaf 100644 --- a/SignalMessaging/Views/ContactTableViewCell.h +++ b/SignalMessaging/Views/ContactTableViewCell.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSContactsManager.h" @@ -12,7 +12,6 @@ NS_ASSUME_NONNULL_BEGIN -extern NSString *const kContactsTable_CellReuseIdentifier; extern const NSUInteger kContactTableViewCellAvatarSize; extern const CGFloat kContactTableViewCellAvatarTextMargin; @@ -25,7 +24,7 @@ extern const CGFloat kContactTableViewCellAvatarTextMargin; @property (nonatomic, nullable) NSString *accessoryMessage; @property (nonatomic, readonly) UILabel *subtitle; -+ (nullable NSString *)reuseIdentifier; ++ (NSString *)reuseIdentifier; + (CGFloat)rowHeight; diff --git a/SignalMessaging/Views/ContactTableViewCell.m b/SignalMessaging/Views/ContactTableViewCell.m index eadbc4cb2..d5dfd2700 100644 --- a/SignalMessaging/Views/ContactTableViewCell.m +++ b/SignalMessaging/Views/ContactTableViewCell.m @@ -18,7 +18,6 @@ NS_ASSUME_NONNULL_BEGIN -NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseIdentifier"; const NSUInteger kContactTableViewCellAvatarSize = 40; const CGFloat kContactTableViewCellAvatarTextMargin = 12; @@ -37,20 +36,15 @@ const CGFloat kContactTableViewCellAvatarTextMargin = 12; @implementation ContactTableViewCell -- (instancetype)init +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(nullable NSString *)reuseIdentifier { - if (self = [super init]) { + if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { [self configureProgrammatically]; } return self; } -+ (nullable NSString *)reuseIdentifier -{ - return NSStringFromClass(self.class); -} - -- (nullable NSString *)reuseIdentifier ++ (NSString *)reuseIdentifier { return NSStringFromClass(self.class); } @@ -62,11 +56,15 @@ const CGFloat kContactTableViewCellAvatarTextMargin = 12; + (CGFloat)rowHeight { - return 59.f; + return 60.f; } - (void)configureProgrammatically { + OWSAssert(!self.nameLabel); + + const CGFloat kMinVMargin = 5; + self.preservesSuperviewLayoutMargins = YES; self.contentView.preservesSuperviewLayoutMargins = YES; @@ -110,6 +108,22 @@ const CGFloat kContactTableViewCellAvatarTextMargin = 12; [_nameContainerView autoPinLeadingToTrailingEdgeOfView:_avatarView offset:kContactTableViewCellAvatarTextMargin]; [_nameContainerView autoPinTrailingToSuperviewMargin]; + // Ensure that the cell's contents never overflow the cell bounds. + // We pin pin to the superview _edge_ and not _margin_ for the purposes + // of overflow, so that changes to the margins do not trip these safe guards. + [_avatarView autoPinEdgeToSuperviewEdge:ALEdgeTop + withInset:kMinVMargin + relation:NSLayoutRelationGreaterThanOrEqual]; + [_avatarView autoPinEdgeToSuperviewEdge:ALEdgeBottom + withInset:kMinVMargin + relation:NSLayoutRelationGreaterThanOrEqual]; + [_nameContainerView autoPinEdgeToSuperviewEdge:ALEdgeTop + withInset:kMinVMargin + relation:NSLayoutRelationGreaterThanOrEqual]; + [_nameContainerView autoPinEdgeToSuperviewEdge:ALEdgeBottom + withInset:kMinVMargin + relation:NSLayoutRelationGreaterThanOrEqual]; + [self configureFonts]; // Force layout, since imageView isn't being initally rendered on App Store optimized build. diff --git a/SignalMessaging/utils/ConversationSearcher.swift b/SignalMessaging/utils/ConversationSearcher.swift index e13156c72..2e87e0fa0 100644 --- a/SignalMessaging/utils/ConversationSearcher.swift +++ b/SignalMessaging/utils/ConversationSearcher.swift @@ -187,8 +187,6 @@ public class ConversationSearcher: NSObject { } } - // MARK: - Helpers - // MARK: Searchers private lazy var groupThreadSearcher: Searcher = Searcher { (groupThread: TSGroupThread) in @@ -202,11 +200,13 @@ public class ConversationSearcher: NSObject { private lazy var contactThreadSearcher: Searcher = Searcher { (contactThread: TSContactThread) in let recipientId = contactThread.contactIdentifier() + Logger.verbose("contactThreadSearcher: \(self.indexingString(recipientId: recipientId))") return self.indexingString(recipientId: recipientId) } private lazy var signalAccountSearcher: Searcher = Searcher { (signalAccount: SignalAccount) in let recipientId = signalAccount.recipientId + Logger.verbose("signalAccountSearcher: \(self.indexingString(recipientId: recipientId))") return self.indexingString(recipientId: recipientId) }