Style the search results.
This commit is contained in:
parent
5fec9b7c18
commit
f4a559156c
@ -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"
|
||||
|
||||
@ -29,8 +29,21 @@ class ConversationSearchViewController: UITableViewController {
|
||||
case messages
|
||||
}
|
||||
|
||||
var contactsManager: OWSContactsManager {
|
||||
return Environment.current().contactsManager
|
||||
}
|
||||
|
||||
var blockedPhoneNumberSet = Set<String>()
|
||||
|
||||
// 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 <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
|
||||
overrideTimestamp:(nullable NSNumber *)overrideTimestamp;
|
||||
|
||||
@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;
|
||||
@ -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<NSString *> *)blockedPhoneNumberSet
|
||||
{
|
||||
[self configureWithThread:thread
|
||||
contactsManager:contactsManager
|
||||
blockedPhoneNumberSet:blockedPhoneNumberSet
|
||||
overrideSnippet:nil
|
||||
overrideTimestamp:nil];
|
||||
}
|
||||
|
||||
- (void)configureWithThread:(ThreadViewModel *)thread
|
||||
contactsManager:(OWSContactsManager *)contactsManager
|
||||
blockedPhoneNumberSet:(NSSet<NSString *> *)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;
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 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.
|
||||
|
||||
@ -187,8 +187,6 @@ public class ConversationSearcher: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
// MARK: Searchers
|
||||
|
||||
private lazy var groupThreadSearcher: Searcher<TSGroupThread> = Searcher { (groupThread: TSGroupThread) in
|
||||
@ -202,11 +200,13 @@ public class ConversationSearcher: NSObject {
|
||||
|
||||
private lazy var contactThreadSearcher: Searcher<TSContactThread> = 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<SignalAccount> = Searcher { (signalAccount: SignalAccount) in
|
||||
let recipientId = signalAccount.recipientId
|
||||
Logger.verbose("signalAccountSearcher: \(self.indexingString(recipientId: recipientId))")
|
||||
return self.indexingString(recipientId: recipientId)
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user