Merge branch 'charlesmchen/styleSearchResults_'

pull/1/head
Matthew Chen 7 years ago
commit 8ebb10b900

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

@ -235,7 +235,7 @@ NS_ASSUME_NONNULL_BEGIN
[stackView autoPinTrailingToSuperviewMarginWithInset:self.iconHMargin];
[stackView autoVCenterInSuperview];
// Ensure that the cell's contents never overflow the cell bounds.
// We pin pin to the superview _edge_ and not _margin_ for the purposes
// We 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.
[stackView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:0 relation:NSLayoutRelationGreaterThanOrEqual];
[stackView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:0 relation:NSLayoutRelationGreaterThanOrEqual];

@ -29,18 +29,22 @@ class ConversationSearchViewController: UITableViewController {
case messages
}
var blockedPhoneNumberSet = Set<String>()
// MARK: View Lifecyle
override func viewDidLoad() {
super.viewDidLoad()
let blockingManager = OWSBlockingManager.shared()
blockedPhoneNumberSet = Set(blockingManager.blockedPhoneNumbers())
tableView.rowHeight = UITableViewAutomaticDimension
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 +135,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 +144,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 +156,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 +169,32 @@ class ConversationSearchViewController: UITableViewController {
return UITableViewCell()
}
cell.configure(searchResult: searchResult)
var overrideSnippet = NSAttributedString()
var overrideDate: Date?
if searchResult.messageId != nil {
if let messageDate = searchResult.messageDate {
overrideDate = messageDate
} else {
owsFail("\(ConversationSearchViewController.logTag) message search result is missing message timestamp")
}
// Note that we only use the snippet for message results,
// not conversation results. HomeViewCell will generate
// a snippet for conversations that reflects the latest
// contents.
if let messageSnippet = searchResult.snippet {
overrideSnippet = NSAttributedString(string: messageSnippet)
} else {
owsFail("\(ConversationSearchViewController.logTag) message search result is missing message snippet")
}
}
cell.configure(withThread: searchResult.thread,
contactsManager: contactsManager,
blockedPhoneNumber: self.blockedPhoneNumberSet,
overrideSnippet: overrideSnippet,
overrideDate: overrideDate)
return cell
}
}
@ -227,154 +255,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 <b> 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"

@ -10,14 +10,18 @@ NS_ASSUME_NONNULL_BEGIN
@interface HomeViewCell : UITableViewCell
+ (CGFloat)rowHeight;
+ (NSString *)cellReuseIdentifier;
- (void)configureWithThread:(ThreadViewModel *)thread
contactsManager:(OWSContactsManager *)contactsManager
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet;
- (void)configureWithThread:(ThreadViewModel *)thread
contactsManager:(OWSContactsManager *)contactsManager
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet
overrideSnippet:(nullable NSAttributedString *)overrideSnippet
overrideDate:(nullable NSDate *)overrideDate;
@end
NS_ASSUME_NONNULL_END

@ -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;
@ -77,6 +79,13 @@ NS_ASSUME_NONNULL_BEGIN
[self.avatarView autoVCenterInSuperview];
[self.avatarView setContentHuggingHigh];
[self.avatarView setCompressionResistanceHigh];
const CGFloat kAvatarMinVMargin = 10;
[self.avatarView autoPinEdgeToSuperviewEdge:ALEdgeTop
withInset:kAvatarMinVMargin
relation:NSLayoutRelationGreaterThanOrEqual];
[self.avatarView autoPinEdgeToSuperviewEdge:ALEdgeBottom
withInset:kAvatarMinVMargin
relation:NSLayoutRelationGreaterThanOrEqual];
self.payloadView = [UIStackView new];
self.payloadView.axis = UILayoutConstraintAxisVertical;
@ -84,10 +93,14 @@ NS_ASSUME_NONNULL_BEGIN
[self.payloadView autoPinLeadingToTrailingEdgeOfView:self.avatarView offset:self.avatarHSpacing];
[self.payloadView autoVCenterInSuperview];
// Ensure that the cell's contents never overflow the cell bounds.
// We pin pin to the superview _edge_ and not _margin_ for the purposes
// We 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:0 relation:NSLayoutRelationGreaterThanOrEqual];
[self.payloadView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:0 relation:NSLayoutRelationGreaterThanOrEqual];
[self.payloadView autoPinEdgeToSuperviewEdge:ALEdgeTop
withInset:kMinVMargin
relation:NSLayoutRelationGreaterThanOrEqual];
[self.payloadView autoPinEdgeToSuperviewEdge:ALEdgeBottom
withInset:kMinVMargin
relation:NSLayoutRelationGreaterThanOrEqual];
// We pin the payloadView traillingEdge later, as part of the "Unread Badge" logic.
self.nameLabel = [UILabel new];
@ -147,6 +160,19 @@ NS_ASSUME_NONNULL_BEGIN
- (void)configureWithThread:(ThreadViewModel *)thread
contactsManager:(OWSContactsManager *)contactsManager
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet
{
[self configureWithThread:thread
contactsManager:contactsManager
blockedPhoneNumberSet:blockedPhoneNumberSet
overrideSnippet:nil
overrideDate:nil];
}
- (void)configureWithThread:(ThreadViewModel *)thread
contactsManager:(OWSContactsManager *)contactsManager
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet
overrideSnippet:(nullable NSAttributedString *)overrideSnippet
overrideDate:(nullable NSDate *)overrideDate
{
OWSAssertIsOnMainThread();
OWSAssert(thread);
@ -168,13 +194,21 @@ 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
= (overrideDate ? [self stringForDate:overrideDate] : [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;

@ -6,6 +6,8 @@
#import <SignalMessaging/OWSViewController.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class TSThread;
@interface HomeViewController : OWSViewController
@ -25,3 +27,5 @@
animatePresentation:(BOOL)animatePresentation;
@end
NS_ASSUME_NONNULL_END

@ -31,6 +31,8 @@
#import <YapDatabase/YapDatabaseViewChange.h>
#import <YapDatabase/YapDatabaseViewConnection.h>
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<UIViewControllerPreviewing>)previewingContext
viewControllerForLocation:(CGPoint)location
- (nullable UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext
viewControllerForLocation:(CGPoint)location
{
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
@ -785,7 +789,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
[stackView autoPinEdgeToSuperviewMargin:ALEdgeLeading relation:NSLayoutRelationGreaterThanOrEqual];
[stackView autoPinEdgeToSuperviewMargin:ALEdgeTrailing relation:NSLayoutRelationGreaterThanOrEqual];
// Ensure that the cell's contents never overflow the cell bounds.
// We pin pin to the superview _edge_ and not _margin_ for the purposes
// We 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.
[stackView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:0 relation:NSLayoutRelationGreaterThanOrEqual];
[stackView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:0 relation:NSLayoutRelationGreaterThanOrEqual];
@ -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

@ -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;
@ -429,19 +432,22 @@ NS_ASSUME_NONNULL_BEGIN
NSArray<SignalAccount *> *signalAccounts = collatedSignalAccounts[i];
NSMutableArray <OWSTableItem *> *contactItems = [NSMutableArray new];
for (SignalAccount *signalAccount in signalAccounts) {
[contactItems addObject:[OWSTableItem itemWithCustomCellBlock:^{
ContactTableViewCell *cell = [ContactTableViewCell new];
BOOL isBlocked = [self.contactsViewHelper isRecipientIdBlocked:signalAccount.recipientId];
if (isBlocked) {
cell.accessoryMessage
= NSLocalizedString(@"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked.");
}
[cell configureWithSignalAccount:signalAccount contactsManager:self.contactsViewHelper.contactsManager];
return cell;
}
customRowHeight:[ContactTableViewCell rowHeight]
[contactItems addObject:[OWSTableItem
itemWithCustomCellBlock:^{
ContactTableViewCell *cell = [ContactTableViewCell new];
BOOL isBlocked = [self.contactsViewHelper
isRecipientIdBlocked:signalAccount.recipientId];
if (isBlocked) {
cell.accessoryMessage = NSLocalizedString(@"CONTACT_CELL_IS_BLOCKED",
@"An indicator that a contact has been blocked.");
}
[cell configureWithSignalAccount:signalAccount
contactsManager:self.contactsViewHelper.contactsManager];
return cell;
}
customRowHeight:UITableViewAutomaticDimension
actionBlock:^{
[weakSelf newConversationWithRecipientId:signalAccount.recipientId];
}]];

@ -112,7 +112,7 @@ const CGFloat kOWSTable_DefaultCellHeight = 45.f;
actionBlock:(nullable OWSTableActionBlock)actionBlock
{
OWSAssert(customCell);
OWSAssert(customRowHeight > 0);
OWSAssert(customRowHeight > 0 || customRowHeight == UITableViewAutomaticDimension);
OWSTableItem *item = [OWSTableItem new];
item.actionBlock = actionBlock;
@ -125,7 +125,7 @@ const CGFloat kOWSTable_DefaultCellHeight = 45.f;
customRowHeight:(CGFloat)customRowHeight
actionBlock:(nullable OWSTableActionBlock)actionBlock
{
OWSAssert(customRowHeight > 0);
OWSAssert(customRowHeight > 0 || customRowHeight == UITableViewAutomaticDimension);
OWSTableItem *item = [self itemWithCustomCellBlock:customCellBlock actionBlock:actionBlock];
item.customRowHeight = @(customRowHeight);
@ -177,7 +177,7 @@ const CGFloat kOWSTable_DefaultCellHeight = 45.f;
customRowHeight:(CGFloat)customRowHeight
actionBlock:(nullable OWSTableActionBlock)actionBlock
{
OWSAssert(customRowHeight > 0);
OWSAssert(customRowHeight > 0 || customRowHeight == UITableViewAutomaticDimension);
OWSTableItem *item = [self disclosureItemWithText:text actionBlock:actionBlock];
item.customRowHeight = @(customRowHeight);
@ -237,7 +237,7 @@ const CGFloat kOWSTable_DefaultCellHeight = 45.f;
customRowHeight:(CGFloat)customRowHeight
actionBlock:(nullable OWSTableSubPageBlock)actionBlock
{
OWSAssert(customRowHeight > 0);
OWSAssert(customRowHeight > 0 || customRowHeight == UITableViewAutomaticDimension);
OWSTableItem *item = [self subPageItemWithText:text actionBlock:actionBlock];
item.customRowHeight = @(customRowHeight);
@ -285,7 +285,7 @@ const CGFloat kOWSTable_DefaultCellHeight = 45.f;
+ (OWSTableItem *)softCenterLabelItemWithText:(NSString *)text customRowHeight:(CGFloat)customRowHeight
{
OWSAssert(customRowHeight > 0);
OWSAssert(customRowHeight > 0 || customRowHeight == UITableViewAutomaticDimension);
OWSTableItem *item = [self softCenterLabelItemWithText:text];
item.customRowHeight = @(customRowHeight);

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

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

@ -9,16 +9,18 @@ public class ConversationSearchResult: Comparable {
public let thread: ThreadViewModel
public let messageId: String?
public let messageDate: Date?
public let snippet: String?
private let sortKey: UInt64
init(thread: ThreadViewModel, messageId: String?, snippet: String?, sortKey: UInt64) {
init(thread: ThreadViewModel, sortKey: UInt64, messageId: String? = nil, messageDate: Date? = nil, snippet: String? = nil) {
self.thread = thread
self.sortKey = sortKey
self.messageId = messageId
self.messageDate = messageDate
self.snippet = snippet
self.sortKey = sortKey
}
// Mark: Comparable
@ -106,11 +108,11 @@ public class ConversationSearcher: NSObject {
var existingConversationRecipientIds: Set<String> = Set()
self.finder.enumerateObjects(searchText: searchText, transaction: transaction) { (match: Any, snippet: String?) in
if let thread = match as? TSThread {
let threadViewModel = ThreadViewModel(thread: thread, transaction: transaction)
let snippet: String? = thread.lastMessageText(transaction: transaction)
let sortKey = NSDate.ows_millisecondsSince1970(for: threadViewModel.lastMessageDate)
let searchResult = ConversationSearchResult(thread: threadViewModel, messageId: nil, snippet: snippet, sortKey: sortKey)
let searchResult = ConversationSearchResult(thread: threadViewModel, sortKey: sortKey)
if let contactThread = thread as? TSContactThread {
let recipientId = contactThread.contactIdentifier()
@ -123,7 +125,12 @@ public class ConversationSearcher: NSObject {
let threadViewModel = ThreadViewModel(thread: thread, transaction: transaction)
let sortKey = message.timestamp
let searchResult = ConversationSearchResult(thread: threadViewModel, messageId: message.uniqueId, snippet: snippet, sortKey: sortKey)
let searchResult = ConversationSearchResult(thread: threadViewModel,
sortKey: sortKey,
messageId: message.uniqueId,
messageDate: NSDate.ows_date(withMillisecondsSince1970: message.timestamp),
snippet: snippet)
messages.append(searchResult)
} else if let signalAccount = match as? SignalAccount {
let searchResult = ContactSearchResult(signalAccount: signalAccount, contactsManager: contactsManager)
@ -187,8 +194,6 @@ public class ConversationSearcher: NSObject {
}
}
// MARK: - Helpers
// MARK: Searchers
private lazy var groupThreadSearcher: Searcher<TSGroupThread> = Searcher { (groupThread: TSGroupThread) in

@ -50,8 +50,10 @@ public class FullTextSearchFinder: NSObject {
let maxSearchResults = 500
var searchResultCount = 0
// (snippet: String, collection: String, key: String, object: Any, stop: UnsafeMutablePointer<ObjCBool>)
ext.enumerateKeysAndObjects(matching: prefixQuery, with: nil) { (snippet: String, _: String, _: String, object: Any, stop: UnsafeMutablePointer<ObjCBool>) in
let snippetOptions = YapDatabaseFullTextSearchSnippetOptions()
snippetOptions.startMatchText = ""
snippetOptions.endMatchText = ""
ext.enumerateKeysAndObjects(matching: prefixQuery, with: snippetOptions) { (snippet: String, _: String, _: String, object: Any, stop: UnsafeMutablePointer<ObjCBool>) in
guard searchResultCount < maxSearchResults else {
stop.pointee = true
return

Loading…
Cancel
Save