Tweak message cells.
This commit is contained in:
parent
825e3f4ac0
commit
ac6f78a5fc
|
@ -224,6 +224,7 @@
|
|||
34D8C0271ED3673300188D7C /* DebugUIMessages.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8C0241ED3673300188D7C /* DebugUIMessages.m */; };
|
||||
34D8C0281ED3673300188D7C /* DebugUITableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8C0261ED3673300188D7C /* DebugUITableViewController.m */; };
|
||||
34D8C02B1ED3685800188D7C /* DebugUIContacts.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8C02A1ED3685800188D7C /* DebugUIContacts.m */; };
|
||||
34D920E220DD39EA00D51158 /* ConversationLayoutInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D920E120DD39E900D51158 /* ConversationLayoutInfo.swift */; };
|
||||
34D99C931F2937CC00D284D6 /* OWSAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D99C911F2937CC00D284D6 /* OWSAnalytics.swift */; };
|
||||
34DB0BED2011548B007B313F /* OWSDatabaseConverterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DB0BEC2011548B007B313F /* OWSDatabaseConverterTest.m */; };
|
||||
34DBF003206BD5A500025978 /* OWSMessageTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DBEFFF206BD5A400025978 /* OWSMessageTextView.m */; };
|
||||
|
@ -881,6 +882,7 @@
|
|||
34D8C0291ED3685800188D7C /* DebugUIContacts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIContacts.h; sourceTree = "<group>"; };
|
||||
34D8C02A1ED3685800188D7C /* DebugUIContacts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIContacts.m; sourceTree = "<group>"; };
|
||||
34D913491F62D4A500722898 /* SignalAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalAttachment.swift; sourceTree = "<group>"; };
|
||||
34D920E120DD39E900D51158 /* ConversationLayoutInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationLayoutInfo.swift; sourceTree = "<group>"; };
|
||||
34D99C8A1F27B13B00D284D6 /* OWSViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSViewController.h; sourceTree = "<group>"; };
|
||||
34D99C8B1F27B13B00D284D6 /* OWSViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSViewController.m; sourceTree = "<group>"; };
|
||||
34D99C911F2937CC00D284D6 /* OWSAnalytics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSAnalytics.swift; sourceTree = "<group>"; };
|
||||
|
@ -1496,10 +1498,12 @@
|
|||
34D1F0951F867BFC0066283D /* Cells */,
|
||||
34D1F0B21F86D31D0066283D /* ConversationCollectionView.h */,
|
||||
34D1F0B31F86D31D0066283D /* ConversationCollectionView.m */,
|
||||
45DDA6232090CEB500DE97F8 /* ConversationHeaderView.swift */,
|
||||
34D1F0671F8678AA0066283D /* ConversationInputTextView.h */,
|
||||
34D1F0681F8678AA0066283D /* ConversationInputTextView.m */,
|
||||
34D1F0691F8678AA0066283D /* ConversationInputToolbar.h */,
|
||||
34D1F06A1F8678AA0066283D /* ConversationInputToolbar.m */,
|
||||
34D920E120DD39E900D51158 /* ConversationLayoutInfo.swift */,
|
||||
343A65971FC4CFE7000477A1 /* ConversationScrollButton.h */,
|
||||
343A65961FC4CFE6000477A1 /* ConversationScrollButton.m */,
|
||||
34D1F06D1F8678AA0066283D /* ConversationViewController.h */,
|
||||
|
@ -1508,7 +1512,6 @@
|
|||
34D1F0701F8678AA0066283D /* ConversationViewItem.m */,
|
||||
34D1F0711F8678AA0066283D /* ConversationViewLayout.h */,
|
||||
34D1F0721F8678AA0066283D /* ConversationViewLayout.m */,
|
||||
45DDA6232090CEB500DE97F8 /* ConversationHeaderView.swift */,
|
||||
);
|
||||
path = ConversationView;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3310,6 +3313,7 @@
|
|||
34D1F0C01F8EC1760066283D /* MessageRecipientStatusUtils.swift in Sources */,
|
||||
45F659731E1BD99C00444429 /* CallKitCallUIAdaptee.swift in Sources */,
|
||||
34277A5E20751BDC006049F2 /* OWSQuotedMessageView.m in Sources */,
|
||||
34D920E220DD39EA00D51158 /* ConversationLayoutInfo.swift in Sources */,
|
||||
458DE9D61DEE3FD00071BB03 /* PeerConnectionClient.swift in Sources */,
|
||||
45DDA6242090CEB500DE97F8 /* ConversationHeaderView.swift in Sources */,
|
||||
45F32C242057297A00A300D5 /* MessageDetailViewController.swift in Sources */,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ConversationLayoutInfo;
|
||||
@class ConversationViewCell;
|
||||
@class ConversationViewItem;
|
||||
@class OWSContactOffersInteraction;
|
||||
|
@ -69,12 +70,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// * Users enters another view (e.g. conversation settings view, call screen, etc.).
|
||||
@property (nonatomic) BOOL isCellVisible;
|
||||
|
||||
// The width of the collection view.
|
||||
@property (nonatomic) int contentWidth;
|
||||
@property (nonatomic, nullable) ConversationLayoutInfo *layoutInfo;
|
||||
|
||||
- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
- (CGSize)cellSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth;
|
||||
- (CGSize)cellSize;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -16,17 +16,18 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.viewItem = nil;
|
||||
self.delegate = nil;
|
||||
self.isCellVisible = NO;
|
||||
self.contentWidth = 0;
|
||||
self.layoutInfo = nil;
|
||||
}
|
||||
|
||||
- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSFail(@"%@ This method should be overridden.", self.logTag);
|
||||
OWS_ABSTRACT_METHOD();
|
||||
}
|
||||
|
||||
- (CGSize)cellSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth
|
||||
- (CGSize)cellSize
|
||||
{
|
||||
OWSFail(@"%@ This method should be overridden.", self.logTag);
|
||||
OWS_ABSTRACT_METHOD();
|
||||
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#import "OWSContactOffersCell.h"
|
||||
#import "ConversationViewItem.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import <SignalMessaging/OWSContactOffersInteraction.h>
|
||||
#import <SignalMessaging/UIColor+OWS.h>
|
||||
#import <SignalMessaging/UIFont+OWS.h>
|
||||
|
@ -38,6 +39,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
{
|
||||
OWSAssert(!self.titleLabel);
|
||||
|
||||
self.preservesSuperviewLayoutMargins = NO;
|
||||
self.contentView.preservesSuperviewLayoutMargins = NO;
|
||||
self.layoutMargins = UIEdgeInsetsZero;
|
||||
self.contentView.layoutMargins = UIEdgeInsetsZero;
|
||||
|
||||
// [self setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
self.titleLabel = [UILabel new];
|
||||
|
@ -169,15 +175,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
layoutButton(self.blockButton, interaction.hasBlockOffer);
|
||||
}
|
||||
|
||||
- (CGSize)cellSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth
|
||||
- (CGSize)cellSize
|
||||
{
|
||||
OWSAssert(self.layoutInfo);
|
||||
OWSAssert(self.layoutInfo.viewWidth > 0);
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[OWSContactOffersInteraction class]]);
|
||||
|
||||
OWSContactOffersInteraction *interaction = (OWSContactOffersInteraction *)self.viewItem.interaction;
|
||||
|
||||
// TODO: Should we use viewWidth?
|
||||
CGSize result = CGSizeMake(viewWidth, 0);
|
||||
CGSize result = CGSizeMake(self.layoutInfo.viewWidth, 0);
|
||||
result.height += self.topVMargin;
|
||||
result.height += self.bottomVMargin;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ContactShareViewModel;
|
||||
@class ConversationLayoutInfo;
|
||||
@class ConversationViewItem;
|
||||
@class OWSContact;
|
||||
@class OWSQuotedReplyModel;
|
||||
|
@ -53,11 +54,13 @@ typedef NS_ENUM(NSUInteger, OWSMessageGestureLocation) {
|
|||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface OWSMessageBubbleView : UIView
|
||||
|
||||
@property (nonatomic, nullable) ConversationViewItem *viewItem;
|
||||
|
||||
@property (nonatomic) int contentWidth;
|
||||
@property (nonatomic) ConversationLayoutInfo *layoutInfo;
|
||||
|
||||
@property (nonatomic) NSCache *cellMediaCache;
|
||||
|
||||
|
@ -78,7 +81,7 @@ typedef NS_ENUM(NSUInteger, OWSMessageGestureLocation) {
|
|||
- (void)loadContent;
|
||||
- (void)unloadContent;
|
||||
|
||||
- (CGSize)sizeForContentWidth:(int)contentWidth;
|
||||
- (CGSize)measureSize;
|
||||
|
||||
- (void)prepareForReuse;
|
||||
|
||||
|
|
|
@ -237,14 +237,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (void)configureViews
|
||||
{
|
||||
OWSAssert(self.layoutInfo);
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert(self.viewItem.interaction);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
|
||||
OWSAssert(self.contentWidth > 0);
|
||||
|
||||
CGSize quotedMessageContentSize = [self quotedMessageSizeForContentWidth:self.contentWidth includeMargins:NO];
|
||||
CGSize bodyMediaContentSize = [self bodyMediaSizeForContentWidth:self.contentWidth];
|
||||
CGSize bodyTextContentSize = [self bodyTextSizeForContentWidth:self.contentWidth includeMargins:NO];
|
||||
CGSize quotedMessageContentSize = [self quotedMessageSize];
|
||||
CGSize bodyMediaContentSize = [self bodyMediaSize];
|
||||
CGSize bodyTextContentSize = [self bodyTextSize:NO];
|
||||
|
||||
self.bubbleView.isOutgoing = self.isOutgoing;
|
||||
self.bubbleView.hideTail = self.viewItem.shouldHideBubbleTail && !self.alwaysShowBubbleTail;
|
||||
|
@ -855,18 +855,18 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
#pragma mark - Measurement
|
||||
|
||||
// Size of "message body" text, not quoted reply text.
|
||||
- (CGSize)bodyTextSizeForContentWidth:(int)contentWidth includeMargins:(BOOL)includeMargins
|
||||
- (CGSize)bodyTextSize:(BOOL)includeMargins
|
||||
{
|
||||
OWSAssert(self.layoutInfo);
|
||||
OWSAssert(self.layoutInfo.maxMessageWidth > 0);
|
||||
|
||||
if (!self.hasBodyText) {
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
BOOL isRTL = self.isRTL;
|
||||
CGFloat leftMargin = isRTL ? self.textTrailingMargin : self.textLeadingMargin;
|
||||
CGFloat rightMargin = isRTL ? self.textLeadingMargin : self.textTrailingMargin;
|
||||
CGFloat hMargins = self.textTrailingMargin + self.textLeadingMargin;
|
||||
|
||||
const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth];
|
||||
const int maxTextWidth = (int)floor(maxMessageWidth - (leftMargin + rightMargin));
|
||||
const int maxTextWidth = (int)floor(self.layoutInfo.maxMessageWidth - hMargins);
|
||||
|
||||
OWSMessageTextView *bodyTextView = [self configureBodyTextView];
|
||||
CGSize textSize = CGSizeCeil([bodyTextView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]);
|
||||
|
@ -874,22 +874,27 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
CGSize result = textSize;
|
||||
|
||||
if (includeMargins) {
|
||||
result.width += leftMargin + rightMargin;
|
||||
result.width += hMargins;
|
||||
result.height += self.textTopMargin + self.textBottomMargin;
|
||||
}
|
||||
|
||||
return CGSizeCeil(result);
|
||||
}
|
||||
|
||||
- (CGSize)bodyMediaSizeForContentWidth:(int)contentWidth
|
||||
- (CGSize)bodyMediaSize
|
||||
{
|
||||
const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth];
|
||||
OWSAssert(self.layoutInfo);
|
||||
OWSAssert(self.layoutInfo.maxMessageWidth > 0);
|
||||
|
||||
CGFloat maxMessageWidth = self.layoutInfo.maxMessageWidth;
|
||||
|
||||
CGSize result = CGSizeZero;
|
||||
switch (self.cellType) {
|
||||
case OWSMessageCellType_Unknown:
|
||||
case OWSMessageCellType_TextMessage:
|
||||
case OWSMessageCellType_OversizeTextMessage: {
|
||||
return CGSizeZero;
|
||||
result = CGSizeZero;
|
||||
break;
|
||||
}
|
||||
case OWSMessageCellType_StillImage:
|
||||
case OWSMessageCellType_AnimatedImage:
|
||||
|
@ -926,28 +931,32 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
return CGSizeRound(CGSizeMake(mediaWidth, mediaHeight));
|
||||
break;
|
||||
}
|
||||
case OWSMessageCellType_Audio:
|
||||
return CGSizeMake(maxMessageWidth, OWSAudioMessageView.bubbleHeight);
|
||||
result = CGSizeMake(maxMessageWidth, OWSAudioMessageView.bubbleHeight);
|
||||
break;
|
||||
case OWSMessageCellType_GenericAttachment:
|
||||
return CGSizeMake(maxMessageWidth, [OWSGenericAttachmentView bubbleHeight]);
|
||||
result = CGSizeMake(maxMessageWidth, [OWSGenericAttachmentView bubbleHeight]);
|
||||
break;
|
||||
case OWSMessageCellType_DownloadingAttachment:
|
||||
return CGSizeMake(200, 90);
|
||||
result = CGSizeMake(200, 90);
|
||||
break;
|
||||
case OWSMessageCellType_ContactShare:
|
||||
OWSAssert(self.viewItem.contactShare);
|
||||
|
||||
return CGSizeMake(
|
||||
result = CGSizeMake(
|
||||
maxMessageWidth, [OWSContactShareView bubbleHeightForContactShare:self.viewItem.contactShare]);
|
||||
break;
|
||||
}
|
||||
|
||||
return CGSizeCeil(result);
|
||||
}
|
||||
|
||||
- (int)maxMessageWidthForContentWidth:(int)contentWidth
|
||||
{
|
||||
return (int)floor(contentWidth * 0.8f);
|
||||
}
|
||||
|
||||
- (CGSize)quotedMessageSizeForContentWidth:(int)contentWidth includeMargins:(BOOL)includeMargins
|
||||
- (CGSize)quotedMessageSize
|
||||
{
|
||||
OWSAssert(self.layoutInfo);
|
||||
OWSAssert(self.layoutInfo.maxMessageWidth > 0);
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
|
||||
|
||||
|
@ -963,31 +972,28 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[OWSQuotedMessageView quotedMessageViewForConversation:self.viewItem.quotedReply
|
||||
displayableQuotedText:displayableQuotedText
|
||||
isOutgoing:isOutgoing];
|
||||
const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth];
|
||||
CGSize result = [quotedMessageView sizeForMaxWidth:maxMessageWidth - kBubbleThornSideInset];
|
||||
if (includeMargins) {
|
||||
result.width += kBubbleThornSideInset;
|
||||
}
|
||||
|
||||
return result;
|
||||
CGSize result = [quotedMessageView sizeForMaxWidth:self.layoutInfo.maxMessageWidth];
|
||||
return CGSizeCeil(result);
|
||||
}
|
||||
|
||||
- (CGSize)sizeForContentWidth:(int)contentWidth
|
||||
- (CGSize)measureSize
|
||||
{
|
||||
OWSAssert(self.layoutInfo);
|
||||
OWSAssert(self.layoutInfo.viewWidth > 0);
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
|
||||
|
||||
CGSize cellSize = CGSizeZero;
|
||||
|
||||
CGSize quotedMessageSize = [self quotedMessageSizeForContentWidth:contentWidth includeMargins:YES];
|
||||
CGSize quotedMessageSize = [self quotedMessageSize];
|
||||
cellSize.width = MAX(cellSize.width, quotedMessageSize.width);
|
||||
cellSize.height += quotedMessageSize.height;
|
||||
|
||||
CGSize mediaContentSize = [self bodyMediaSizeForContentWidth:contentWidth];
|
||||
CGSize mediaContentSize = [self bodyMediaSize];
|
||||
cellSize.width = MAX(cellSize.width, mediaContentSize.width);
|
||||
cellSize.height += mediaContentSize.height;
|
||||
|
||||
CGSize textContentSize = [self bodyTextSizeForContentWidth:contentWidth includeMargins:YES];
|
||||
CGSize textContentSize = [self bodyTextSize:YES];
|
||||
cellSize.width = MAX(cellSize.width, textContentSize.width);
|
||||
cellSize.height += textContentSize.height;
|
||||
|
||||
|
|
|
@ -51,6 +51,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// Ensure only called once.
|
||||
OWSAssert(!self.messageBubbleView);
|
||||
|
||||
self.preservesSuperviewLayoutMargins = NO;
|
||||
self.contentView.preservesSuperviewLayoutMargins = NO;
|
||||
|
||||
_viewConstraints = [NSMutableArray new];
|
||||
|
||||
self.layoutMargins = UIEdgeInsetsZero;
|
||||
|
@ -97,6 +100,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self addGestureRecognizer:panGesture];
|
||||
}
|
||||
|
||||
- (void)setLayoutInfo:(nullable ConversationLayoutInfo *)layoutInfo
|
||||
{
|
||||
[super setLayoutInfo:layoutInfo];
|
||||
|
||||
self.messageBubbleView.layoutInfo = layoutInfo;
|
||||
}
|
||||
|
||||
+ (NSString *)cellReuseIdentifier
|
||||
{
|
||||
return NSStringFromClass([self class]);
|
||||
|
@ -152,14 +162,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSAssert(self.layoutInfo);
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert(self.viewItem.interaction);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
|
||||
OWSAssert(self.contentWidth > 0);
|
||||
OWSAssert(self.messageBubbleView);
|
||||
|
||||
self.messageBubbleView.viewItem = self.viewItem;
|
||||
self.messageBubbleView.contentWidth = self.contentWidth;
|
||||
self.messageBubbleView.cellMediaCache = self.delegate.cellMediaCache;
|
||||
[self.messageBubbleView configureViews];
|
||||
[self.messageBubbleView loadContent];
|
||||
|
@ -209,7 +218,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (void)updateDateHeader
|
||||
{
|
||||
OWSAssert(self.contentWidth > 0);
|
||||
OWSAssert(self.layoutInfo);
|
||||
|
||||
static NSDateFormatter *dateHeaderDateFormatter = nil;
|
||||
static NSDateFormatter *dateHeaderTimeFormatter = nil;
|
||||
|
@ -256,11 +265,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.dateHeaderLabel.hidden = NO;
|
||||
|
||||
[self.viewConstraints addObjectsFromArray:@[
|
||||
// Date headers should be visually centered within the conversation view,
|
||||
// so they need to extend outside the cell's boundaries.
|
||||
[self.dateHeaderLabel autoSetDimension:ALDimensionWidth toSize:self.contentWidth],
|
||||
(self.isIncoming ? [self.dateHeaderLabel autoPinEdgeToSuperviewEdge:ALEdgeLeading]
|
||||
: [self.dateHeaderLabel autoPinEdgeToSuperviewEdge:ALEdgeTrailing]),
|
||||
// TODO: Are data headers symmetric or are they asymmetric? gutters are asymmetric?
|
||||
[self.dateHeaderLabel autoPinLeadingToSuperviewMarginWithInset:self.layoutInfo.gutterLeading],
|
||||
[self.dateHeaderLabel autoPinTrailingToSuperviewMarginWithInset:self.layoutInfo.gutterTrailing],
|
||||
[self.dateHeaderLabel autoPinEdgeToSuperviewEdge:ALEdgeTop],
|
||||
[self.dateHeaderLabel autoSetDimension:ALDimensionHeight toSize:self.dateHeaderHeight],
|
||||
]];
|
||||
|
@ -362,7 +369,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// we want to leave spaces for an expiration timer and
|
||||
// include padding so that they still visually "cling" to the
|
||||
// appropriate incoming/outgoing edge.
|
||||
const CGFloat maxFooterLabelWidth = self.contentWidth - 100;
|
||||
const CGFloat maxFooterLabelWidth = self.layoutInfo.maxFooterWidth;
|
||||
if (hasExpirationTimer &&
|
||||
attributedText) {
|
||||
[self.viewConstraints addObjectsFromArray:@[
|
||||
|
@ -411,16 +418,18 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
#pragma mark - Measurement
|
||||
|
||||
- (CGSize)cellSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth
|
||||
|
||||
- (CGSize)cellSize
|
||||
{
|
||||
OWSAssert(self.layoutInfo);
|
||||
OWSAssert(self.layoutInfo.viewWidth > 0);
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
|
||||
OWSAssert(self.messageBubbleView);
|
||||
|
||||
self.messageBubbleView.viewItem = self.viewItem;
|
||||
self.messageBubbleView.contentWidth = self.contentWidth;
|
||||
self.messageBubbleView.cellMediaCache = self.delegate.cellMediaCache;
|
||||
CGSize messageBubbleSize = [self.messageBubbleView sizeForContentWidth:contentWidth];
|
||||
CGSize messageBubbleSize = [self.messageBubbleView measureSize];
|
||||
|
||||
CGSize cellSize = messageBubbleSize;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#import "OWSSystemMessageCell.h"
|
||||
#import "ConversationViewItem.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import "UIColor+OWS.h"
|
||||
#import "UIFont+OWS.h"
|
||||
#import "UIView+OWS.h"
|
||||
|
@ -22,6 +23,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@property (nonatomic) UIImageView *imageView;
|
||||
@property (nonatomic) UILabel *titleLabel;
|
||||
@property (nonatomic) UIStackView *stackView;
|
||||
@property (nonatomic) NSArray<NSLayoutConstraint *> *layoutConstraints;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -43,19 +46,31 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
{
|
||||
OWSAssert(!self.imageView);
|
||||
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
self.preservesSuperviewLayoutMargins = NO;
|
||||
self.contentView.preservesSuperviewLayoutMargins = NO;
|
||||
self.layoutMargins = UIEdgeInsetsZero;
|
||||
self.contentView.layoutMargins = UIEdgeInsetsZero;
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
self.imageView = [UIImageView new];
|
||||
[self.contentView addSubview:self.imageView];
|
||||
[self.imageView autoSetDimension:ALDimensionWidth toSize:self.iconSize];
|
||||
[self.imageView autoSetDimension:ALDimensionHeight toSize:self.iconSize];
|
||||
[self.imageView setContentHuggingHigh];
|
||||
|
||||
self.titleLabel = [UILabel new];
|
||||
self.titleLabel.textColor = [UIColor colorWithRGBHex:0x403e3b];
|
||||
self.titleLabel.font = [self titleFont];
|
||||
self.titleLabel.numberOfLines = 0;
|
||||
self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
[self.contentView addSubview:self.titleLabel];
|
||||
|
||||
self.stackView = [[UIStackView alloc] initWithArrangedSubviews:@[
|
||||
self.imageView,
|
||||
self.titleLabel,
|
||||
]];
|
||||
self.stackView.axis = UILayoutConstraintAxisHorizontal;
|
||||
self.stackView.spacing = self.hSpacing;
|
||||
self.stackView.alignment = UIStackViewAlignmentCenter;
|
||||
[self.contentView addSubview:self.stackView];
|
||||
|
||||
UITapGestureRecognizer *tap =
|
||||
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
|
||||
|
@ -66,6 +81,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self addGestureRecognizer:longPress];
|
||||
}
|
||||
|
||||
- (void)configureFonts
|
||||
{
|
||||
// Update cell to reflect changes in dynamic text.
|
||||
self.titleLabel.font = UIFont.ows_dynamicTypeFootnoteFont;
|
||||
}
|
||||
|
||||
+ (NSString *)cellReuseIdentifier
|
||||
{
|
||||
return NSStringFromClass([self class]);
|
||||
|
@ -73,6 +94,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSAssert(self.layoutInfo);
|
||||
OWSAssert(self.viewItem);
|
||||
|
||||
TSInteraction *interaction = self.viewItem.interaction;
|
||||
|
@ -81,9 +103,27 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.imageView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
self.imageView.tintColor = [self iconColorForInteraction:interaction];
|
||||
self.titleLabel.textColor = [self textColor];
|
||||
[self applyTitleForInteraction:interaction label:self.titleLabel transaction:transaction];
|
||||
[self applyTitleForInteraction:interaction label:self.titleLabel];
|
||||
|
||||
[self setNeedsLayout];
|
||||
CGSize titleSize = [self titleSize];
|
||||
|
||||
[NSLayoutConstraint deactivateConstraints:self.layoutConstraints];
|
||||
self.layoutConstraints = @[
|
||||
[self.titleLabel autoSetDimension:ALDimensionWidth toSize:titleSize.width],
|
||||
[self.stackView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.topVMargin],
|
||||
[self.stackView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.bottomVMargin],
|
||||
[self.stackView autoHCenterInSuperview],
|
||||
[self.stackView autoPinEdgeToSuperviewEdge:ALEdgeLeading
|
||||
withInset:self.layoutInfo.fullWidthGutterLeading + self.hMargin
|
||||
relation:NSLayoutRelationGreaterThanOrEqual],
|
||||
[self.stackView autoPinEdgeToSuperviewEdge:ALEdgeTrailing
|
||||
withInset:self.layoutInfo.fullWidthGutterTrailing + self.hMargin
|
||||
relation:NSLayoutRelationGreaterThanOrEqual],
|
||||
];
|
||||
|
||||
[self.contentView logFrameLaterWithLabel:@"contentView"];
|
||||
[self.stackView logFrameLaterWithLabel:@"stackView"];
|
||||
[self.titleLabel logFrameLaterWithLabel:@"titleLabel"];
|
||||
}
|
||||
|
||||
- (UIColor *)textColor
|
||||
|
@ -162,19 +202,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (void)applyTitleForInteraction:(TSInteraction *)interaction
|
||||
label:(UILabel *)label
|
||||
transaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSAssert(interaction);
|
||||
OWSAssert(label);
|
||||
|
||||
// Update cell to reflect changes in dynamic text.
|
||||
self.titleLabel.font = [self titleFont];
|
||||
[self configureFonts];
|
||||
|
||||
// TODO: Should we move the copy generation into this view?
|
||||
|
||||
if ([interaction isKindOfClass:[TSErrorMessage class]]) {
|
||||
TSErrorMessage *errorMessage = (TSErrorMessage *)interaction;
|
||||
label.text = [errorMessage previewTextWithTransaction:transaction];
|
||||
label.text = [errorMessage previewText];
|
||||
} else if ([interaction isKindOfClass:[TSInfoMessage class]]) {
|
||||
TSInfoMessage *infoMessage = (TSInfoMessage *)interaction;
|
||||
if ([infoMessage isKindOfClass:[OWSVerificationStateChangeMessage class]]) {
|
||||
|
@ -199,22 +237,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@"another device. Embeds {{user's name or phone number}}.")));
|
||||
label.text = [NSString stringWithFormat:titleFormat, displayName];
|
||||
} else {
|
||||
label.text = [infoMessage previewTextWithTransaction:transaction];
|
||||
label.text = [infoMessage previewText];
|
||||
}
|
||||
} else if ([interaction isKindOfClass:[TSCall class]]) {
|
||||
TSCall *call = (TSCall *)interaction;
|
||||
label.text = [call previewTextWithTransaction:transaction];
|
||||
label.text = [call previewText];
|
||||
} else {
|
||||
OWSFail(@"Unknown interaction type: %@", [interaction class]);
|
||||
label.text = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (UIFont *)titleFont
|
||||
{
|
||||
return UIFont.ows_dynamicTypeFootnoteFont;
|
||||
}
|
||||
|
||||
- (CGFloat)hMargin
|
||||
{
|
||||
return 25.f;
|
||||
|
@ -240,48 +273,30 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return 20.f;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
- (CGSize)titleSize
|
||||
{
|
||||
[super layoutSubviews];
|
||||
OWSAssert(self.layoutInfo);
|
||||
OWSAssert(self.viewItem);
|
||||
|
||||
CGFloat maxTitleWidth = (self.contentView.width - ([self hMargin] * 2.f + [self hSpacing] + [self iconSize]));
|
||||
CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)];
|
||||
|
||||
CGFloat contentWidth = ([self iconSize] + [self hSpacing] + titleSize.width);
|
||||
|
||||
CGFloat contentLeft = round((self.contentView.width - contentWidth) * 0.5f);
|
||||
CGFloat imageLeft = ([self isRTL] ? round(contentLeft + contentWidth - [self iconSize]) : contentLeft);
|
||||
CGFloat titleLeft = ([self isRTL] ? contentLeft : round(imageLeft + [self iconSize] + [self hSpacing]));
|
||||
|
||||
self.imageView.frame = CGRectMake(
|
||||
imageLeft, round((self.contentView.height - [self iconSize]) * 0.5f), [self iconSize], [self iconSize]);
|
||||
|
||||
self.titleLabel.frame = CGRectMake(titleLeft,
|
||||
round((self.contentView.height - titleSize.height) * 0.5f),
|
||||
ceil(titleSize.width + 1.f),
|
||||
ceil(titleSize.height + 1.f));
|
||||
CGFloat maxTitleWidth
|
||||
= (CGFloat)floor(self.layoutInfo.fullWidthContentWidth - (2 * self.hMargin + self.iconSize + self.hSpacing));
|
||||
return [self.titleLabel sizeThatFits:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)];
|
||||
}
|
||||
|
||||
- (CGSize)cellSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth
|
||||
- (CGSize)cellSize
|
||||
{
|
||||
OWSAssert(self.layoutInfo);
|
||||
OWSAssert(self.viewItem);
|
||||
|
||||
TSInteraction *interaction = self.viewItem.interaction;
|
||||
|
||||
CGSize result = CGSizeMake(contentWidth, 0);
|
||||
result.height += self.topVMargin;
|
||||
result.height += self.bottomVMargin;
|
||||
CGSize result = CGSizeMake(self.layoutInfo.viewWidth, 0);
|
||||
|
||||
// FIXME pass in transaction from the uiDBConnection.
|
||||
[[TSYapDatabaseObject dbReadConnection] readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
[self applyTitleForInteraction:interaction label:self.titleLabel transaction:transaction];
|
||||
}];
|
||||
|
||||
CGFloat maxTitleWidth = (contentWidth - ([self hMargin] * 2.f + [self hSpacing] + [self iconSize]));
|
||||
CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)];
|
||||
[self applyTitleForInteraction:interaction label:self.titleLabel];
|
||||
|
||||
CGSize titleSize = [self titleSize];
|
||||
CGFloat contentHeight = ceil(MAX([self iconSize], titleSize.height));
|
||||
result.height += contentHeight;
|
||||
result.height = (contentHeight + self.topVMargin + self.bottomVMargin);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#import "OWSUnreadIndicatorCell.h"
|
||||
#import "ConversationViewItem.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import <SignalMessaging/TSUnreadIndicatorInteraction.h>
|
||||
#import <SignalMessaging/UIColor+OWS.h>
|
||||
#import <SignalMessaging/UIFont+OWS.h>
|
||||
|
@ -15,12 +16,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@property (nonatomic, nullable) TSUnreadIndicatorInteraction *interaction;
|
||||
|
||||
@property (nonatomic) UIView *bannerView;
|
||||
@property (nonatomic) UIView *bannerTopHighlightView;
|
||||
@property (nonatomic) UIView *bannerBottomHighlightView1;
|
||||
@property (nonatomic) UIView *bannerBottomHighlightView2;
|
||||
// TODO:
|
||||
//@property (nonatomic) UIView *bannerView;
|
||||
//@property (nonatomic) UIView *bannerTopHighlightView;
|
||||
//@property (nonatomic) UIView *bannerBottomHighlightView1;
|
||||
//@property (nonatomic) UIView *bannerBottomHighlightView2;
|
||||
@property (nonatomic) UILabel *titleLabel;
|
||||
@property (nonatomic) UILabel *subtitleLabel;
|
||||
//@property (nonatomic) UILabel *subtitleLabel;
|
||||
@property (nonatomic) NSArray<NSLayoutConstraint *> *layoutConstraints;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -40,41 +43,64 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (void)commontInit
|
||||
{
|
||||
OWSAssert(!self.bannerView);
|
||||
OWSAssert(!self.titleLabel);
|
||||
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
self.preservesSuperviewLayoutMargins = NO;
|
||||
self.contentView.preservesSuperviewLayoutMargins = NO;
|
||||
self.layoutMargins = UIEdgeInsetsZero;
|
||||
self.contentView.layoutMargins = UIEdgeInsetsZero;
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
// self.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
self.bannerView = [UIView new];
|
||||
self.bannerView.backgroundColor = [UIColor colorWithRGBHex:0xf6eee3];
|
||||
[self.contentView addSubview:self.bannerView];
|
||||
|
||||
self.bannerTopHighlightView = [UIView new];
|
||||
self.bannerTopHighlightView.backgroundColor = [UIColor colorWithRGBHex:0xf9f3eb];
|
||||
[self.bannerView addSubview:self.bannerTopHighlightView];
|
||||
|
||||
self.bannerBottomHighlightView1 = [UIView new];
|
||||
self.bannerBottomHighlightView1.backgroundColor = [UIColor colorWithRGBHex:0xd1c6b8];
|
||||
[self.bannerView addSubview:self.bannerBottomHighlightView1];
|
||||
|
||||
self.bannerBottomHighlightView2 = [UIView new];
|
||||
self.bannerBottomHighlightView2.backgroundColor = [UIColor colorWithRGBHex:0xdbcfc0];
|
||||
[self.bannerView addSubview:self.bannerBottomHighlightView2];
|
||||
// self.bannerView = [UIView new];
|
||||
// self.bannerView.backgroundColor = [UIColor colorWithRGBHex:0xf6eee3];
|
||||
// [self.contentView addSubview:self.bannerView];
|
||||
//
|
||||
// self.bannerTopHighlightView = [UIView new];
|
||||
// self.bannerTopHighlightView.backgroundColor = [UIColor colorWithRGBHex:0xf9f3eb];
|
||||
// [self.bannerView addSubview:self.bannerTopHighlightView];
|
||||
//
|
||||
// self.bannerBottomHighlightView1 = [UIView new];
|
||||
// self.bannerBottomHighlightView1.backgroundColor = [UIColor colorWithRGBHex:0xd1c6b8];
|
||||
// [self.bannerView addSubview:self.bannerBottomHighlightView1];
|
||||
//
|
||||
// self.bannerBottomHighlightView2 = [UIView new];
|
||||
// self.bannerBottomHighlightView2.backgroundColor = [UIColor colorWithRGBHex:0xdbcfc0];
|
||||
// [self.bannerView addSubview:self.bannerBottomHighlightView2];
|
||||
|
||||
self.titleLabel = [UILabel new];
|
||||
self.titleLabel.textColor = [UIColor colorWithRGBHex:0x403e3b];
|
||||
self.titleLabel.font = [self titleFont];
|
||||
[self.bannerView addSubview:self.titleLabel];
|
||||
self.titleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
[self.contentView addSubview:self.titleLabel];
|
||||
|
||||
self.subtitleLabel = [UILabel new];
|
||||
self.subtitleLabel.textColor = [UIColor ows_infoMessageBorderColor];
|
||||
self.subtitleLabel.font = [self subtitleFont];
|
||||
// The subtitle may wrap to a second line.
|
||||
self.subtitleLabel.numberOfLines = 0;
|
||||
self.subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
self.subtitleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
[self.contentView addSubview:self.subtitleLabel];
|
||||
// self.subtitleLabel = [UILabel new];
|
||||
// self.subtitleLabel.textColor = [UIColor ows_infoMessageBorderColor];
|
||||
// self.subtitleLabel.font = [self subtitleFont];
|
||||
// // The subtitle may wrap to a second line.
|
||||
// self.subtitleLabel.numberOfLines = 0;
|
||||
// self.subtitleLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
// self.subtitleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
// [self.contentView addSubview:self.subtitleLabel];
|
||||
|
||||
[self configureFonts];
|
||||
}
|
||||
|
||||
- (void)configureFonts
|
||||
{
|
||||
// Update cell to reflect changes in dynamic text.
|
||||
self.titleLabel.font = UIFont.ows_dynamicTypeBodyFont;
|
||||
// self.subtitleLabel.font = [self subtitleFont];
|
||||
|
||||
|
||||
// - (UIFont *)titleFont
|
||||
// {
|
||||
// return UIFont.ows_dynamicTypeBodyFont;
|
||||
// }
|
||||
//
|
||||
// - (UIFont *)subtitleFont
|
||||
// {
|
||||
// return UIFont.ows_dynamicTypeCaption1Font;
|
||||
// }
|
||||
}
|
||||
|
||||
+ (NSString *)cellReuseIdentifier
|
||||
|
@ -84,31 +110,29 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSAssert(self.layoutInfo);
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSUnreadIndicatorInteraction class]]);
|
||||
|
||||
[self configureFonts];
|
||||
|
||||
TSUnreadIndicatorInteraction *interaction = (TSUnreadIndicatorInteraction *)self.viewItem.interaction;
|
||||
|
||||
self.titleLabel.text = [self titleForInteraction:interaction];
|
||||
self.subtitleLabel.text = [self subtitleForInteraction:interaction];
|
||||
|
||||
// Update cell to reflect changes in dynamic text.
|
||||
self.titleLabel.font = [self titleFont];
|
||||
self.subtitleLabel.font = [self subtitleFont];
|
||||
// self.subtitleLabel.text = [self subtitleForInteraction:interaction];
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
[NSLayoutConstraint deactivateConstraints:self.layoutConstraints];
|
||||
self.layoutConstraints = @[
|
||||
// TODO: Constants.
|
||||
[self.titleLabel autoVCenterInSuperview],
|
||||
[self.titleLabel autoPinLeadingToSuperviewMarginWithInset:self.layoutInfo.fullWidthGutterLeading],
|
||||
[self.titleLabel autoPinTrailingToSuperviewMarginWithInset:self.layoutInfo.fullWidthGutterTrailing],
|
||||
];
|
||||
|
||||
- (UIFont *)titleFont
|
||||
{
|
||||
return UIFont.ows_dynamicTypeBodyFont;
|
||||
}
|
||||
|
||||
- (UIFont *)subtitleFont
|
||||
{
|
||||
return UIFont.ows_dynamicTypeCaption1Font;
|
||||
// TODO:
|
||||
// [self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (NSString *)titleForInteraction:(TSUnreadIndicatorInteraction *)interaction
|
||||
|
@ -117,126 +141,130 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
.uppercaseString;
|
||||
}
|
||||
|
||||
- (NSString *)subtitleForInteraction:(TSUnreadIndicatorInteraction *)interaction
|
||||
{
|
||||
if (!interaction.hasMoreUnseenMessages) {
|
||||
return nil;
|
||||
}
|
||||
NSString *subtitleFormat = (interaction.missingUnseenSafetyNumberChangeCount > 0
|
||||
? NSLocalizedString(@"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_FORMAT",
|
||||
@"Messages that indicates that there are more unseen messages that be revealed by tapping the 'load "
|
||||
@"earlier messages' button. Embeds {{the name of the 'load earlier messages' button}}")
|
||||
: NSLocalizedString(
|
||||
@"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_AND_SAFETY_NUMBER_CHANGES_FORMAT",
|
||||
@"Messages that indicates that there are more unseen messages including safety number changes that "
|
||||
@"be revealed by tapping the 'load earlier messages' button. Embeds {{the name of the 'load earlier "
|
||||
@"messages' button}}."));
|
||||
NSString *loadMoreButtonName = NSLocalizedString(
|
||||
@"load_earlier_messages", @"Label for button that loads more messages in conversation view.");
|
||||
return [NSString stringWithFormat:subtitleFormat, loadMoreButtonName];
|
||||
}
|
||||
|
||||
- (CGFloat)subtitleHMargin
|
||||
{
|
||||
return 20.f;
|
||||
}
|
||||
|
||||
- (CGFloat)subtitleVSpacing
|
||||
{
|
||||
return 3.f;
|
||||
}
|
||||
|
||||
- (CGFloat)titleInnerHMargin
|
||||
{
|
||||
return 10.f;
|
||||
}
|
||||
|
||||
- (CGFloat)titleVMargin
|
||||
{
|
||||
return 5.5f;
|
||||
}
|
||||
|
||||
- (CGFloat)topVMargin
|
||||
{
|
||||
return 5.f;
|
||||
}
|
||||
|
||||
- (CGFloat)bottomVMargin
|
||||
{
|
||||
return 5.f;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
[self.titleLabel sizeToFit];
|
||||
|
||||
// It's a bit of a hack, but we use a view that extends _outside_ the cell's bounds
|
||||
// to draw its background, since we want the background to extend to the edges of the
|
||||
// collection view.
|
||||
//
|
||||
// This layout logic assumes that the cell insets are symmetrical and can be deduced
|
||||
// from the cell frame.
|
||||
CGRect bannerViewFrame = CGRectMake(-self.left,
|
||||
round(self.topVMargin),
|
||||
round(self.width + self.left * 2.f),
|
||||
round(self.titleLabel.height + self.titleVMargin * 2.f));
|
||||
self.bannerView.frame = [self convertRect:bannerViewFrame toView:self.contentView];
|
||||
|
||||
// The highlights should be 1px (not 1pt), so adapt their thickness to
|
||||
// the device resolution.
|
||||
CGFloat kHighlightThickness = 1.f / [UIScreen mainScreen].scale;
|
||||
self.bannerTopHighlightView.frame = CGRectMake(0, 0, self.bannerView.width, kHighlightThickness);
|
||||
self.bannerBottomHighlightView1.frame
|
||||
= CGRectMake(0, self.bannerView.height - kHighlightThickness * 2.f, self.bannerView.width, kHighlightThickness);
|
||||
self.bannerBottomHighlightView2.frame
|
||||
= CGRectMake(0, self.bannerView.height - kHighlightThickness * 1.f, self.bannerView.width, kHighlightThickness);
|
||||
|
||||
[self.titleLabel centerOnSuperview];
|
||||
|
||||
if (self.subtitleLabel.text.length > 0) {
|
||||
CGSize subtitleSize = [self.subtitleLabel
|
||||
sizeThatFits:CGSizeMake(self.contentView.width - [self subtitleHMargin] * 2.f, CGFLOAT_MAX)];
|
||||
self.subtitleLabel.frame = CGRectMake(round((self.contentView.width - subtitleSize.width) * 0.5f),
|
||||
round(self.bannerView.bottom + self.subtitleVSpacing),
|
||||
ceil(subtitleSize.width),
|
||||
ceil(subtitleSize.height));
|
||||
}
|
||||
}
|
||||
|
||||
- (CGSize)cellSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth
|
||||
//- (NSString *)subtitleForInteraction:(TSUnreadIndicatorInteraction *)interaction
|
||||
//{
|
||||
// if (!interaction.hasMoreUnseenMessages) {
|
||||
// return nil;
|
||||
// }
|
||||
// NSString *subtitleFormat = (interaction.missingUnseenSafetyNumberChangeCount > 0
|
||||
// ? NSLocalizedString(@"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_FORMAT",
|
||||
// @"Messages that indicates that there are more unseen messages that be revealed by tapping the 'load
|
||||
// "
|
||||
// @"earlier messages' button. Embeds {{the name of the 'load earlier messages' button}}")
|
||||
// : NSLocalizedString(
|
||||
// @"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_AND_SAFETY_NUMBER_CHANGES_FORMAT",
|
||||
// @"Messages that indicates that there are more unseen messages including safety number changes that "
|
||||
// @"be revealed by tapping the 'load earlier messages' button. Embeds {{the name of the 'load earlier
|
||||
// "
|
||||
// @"messages' button}}."));
|
||||
// NSString *loadMoreButtonName = NSLocalizedString(
|
||||
// @"load_earlier_messages", @"Label for button that loads more messages in conversation view.");
|
||||
// return [NSString stringWithFormat:subtitleFormat, loadMoreButtonName];
|
||||
//}
|
||||
|
||||
//- (CGFloat)subtitleHMargin
|
||||
//{
|
||||
// return 20.f;
|
||||
//}
|
||||
//
|
||||
//- (CGFloat)subtitleVSpacing
|
||||
//{
|
||||
// return 3.f;
|
||||
//}
|
||||
//
|
||||
//- (CGFloat)titleInnerHMargin
|
||||
//{
|
||||
// return 10.f;
|
||||
//}
|
||||
//
|
||||
//- (CGFloat)titleVMargin
|
||||
//{
|
||||
// return 5.5f;
|
||||
//}
|
||||
//
|
||||
//- (CGFloat)topVMargin
|
||||
//{
|
||||
// return 5.f;
|
||||
//}
|
||||
//
|
||||
//- (CGFloat)bottomVMargin
|
||||
//{
|
||||
// return 5.f;
|
||||
//}
|
||||
|
||||
//- (void)layoutSubviews
|
||||
//{
|
||||
// [super layoutSubviews];
|
||||
//
|
||||
// [self.titleLabel sizeToFit];
|
||||
//
|
||||
// // It's a bit of a hack, but we use a view that extends _outside_ the cell's bounds
|
||||
// // to draw its background, since we want the background to extend to the edges of the
|
||||
// // collection view.
|
||||
// //
|
||||
// // This layout logic assumes that the cell insets are symmetrical and can be deduced
|
||||
// // from the cell frame.
|
||||
// CGRect bannerViewFrame = CGRectMake(-self.left,
|
||||
// round(self.topVMargin),
|
||||
// round(self.width + self.left * 2.f),
|
||||
// round(self.titleLabel.height + self.titleVMargin * 2.f));
|
||||
// self.bannerView.frame = [self convertRect:bannerViewFrame toView:self.contentView];
|
||||
//
|
||||
// // The highlights should be 1px (not 1pt), so adapt their thickness to
|
||||
// // the device resolution.
|
||||
// CGFloat kHighlightThickness = 1.f / [UIScreen mainScreen].scale;
|
||||
// self.bannerTopHighlightView.frame = CGRectMake(0, 0, self.bannerView.width, kHighlightThickness);
|
||||
// self.bannerBottomHighlightView1.frame
|
||||
// = CGRectMake(0, self.bannerView.height - kHighlightThickness * 2.f, self.bannerView.width,
|
||||
// kHighlightThickness);
|
||||
// self.bannerBottomHighlightView2.frame
|
||||
// = CGRectMake(0, self.bannerView.height - kHighlightThickness * 1.f, self.bannerView.width,
|
||||
// kHighlightThickness);
|
||||
//
|
||||
// [self.titleLabel centerOnSuperview];
|
||||
//
|
||||
// if (self.subtitleLabel.text.length > 0) {
|
||||
// CGSize subtitleSize = [self.subtitleLabel
|
||||
// sizeThatFits:CGSizeMake(self.contentView.width - [self subtitleHMargin] * 2.f, CGFLOAT_MAX)];
|
||||
// self.subtitleLabel.frame = CGRectMake(round((self.contentView.width - subtitleSize.width) * 0.5f),
|
||||
// round(self.bannerView.bottom + self.subtitleVSpacing),
|
||||
// ceil(subtitleSize.width),
|
||||
// ceil(subtitleSize.height));
|
||||
// }
|
||||
//}
|
||||
|
||||
- (CGSize)cellSize
|
||||
{
|
||||
OWSAssert(self.layoutInfo);
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSUnreadIndicatorInteraction class]]);
|
||||
|
||||
TSUnreadIndicatorInteraction *interaction = (TSUnreadIndicatorInteraction *)self.viewItem.interaction;
|
||||
[self configureFonts];
|
||||
|
||||
// Update cell to reflect changes in dynamic text.
|
||||
self.titleLabel.font = [self titleFont];
|
||||
self.subtitleLabel.font = [self subtitleFont];
|
||||
// TSUnreadIndicatorInteraction *interaction = (TSUnreadIndicatorInteraction *)self.viewItem.interaction;
|
||||
|
||||
// TODO: Should we use viewWidth?
|
||||
CGSize result = CGSizeMake(viewWidth, 0);
|
||||
result.height += self.titleVMargin * 2.f;
|
||||
result.height += self.topVMargin;
|
||||
result.height += self.bottomVMargin;
|
||||
// TODO:
|
||||
CGSize result = CGSizeMake(self.layoutInfo.fullWidthContentWidth, self.titleLabel.font.lineHeight + 24.f * 2);
|
||||
// result.height += self.titleVMargin * 2.f;
|
||||
// result.height += self.topVMargin;
|
||||
// result.height += self.bottomVMargin;
|
||||
//
|
||||
// NSString *title = [self titleForInteraction:interaction];
|
||||
// NSString *subtitle = [self subtitleForInteraction:interaction];
|
||||
//
|
||||
// self.titleLabel.text = title;
|
||||
// result.height += ceil([self.titleLabel sizeThatFits:CGSizeZero].height);
|
||||
//
|
||||
// if (subtitle.length > 0) {
|
||||
// result.height += self.subtitleVSpacing;
|
||||
//
|
||||
// self.subtitleLabel.text = subtitle;
|
||||
// result.height += ceil(
|
||||
// [self.subtitleLabel sizeThatFits:CGSizeMake(self.layoutInfo.fullWidthContentWidth -
|
||||
// self.subtitleHMargin * 2.f, CGFLOAT_MAX)].height);
|
||||
// }
|
||||
|
||||
NSString *title = [self titleForInteraction:interaction];
|
||||
NSString *subtitle = [self subtitleForInteraction:interaction];
|
||||
|
||||
self.titleLabel.text = title;
|
||||
result.height += ceil([self.titleLabel sizeThatFits:CGSizeZero].height);
|
||||
|
||||
if (subtitle.length > 0) {
|
||||
result.height += self.subtitleVSpacing;
|
||||
|
||||
self.subtitleLabel.text = subtitle;
|
||||
result.height += ceil(
|
||||
[self.subtitleLabel sizeThatFits:CGSizeMake(viewWidth - self.subtitleHMargin * 2.f, CGFLOAT_MAX)].height);
|
||||
}
|
||||
|
||||
return result;
|
||||
return CGSizeCeil(result);
|
||||
}
|
||||
|
||||
- (void)prepareForReuse
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc
|
||||
public class ConversationLayoutInfo: NSObject {
|
||||
|
||||
private let thread: TSThread
|
||||
|
||||
private let isRTL: Bool
|
||||
|
||||
// The width of the collection view.
|
||||
@objc public var viewWidth: CGFloat = 0 {
|
||||
didSet {
|
||||
SwiftAssertIsOnMainThread(#function)
|
||||
|
||||
updateProperties()
|
||||
}
|
||||
}
|
||||
|
||||
@objc public let contentMarginTop: CGFloat = 0
|
||||
@objc public let contentMarginBottom: CGFloat = 0
|
||||
|
||||
@objc public var gutterLeading: CGFloat = 0
|
||||
@objc public var gutterTrailing: CGFloat = 0
|
||||
// These are the gutters used by "full width" views
|
||||
// like "date headers" and "unread indicator".
|
||||
@objc public var fullWidthGutterLeading: CGFloat = 0
|
||||
@objc public var fullWidthGutterTrailing: CGFloat = 0
|
||||
|
||||
// viewWidth - (gutterLeading + gutterTrailing)
|
||||
@objc public var contentWidth: CGFloat = 0
|
||||
|
||||
// viewWidth - (gutterfullWidthGutterLeadingLeading + fullWidthGutterTrailing)
|
||||
// TODO: Is this necessary?
|
||||
@objc public var fullWidthContentWidth: CGFloat = 0
|
||||
|
||||
@objc public var maxMessageWidth: CGFloat = 0
|
||||
@objc public var maxFooterWidth: CGFloat = 0
|
||||
|
||||
@objc
|
||||
public required init(thread: TSThread) {
|
||||
|
||||
self.thread = thread
|
||||
self.isRTL = CurrentAppContext().isRTL
|
||||
|
||||
super.init()
|
||||
|
||||
updateProperties()
|
||||
}
|
||||
|
||||
private func updateProperties() {
|
||||
if thread.isGroupThread() {
|
||||
gutterLeading = 16
|
||||
gutterTrailing = 20
|
||||
} else {
|
||||
gutterLeading = 40
|
||||
gutterTrailing = 20
|
||||
}
|
||||
// TODO: Should these be symmetric?
|
||||
fullWidthGutterLeading = gutterLeading
|
||||
fullWidthGutterTrailing = gutterTrailing
|
||||
|
||||
contentWidth = viewWidth - (gutterLeading + gutterTrailing)
|
||||
|
||||
fullWidthContentWidth = viewWidth - (fullWidthGutterLeading + fullWidthGutterTrailing)
|
||||
|
||||
maxMessageWidth = floor(contentWidth * 0.8)
|
||||
maxFooterWidth = floor(contentWidth - 100)
|
||||
}
|
||||
}
|
|
@ -174,6 +174,7 @@ typedef enum : NSUInteger {
|
|||
@property (nonatomic, readonly) ConversationInputToolbar *inputToolbar;
|
||||
@property (nonatomic, readonly) ConversationCollectionView *collectionView;
|
||||
@property (nonatomic, readonly) ConversationViewLayout *layout;
|
||||
@property (nonatomic, readonly) ConversationLayoutInfo *layoutInfo;
|
||||
|
||||
@property (nonatomic) NSArray<ConversationViewItem *> *viewItems;
|
||||
@property (nonatomic) NSMutableDictionary<NSString *, ConversationViewItem *> *viewItemCache;
|
||||
|
@ -217,7 +218,6 @@ typedef enum : NSUInteger {
|
|||
@property (nonatomic) UILabel *loadMoreHeader;
|
||||
@property (nonatomic) uint64_t lastVisibleTimestamp;
|
||||
|
||||
@property (nonatomic, readonly) BOOL isGroupConversation;
|
||||
@property (nonatomic) BOOL isUserScrolling;
|
||||
|
||||
@property (nonatomic) NSLayoutConstraint *scrollDownButtonButtomConstraint;
|
||||
|
@ -356,6 +356,13 @@ typedef enum : NSUInteger {
|
|||
object:nil];
|
||||
}
|
||||
|
||||
- (BOOL)isGroupConversation
|
||||
{
|
||||
OWSAssert(self.thread);
|
||||
|
||||
return self.thread.isGroupThread;
|
||||
}
|
||||
|
||||
- (void)signalAccountsDidChange:(NSNotification *)notification
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
@ -435,12 +442,12 @@ typedef enum : NSUInteger {
|
|||
OWSAssert(thread);
|
||||
|
||||
_thread = thread;
|
||||
_isGroupConversation = [self.thread isKindOfClass:[TSGroupThread class]];
|
||||
self.actionOnOpen = action;
|
||||
self.focusMessageIdOnOpen = focusMessageId;
|
||||
_cellMediaCache = [NSCache new];
|
||||
// Cache the cell media for ~24 cells.
|
||||
self.cellMediaCache.countLimit = 24;
|
||||
_layoutInfo = [[ConversationLayoutInfo alloc] initWithThread:thread];
|
||||
|
||||
// We need to update the "unread indicator" _before_ we determine the initial range
|
||||
// size, since it depends on where the unread indicator is placed.
|
||||
|
@ -526,7 +533,11 @@ typedef enum : NSUInteger {
|
|||
|
||||
- (void)createContents
|
||||
{
|
||||
_layout = [ConversationViewLayout new];
|
||||
OWSAssert(self.layoutInfo);
|
||||
|
||||
_layout = [[ConversationViewLayout alloc] initWithLayoutInfo:self.layoutInfo];
|
||||
self.layoutInfo.viewWidth = self.view.width;
|
||||
|
||||
self.layout.delegate = self;
|
||||
// We use the root view bounds as the initial frame for the collection
|
||||
// view so that its contents can be laid out immediately.
|
||||
|
@ -982,15 +993,16 @@ typedef enum : NSUInteger {
|
|||
@"numbers of multiple users.")
|
||||
: NSLocalizedString(@"VERIFY_PRIVACY",
|
||||
@"Label for button or row which allows users to verify the safety "
|
||||
@"number of another user."))style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
@"number of another user."))
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *action) {
|
||||
[weakSelf showNoLongerVerifiedUI];
|
||||
}];
|
||||
[actionSheetController addAction:verifyAction];
|
||||
|
||||
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:CommonStrings.dismissButton
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
[weakSelf resetVerificationStateToDefault];
|
||||
}];
|
||||
[actionSheetController addAction:dismissAction];
|
||||
|
@ -1656,7 +1668,7 @@ typedef enum : NSUInteger {
|
|||
|
||||
- (void)updateDisappearingMessagesConfiguration
|
||||
{
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
self.disappearingMessagesConfiguration =
|
||||
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:self.thread.uniqueId transaction:transaction];
|
||||
}];
|
||||
|
@ -1747,7 +1759,7 @@ typedef enum : NSUInteger {
|
|||
|
||||
UIAlertAction *deleteMessageAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_DELETE_TITLE", @"")
|
||||
style:UIAlertActionStyleDestructive
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
[message remove];
|
||||
}];
|
||||
[actionSheetController addAction:deleteMessageAction];
|
||||
|
@ -1755,17 +1767,17 @@ typedef enum : NSUInteger {
|
|||
UIAlertAction *retryAction = [UIAlertAction
|
||||
actionWithTitle:NSLocalizedString(@"MESSAGES_VIEW_FAILED_DOWNLOAD_RETRY_ACTION", @"Action sheet button text")
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
OWSAttachmentsProcessor *processor =
|
||||
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointer:attachmentPointer
|
||||
networkManager:self.networkManager];
|
||||
[processor fetchAttachmentsForMessage:message
|
||||
primaryStorage:self.primaryStorage
|
||||
success:^(TSAttachmentStream *_Nonnull attachmentStream) {
|
||||
success:^(TSAttachmentStream *attachmentStream) {
|
||||
DDLogInfo(
|
||||
@"%@ Successfully redownloaded attachment in thread: %@", self.logTag, message.thread);
|
||||
}
|
||||
failure:^(NSError *_Nonnull error) {
|
||||
failure:^(NSError *error) {
|
||||
DDLogWarn(@"%@ Failed to redownload message with error: %@", self.logTag, error);
|
||||
}];
|
||||
}];
|
||||
|
@ -1787,7 +1799,7 @@ typedef enum : NSUInteger {
|
|||
|
||||
UIAlertAction *deleteMessageAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_DELETE_TITLE", @"")
|
||||
style:UIAlertActionStyleDestructive
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
[message remove];
|
||||
}];
|
||||
[actionSheetController addAction:deleteMessageAction];
|
||||
|
@ -1795,12 +1807,12 @@ typedef enum : NSUInteger {
|
|||
UIAlertAction *resendMessageAction =
|
||||
[UIAlertAction actionWithTitle:NSLocalizedString(@"SEND_AGAIN_BUTTON", @"")
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self.messageSender enqueueMessage:message
|
||||
success:^{
|
||||
DDLogInfo(@"%@ Successfully resent failed message.", self.logTag);
|
||||
}
|
||||
failure:^(NSError *_Nonnull error) {
|
||||
failure:^(NSError *error) {
|
||||
DDLogWarn(@"%@ Failed to send message with error: %@", self.logTag, error);
|
||||
}];
|
||||
}];
|
||||
|
@ -1922,7 +1934,7 @@ typedef enum : NSUInteger {
|
|||
UIAlertAction *resetSessionAction = [UIAlertAction
|
||||
actionWithTitle:NSLocalizedString(@"FINGERPRINT_SHRED_KEYMATERIAL_BUTTON", @"")
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
if (![self.thread isKindOfClass:[TSContactThread class]]) {
|
||||
// Corrupt Message errors only appear in contact threads.
|
||||
DDLogError(@"%@ Unexpected request to reset session in group thread. Refusing", self.logTag);
|
||||
|
@ -1955,7 +1967,7 @@ typedef enum : NSUInteger {
|
|||
UIAlertAction *showSafteyNumberAction =
|
||||
[UIAlertAction actionWithTitle:NSLocalizedString(@"SHOW_SAFETY_NUMBER_ACTION", @"Action sheet item")
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
DDLogInfo(@"%@ Remote Key Changed actions: Show fingerprint display", self.logTag);
|
||||
[self showFingerprintWithRecipientId:errorMessage.theirSignalId];
|
||||
}];
|
||||
|
@ -1964,7 +1976,7 @@ typedef enum : NSUInteger {
|
|||
UIAlertAction *acceptSafetyNumberAction =
|
||||
[UIAlertAction actionWithTitle:NSLocalizedString(@"ACCEPT_NEW_IDENTITY_ACTION", @"Action sheet item")
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
DDLogInfo(@"%@ Remote Key Changed actions: Accepted new identity key", self.logTag);
|
||||
|
||||
// DEPRECATED: we're no longer creating these incoming SN error's per message,
|
||||
|
@ -2000,7 +2012,7 @@ typedef enum : NSUInteger {
|
|||
__weak ConversationViewController *weakSelf = self;
|
||||
UIAlertAction *callAction = [UIAlertAction actionWithTitle:[CallStrings callBackAlertCallButton]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
[weakSelf startAudioCall];
|
||||
}];
|
||||
[alertController addAction:callAction];
|
||||
|
@ -2044,7 +2056,7 @@ typedef enum : NSUInteger {
|
|||
actionWithTitle:NSLocalizedString(
|
||||
@"BLOCK_OFFER_ACTIONSHEET_BLOCK_ACTION", @"Action sheet that will block an unknown user.")
|
||||
style:UIAlertActionStyleDestructive
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
DDLogInfo(@"%@ Blocking an unknown user.", self.logTag);
|
||||
[self.blockingManager addBlockedPhoneNumber:interaction.recipientId];
|
||||
// Delete the offers.
|
||||
|
@ -2283,20 +2295,20 @@ typedef enum : NSUInteger {
|
|||
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointer:attachmentPointer
|
||||
networkManager:self.networkManager];
|
||||
|
||||
[self.editingDatabaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
|
||||
[self.editingDatabaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[processor fetchAttachmentsForMessage:nil
|
||||
transaction:transaction
|
||||
success:^(TSAttachmentStream *_Nonnull attachmentStream) {
|
||||
success:^(TSAttachmentStream *attachmentStream) {
|
||||
[self.editingDatabaseConnection
|
||||
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull postSuccessTransaction) {
|
||||
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *postSuccessTransaction) {
|
||||
[message setQuotedMessageThumbnailAttachmentStream:attachmentStream];
|
||||
[message saveWithTransaction:postSuccessTransaction];
|
||||
}];
|
||||
}
|
||||
failure:^(NSError *_Nonnull error) {
|
||||
failure:^(NSError *error) {
|
||||
DDLogWarn(@"%@ Failed to redownload thumbnail with error: %@", self.logTag, error);
|
||||
[self.editingDatabaseConnection
|
||||
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull postSuccessTransaction) {
|
||||
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *postSuccessTransaction) {
|
||||
[message touchWithTransaction:transaction];
|
||||
}];
|
||||
}];
|
||||
|
@ -2443,6 +2455,7 @@ typedef enum : NSUInteger {
|
|||
MessageDetailViewController *view =
|
||||
[[MessageDetailViewController alloc] initWithViewItem:conversationItem
|
||||
message:message
|
||||
thread:self.thread
|
||||
mode:MessageMetadataViewModeFocusOnMetadata];
|
||||
[self.navigationController pushViewController:view animated:YES];
|
||||
}
|
||||
|
@ -2452,7 +2465,7 @@ typedef enum : NSUInteger {
|
|||
DDLogDebug(@"%@ user did tap reply", self.logTag);
|
||||
|
||||
__block OWSQuotedReplyModel *quotedReply;
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
quotedReply = [OWSQuotedReplyModel quotedReplyForConversationViewItem:conversationItem transaction:transaction];
|
||||
}];
|
||||
|
||||
|
@ -3739,7 +3752,7 @@ typedef enum : NSUInteger {
|
|||
UIAlertAction *takeMediaAction = [UIAlertAction
|
||||
actionWithTitle:NSLocalizedString(@"MEDIA_FROM_CAMERA_BUTTON", @"media picker option to take photo or video")
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self takePictureOrVideo];
|
||||
}];
|
||||
UIImage *takeMediaImage = [UIImage imageNamed:@"actionsheet_camera_black"];
|
||||
|
@ -3750,7 +3763,7 @@ typedef enum : NSUInteger {
|
|||
UIAlertAction *chooseMediaAction = [UIAlertAction
|
||||
actionWithTitle:NSLocalizedString(@"MEDIA_FROM_LIBRARY_BUTTON", @"media picker option to choose from library")
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self chooseFromLibraryAsMedia];
|
||||
}];
|
||||
UIImage *chooseMediaImage = [UIImage imageNamed:@"actionsheet_camera_roll_black"];
|
||||
|
@ -3761,7 +3774,7 @@ typedef enum : NSUInteger {
|
|||
UIAlertAction *gifAction = [UIAlertAction
|
||||
actionWithTitle:NSLocalizedString(@"SELECT_GIF_BUTTON", @"Label for 'select GIF to attach' action sheet button")
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self showGifPicker];
|
||||
}];
|
||||
UIImage *gifImage = [UIImage imageNamed:@"actionsheet_gif_black"];
|
||||
|
@ -3773,7 +3786,7 @@ typedef enum : NSUInteger {
|
|||
[UIAlertAction actionWithTitle:NSLocalizedString(@"MEDIA_FROM_DOCUMENT_PICKER_BUTTON",
|
||||
@"action sheet button title when choosing attachment type")
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self showAttachmentDocumentPickerMenu];
|
||||
}];
|
||||
UIImage *chooseDocumentImage = [UIImage imageNamed:@"actionsheet_document_black"];
|
||||
|
@ -3786,7 +3799,7 @@ typedef enum : NSUInteger {
|
|||
[UIAlertAction actionWithTitle:NSLocalizedString(@"ATTACHMENT_MENU_CONTACT_BUTTON",
|
||||
@"attachment menu option to send contact")
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self chooseContactForSending];
|
||||
}];
|
||||
UIImage *chooseContactImage = [UIImage imageNamed:@"actionsheet_contact"];
|
||||
|
@ -3948,7 +3961,7 @@ typedef enum : NSUInteger {
|
|||
successCompletion();
|
||||
}
|
||||
}
|
||||
failure:^(NSError *_Nonnull error) {
|
||||
failure:^(NSError *error) {
|
||||
DDLogError(@"%@ Failed to send group avatar update with error: %@", self.logTag, error);
|
||||
}];
|
||||
} else {
|
||||
|
@ -3959,7 +3972,7 @@ typedef enum : NSUInteger {
|
|||
successCompletion();
|
||||
}
|
||||
}
|
||||
failure:^(NSError *_Nonnull error) {
|
||||
failure:^(NSError *error) {
|
||||
DDLogError(@"%@ Failed to send group update with error: %@", self.logTag, error);
|
||||
}];
|
||||
}
|
||||
|
@ -4754,6 +4767,7 @@ typedef enum : NSUInteger {
|
|||
OWSAssertIsOnMainThread();
|
||||
|
||||
[self updateLastVisibleTimestamp];
|
||||
self.layoutInfo.viewWidth = self.collectionView.width;
|
||||
}
|
||||
|
||||
#pragma mark - View Items
|
||||
|
@ -4799,7 +4813,8 @@ typedef enum : NSUInteger {
|
|||
} else {
|
||||
viewItem = [[ConversationViewItem alloc] initWithInteraction:interaction
|
||||
isGroupThread:isGroupThread
|
||||
transaction:transaction];
|
||||
transaction:transaction
|
||||
layoutInfo:self.layoutInfo];
|
||||
}
|
||||
viewItem.row = (NSInteger)row;
|
||||
[viewItems addObject:viewItem];
|
||||
|
@ -4966,9 +4981,9 @@ typedef enum : NSUInteger {
|
|||
OWSMessageCell *messageCell = (OWSMessageCell *)cell;
|
||||
messageCell.messageBubbleView.delegate = self;
|
||||
}
|
||||
cell.contentWidth = self.layout.contentWidth;
|
||||
cell.layoutInfo = self.layoutInfo;
|
||||
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
[cell loadForDisplayWithTransaction:transaction];
|
||||
}];
|
||||
|
||||
|
|
|
@ -66,10 +66,13 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
|
|||
// previous update.
|
||||
@property (nonatomic) NSInteger previousRow;
|
||||
|
||||
@property (nonatomic, readonly) ConversationLayoutInfo *layoutInfo;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithInteraction:(TSInteraction *)interaction
|
||||
isGroupThread:(BOOL)isGroupThread
|
||||
transaction:(YapDatabaseReadTransaction *)transaction;
|
||||
transaction:(YapDatabaseReadTransaction *)transaction
|
||||
layoutInfo:(ConversationLayoutInfo *)layoutInfo;
|
||||
|
||||
- (ConversationViewCell *)dequeueCellForCollectionView:(UICollectionView *)collectionView
|
||||
indexPath:(NSIndexPath *)indexPath;
|
||||
|
|
|
@ -77,7 +77,12 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
- (instancetype)initWithInteraction:(TSInteraction *)interaction
|
||||
isGroupThread:(BOOL)isGroupThread
|
||||
transaction:(YapDatabaseReadTransaction *)transaction
|
||||
layoutInfo:(ConversationLayoutInfo *)layoutInfo
|
||||
{
|
||||
OWSAssert(interaction);
|
||||
OWSAssert(transaction);
|
||||
OWSAssert(layoutInfo);
|
||||
|
||||
self = [super init];
|
||||
|
||||
if (!self) {
|
||||
|
@ -86,6 +91,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
|
||||
_interaction = interaction;
|
||||
_isGroupThread = isGroupThread;
|
||||
_layoutInfo = layoutInfo;
|
||||
self.row = NSNotFound;
|
||||
self.previousRow = NSNotFound;
|
||||
|
||||
|
@ -172,14 +178,16 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
self.cachedCellSize = nil;
|
||||
}
|
||||
|
||||
- (CGSize)cellSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth
|
||||
- (CGSize)cellSize
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
OWSAssert(self.layoutInfo);
|
||||
|
||||
if (!self.cachedCellSize) {
|
||||
ConversationViewCell *_Nullable measurementCell = [self measurementCell];
|
||||
measurementCell.viewItem = self;
|
||||
CGSize cellSize = [measurementCell cellSizeForViewWidth:viewWidth contentWidth:contentWidth];
|
||||
measurementCell.layoutInfo = self.layoutInfo;
|
||||
CGSize cellSize = [measurementCell cellSize];
|
||||
self.cachedCellSize = [NSValue valueWithCGSize:cellSize];
|
||||
[measurementCell prepareForReuse];
|
||||
}
|
||||
|
@ -252,6 +260,14 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
return measurementCell;
|
||||
}
|
||||
|
||||
- (CGFloat)vSpacingWithLastLayoutItem:(id<ConversationViewLayoutItem>)lastLayoutItem
|
||||
{
|
||||
OWSAssert(lastLayoutItem);
|
||||
|
||||
// TODO:
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
- (ConversationViewCell *)dequeueCellForCollectionView:(UICollectionView *)collectionView
|
||||
indexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// TODO: Remove this enum.
|
||||
typedef NS_ENUM(NSInteger, ConversationViewLayoutAlignment) {
|
||||
// We use incoming/outgoing, not left/right to support RTL.
|
||||
ConversationViewLayoutAlignment_Incoming,
|
||||
|
@ -12,12 +13,16 @@ typedef NS_ENUM(NSInteger, ConversationViewLayoutAlignment) {
|
|||
ConversationViewLayoutAlignment_Center,
|
||||
};
|
||||
|
||||
@class ConversationLayoutInfo;
|
||||
|
||||
@protocol ConversationViewLayoutItem <NSObject>
|
||||
|
||||
- (CGSize)cellSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth;
|
||||
- (CGSize)cellSize;
|
||||
|
||||
- (ConversationViewLayoutAlignment)layoutAlignment;
|
||||
|
||||
- (CGFloat)vSpacingWithLastLayoutItem:(id<ConversationViewLayoutItem>)lastLayoutItem;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
@ -39,7 +44,11 @@ typedef NS_ENUM(NSInteger, ConversationViewLayoutAlignment) {
|
|||
@property (nonatomic, weak) id<ConversationViewLayoutDelegate> delegate;
|
||||
@property (nonatomic, readonly) BOOL hasLayout;
|
||||
@property (nonatomic, readonly) BOOL hasEverHadLayout;
|
||||
@property (nonatomic, readonly) int contentWidth;
|
||||
@property (nonatomic, readonly) ConversationLayoutInfo *layoutInfo;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithLayoutInfo:(ConversationLayoutInfo *)layoutInfo;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//
|
||||
|
||||
#import "ConversationViewLayout.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import "UIView+OWS.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
@ -21,18 +22,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (nonatomic) BOOL hasLayout;
|
||||
@property (nonatomic) BOOL hasEverHadLayout;
|
||||
|
||||
@property (nonatomic) int contentWidth;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation ConversationViewLayout
|
||||
|
||||
- (instancetype)init
|
||||
- (instancetype)initWithLayoutInfo:(ConversationLayoutInfo *)layoutInfo
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_itemAttributesMap = [NSMutableDictionary new];
|
||||
_layoutInfo = layoutInfo;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -94,48 +94,29 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// TODO: Remove this log statement after we've reduced the invalidation churn.
|
||||
DDLogVerbose(@"%@ prepareLayout", self.logTag);
|
||||
|
||||
const int vInset = 8;
|
||||
const int hInset = 10;
|
||||
const int vSpacing = 5;
|
||||
const int viewWidth = (int)floor(self.collectionView.bounds.size.width);
|
||||
const int contentWidth = (int)floor(viewWidth - 2 * hInset);
|
||||
self.contentWidth = contentWidth;
|
||||
const CGFloat viewWidth = self.layoutInfo.viewWidth;
|
||||
|
||||
NSArray<id<ConversationViewLayoutItem>> *layoutItems = self.delegate.layoutItems;
|
||||
|
||||
CGFloat y = vInset + self.delegate.layoutHeaderHeight;
|
||||
CGFloat y = self.layoutInfo.contentMarginTop + self.delegate.layoutHeaderHeight;
|
||||
CGFloat contentBottom = y;
|
||||
BOOL isRTL = self.collectionView.isRTL;
|
||||
|
||||
NSInteger row = 0;
|
||||
id<ConversationViewLayoutItem> _Nullable lastLayoutItem = nil;
|
||||
for (id<ConversationViewLayoutItem> layoutItem in layoutItems) {
|
||||
CGSize layoutSize = [layoutItem cellSizeForViewWidth:viewWidth contentWidth:contentWidth];
|
||||
|
||||
layoutSize.width = MIN(viewWidth, floor(layoutSize.width));
|
||||
layoutSize.height = floor(layoutSize.height);
|
||||
CGRect itemFrame;
|
||||
switch (layoutItem.layoutAlignment) {
|
||||
case ConversationViewLayoutAlignment_Incoming:
|
||||
case ConversationViewLayoutAlignment_Outgoing: {
|
||||
BOOL isIncoming = layoutItem.layoutAlignment == ConversationViewLayoutAlignment_Incoming;
|
||||
BOOL isLeft = isIncoming ^ isRTL;
|
||||
if (isLeft) {
|
||||
itemFrame = CGRectMake(hInset, y, layoutSize.width, layoutSize.height);
|
||||
} else {
|
||||
itemFrame
|
||||
= CGRectMake(viewWidth - (hInset + layoutSize.width), y, layoutSize.width, layoutSize.height);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ConversationViewLayoutAlignment_FullWidth:
|
||||
itemFrame = CGRectMake(0, y, viewWidth, layoutSize.height);
|
||||
break;
|
||||
case ConversationViewLayoutAlignment_Center:
|
||||
itemFrame = CGRectMake(
|
||||
hInset + round((viewWidth - layoutSize.width) * 0.5f), y, layoutSize.width, layoutSize.height);
|
||||
break;
|
||||
if (lastLayoutItem) {
|
||||
y += [layoutItem vSpacingWithLastLayoutItem:lastLayoutItem];
|
||||
}
|
||||
|
||||
CGSize layoutSize = CGSizeCeil([layoutItem cellSize]);
|
||||
|
||||
// Ensure cell fits within view.
|
||||
OWSAssert(layoutSize.width <= viewWidth);
|
||||
layoutSize.width = MIN(viewWidth, layoutSize.width);
|
||||
|
||||
// All cell are "full width" and are responsible for aligning their own content.
|
||||
CGRect itemFrame = CGRectMake(0, y, viewWidth, layoutSize.height);
|
||||
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
|
||||
UICollectionViewLayoutAttributes *itemAttributes =
|
||||
[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
|
||||
|
@ -143,11 +124,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.itemAttributesMap[@(row)] = itemAttributes;
|
||||
|
||||
contentBottom = itemFrame.origin.y + itemFrame.size.height;
|
||||
y = contentBottom + vSpacing;
|
||||
y = contentBottom;
|
||||
row++;
|
||||
lastLayoutItem = layoutItem;
|
||||
}
|
||||
|
||||
contentBottom += vInset;
|
||||
contentBottom += self.layoutInfo.contentMarginBottom;
|
||||
self.contentSize = CGSizeMake(viewWidth, contentBottom);
|
||||
}
|
||||
|
||||
|
|
|
@ -1963,6 +1963,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[prepareBlocks addObject:replyAssetLoader.prepareBlock];
|
||||
}
|
||||
|
||||
// We don't need to configure ConversationLayoutInfo's view width in this case.
|
||||
ConversationLayoutInfo *layoutInfo = [[ConversationLayoutInfo alloc] initWithThread:thread];
|
||||
|
||||
return [DebugUIMessagesSingleAction
|
||||
actionWithLabel:label
|
||||
unstaggeredActionBlock:^(NSUInteger index, YapDatabaseReadWriteTransaction *transaction) {
|
||||
|
@ -1980,7 +1983,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSAssert(messageToQuote);
|
||||
DDLogVerbose(@"%@ %@", self.logTag, label);
|
||||
[DDLog flushLog];
|
||||
ConversationViewItem *viewItem = [[ConversationViewItem alloc] initWithInteraction:messageToQuote isGroupThread:thread.isGroupThread transaction:transaction];
|
||||
ConversationViewItem *viewItem = [[ConversationViewItem alloc] initWithInteraction:messageToQuote
|
||||
isGroupThread:thread.isGroupThread
|
||||
transaction:transaction
|
||||
layoutInfo:layoutInfo];
|
||||
quotedMessage = [[OWSQuotedReplyModel quotedReplyForConversationViewItem:viewItem transaction:transaction] buildQuotedMessage];
|
||||
} else {
|
||||
TSOutgoingMessage *_Nullable messageToQuote = [self createFakeOutgoingMessage:thread
|
||||
|
@ -1994,7 +2000,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
transaction:transaction];
|
||||
OWSAssert(messageToQuote);
|
||||
|
||||
ConversationViewItem *viewItem = [[ConversationViewItem alloc] initWithInteraction:messageToQuote isGroupThread:thread.isGroupThread transaction:transaction];
|
||||
ConversationViewItem *viewItem = [[ConversationViewItem alloc] initWithInteraction:messageToQuote
|
||||
isGroupThread:thread.isGroupThread
|
||||
transaction:transaction
|
||||
layoutInfo:layoutInfo];
|
||||
quotedMessage = [[OWSQuotedReplyModel quotedReplyForConversationViewItem:viewItem transaction:transaction] buildQuotedMessage];
|
||||
}
|
||||
OWSAssert(quotedMessage);
|
||||
|
|
|
@ -479,7 +479,11 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
|
|||
self.uiDatabaseConnection.read { transaction in
|
||||
let message = galleryItem.message
|
||||
let thread = message.thread(with: transaction)
|
||||
fetchedItem = ConversationViewItem(interaction: message, isGroupThread: thread.isGroupThread(), transaction: transaction)
|
||||
let conversationLayoutInfo = ConversationLayoutInfo(thread: thread)
|
||||
fetchedItem = ConversationViewItem(interaction: message,
|
||||
isGroupThread: thread.isGroupThread(),
|
||||
transaction: transaction,
|
||||
layoutInfo: conversationLayoutInfo)
|
||||
}
|
||||
|
||||
guard let viewItem = fetchedItem else {
|
||||
|
|
|
@ -40,6 +40,8 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele
|
|||
var attachmentStream: TSAttachmentStream?
|
||||
var messageBody: String?
|
||||
|
||||
var conversationLayoutInfo: ConversationLayoutInfo
|
||||
|
||||
private var contactShareViewHelper: ContactShareViewHelper
|
||||
|
||||
// MARK: Initializers
|
||||
|
@ -50,13 +52,15 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele
|
|||
}
|
||||
|
||||
@objc
|
||||
required init(viewItem: ConversationViewItem, message: TSMessage, mode: MessageMetadataViewMode) {
|
||||
required init(viewItem: ConversationViewItem, message: TSMessage, thread: TSThread, mode: MessageMetadataViewMode) {
|
||||
self.contactsManager = Environment.current().contactsManager
|
||||
self.viewItem = viewItem
|
||||
self.message = message
|
||||
self.mode = mode
|
||||
self.uiDatabaseConnection = OWSPrimaryStorage.shared().newDatabaseConnection()
|
||||
self.contactShareViewHelper = ContactShareViewHelper(contactsManager: contactsManager)
|
||||
self.conversationLayoutInfo = ConversationLayoutInfo(thread: thread)
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
contactShareViewHelper.delegate = self
|
||||
|
@ -83,6 +87,22 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele
|
|||
object: OWSPrimaryStorage.shared().dbNotificationObject)
|
||||
}
|
||||
|
||||
override public func viewWillLayoutSubviews() {
|
||||
Logger.debug("\(self.logTag) in \(#function)")
|
||||
|
||||
super.viewWillLayoutSubviews()
|
||||
|
||||
self.conversationLayoutInfo.viewWidth = self.view.width()
|
||||
}
|
||||
|
||||
override public func viewDidLayoutSubviews() {
|
||||
Logger.debug("\(self.logTag) in \(#function)")
|
||||
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
self.conversationLayoutInfo.viewWidth = self.view.width()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
|
@ -331,7 +351,7 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele
|
|||
self.messageBubbleView = messageBubbleView
|
||||
messageBubbleView.viewItem = viewItem
|
||||
messageBubbleView.cellMediaCache = NSCache()
|
||||
messageBubbleView.contentWidth = contentWidth()
|
||||
messageBubbleView.layoutInfo = self.conversationLayoutInfo
|
||||
messageBubbleView.alwaysShowBubbleTail = true
|
||||
messageBubbleView.configureViews()
|
||||
messageBubbleView.loadContent()
|
||||
|
@ -574,10 +594,6 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele
|
|||
|
||||
// MARK: - Message Bubble Layout
|
||||
|
||||
private func contentWidth() -> Int32 {
|
||||
return Int32(round(self.view.width() - (2 * bubbleViewHMargin)))
|
||||
}
|
||||
|
||||
private func updateMessageBubbleViewLayout() {
|
||||
guard let messageBubbleView = messageBubbleView else {
|
||||
return
|
||||
|
@ -589,9 +605,7 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele
|
|||
return
|
||||
}
|
||||
|
||||
messageBubbleView.contentWidth = contentWidth()
|
||||
|
||||
let messageBubbleSize = messageBubbleView.size(forContentWidth: contentWidth())
|
||||
let messageBubbleSize = messageBubbleView.measureSize()
|
||||
messageBubbleViewWidthLayoutConstraint.constant = messageBubbleSize.width
|
||||
messageBubbleViewHeightLayoutConstraint.constant = messageBubbleSize.height
|
||||
}
|
||||
|
|
|
@ -77,7 +77,10 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value);
|
|||
// contents.
|
||||
//
|
||||
// NOTE: the margin values are inverted in RTL layouts.
|
||||
//
|
||||
// TODO: Remove this in favor of AppContext.isRTL()
|
||||
- (BOOL)isRTL;
|
||||
|
||||
- (NSArray<NSLayoutConstraint *> *)autoPinLeadingAndTrailingToSuperviewMargin;
|
||||
- (NSLayoutConstraint *)autoPinLeadingToSuperviewMargin;
|
||||
- (NSLayoutConstraint *)autoPinLeadingToSuperviewMarginWithInset:(CGFloat)margin;
|
||||
|
@ -150,6 +153,11 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
|
|||
return CGSizeMake((CGFloat)ceil(size.width), (CGFloat)ceil(size.height));
|
||||
}
|
||||
|
||||
CG_INLINE CGSize CGSizeFloor(CGSize size)
|
||||
{
|
||||
return CGSizeMake((CGFloat)floor(size.width), (CGFloat)floor(size.height));
|
||||
}
|
||||
|
||||
CG_INLINE CGSize CGSizeRound(CGSize size)
|
||||
{
|
||||
return CGSizeMake((CGFloat)round(size.width), (CGFloat)round(size.height));
|
||||
|
|
|
@ -20,6 +20,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
createdByRemoteName:(nullable NSString *)remoteName
|
||||
createdInExistingGroup:(BOOL)createdInExistingGroup;
|
||||
|
||||
- (NSString *)previewText;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -50,6 +50,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
-(NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
return [self previewText];
|
||||
}
|
||||
|
||||
- (NSString *)previewText
|
||||
{
|
||||
if (self.createdInExistingGroup) {
|
||||
OWSAssert(self.configurationIsEnabled && self.configurationDurationSeconds > 0);
|
||||
|
|
|
@ -27,6 +27,9 @@ typedef NS_ENUM(int32_t, TSErrorMessageType) {
|
|||
|
||||
@interface TSErrorMessage : TSMessage <OWSReadTracking>
|
||||
|
||||
@property (nonatomic, readonly) TSErrorMessageType errorType;
|
||||
@property (nullable, nonatomic, readonly) NSString *recipientId;
|
||||
|
||||
- (instancetype)initMessageWithTimestamp:(uint64_t)timestamp
|
||||
inThread:(nullable TSThread *)thread
|
||||
messageBody:(nullable NSString *)body
|
||||
|
@ -70,8 +73,7 @@ typedef NS_ENUM(int32_t, TSErrorMessageType) {
|
|||
|
||||
+ (instancetype)nonblockingIdentityChangeInThread:(TSThread *)thread recipientId:(NSString *)recipientId;
|
||||
|
||||
@property (nonatomic, readonly) TSErrorMessageType errorType;
|
||||
@property (nullable, nonatomic, readonly) NSString *recipientId;
|
||||
- (NSString *)previewText;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -104,6 +104,11 @@ NSUInteger TSErrorMessageSchemaVersion = 1;
|
|||
}
|
||||
|
||||
- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
return [self previewText];
|
||||
}
|
||||
|
||||
- (NSString *)previewText
|
||||
{
|
||||
switch (_errorType) {
|
||||
case TSErrorMessageNoSession:
|
||||
|
|
|
@ -61,6 +61,8 @@ typedef NS_ENUM(NSInteger, TSInfoMessageType) {
|
|||
expiresInSeconds:(uint32_t)expiresInSeconds
|
||||
expireStartedAt:(uint64_t)expireStartedAt NS_UNAVAILABLE;
|
||||
|
||||
- (NSString *)previewText;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -112,6 +112,11 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
|
|||
}
|
||||
|
||||
- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
return [self previewText];
|
||||
}
|
||||
|
||||
- (NSString *)previewText
|
||||
{
|
||||
switch (_messageType) {
|
||||
case TSInfoMessageTypeSessionDidEnd:
|
||||
|
|
|
@ -35,6 +35,8 @@ typedef enum {
|
|||
|
||||
- (void)updateCallType:(RPRecentCallType)callType;
|
||||
|
||||
- (NSString *)previewText;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -68,6 +68,11 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
|
|||
}
|
||||
|
||||
- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
return [self previewText];
|
||||
}
|
||||
|
||||
- (NSString *)previewText
|
||||
{
|
||||
// We don't actually use the `transaction` but other sibling classes do.
|
||||
switch (_callType) {
|
||||
|
|
|
@ -629,16 +629,6 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
|
|||
[socketMessage didFailWithStatusCode:(NSInteger)responseStatus responseData:responseData error:error];
|
||||
}
|
||||
}
|
||||
|
||||
DDLogVerbose(@"%@ received WebSocket response: %llu, %zd, %@, %zd, %@, %d, %@.",
|
||||
self.logTag,
|
||||
(unsigned long long)requestId,
|
||||
(NSInteger)responseStatus,
|
||||
responseMessage,
|
||||
responseData.length,
|
||||
responseHeaders,
|
||||
socketMessage != nil,
|
||||
responseObject);
|
||||
}
|
||||
|
||||
- (void)failAllPendingSocketMessagesIfNecessary
|
||||
|
|
|
@ -73,7 +73,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
#endif
|
||||
|
||||
#define OWS_ABSTRACT_METHOD() OWSFail(@"Method needs to be implemented by subclasses.")
|
||||
#define OWS_ABSTRACT_METHOD() OWSFail(@"%@ Method needs to be implemented by subclasses.", self.logTag)
|
||||
|
||||
#pragma mark - Singleton Asserts
|
||||
|
||||
|
|
Loading…
Reference in New Issue