Merge branch 'charlesmchen/quotedReplies3'

pull/1/head
Matthew Chen 7 years ago
commit 7fe83cbc42

@ -42,6 +42,7 @@
340FC8CD20518C77007AEB0F /* OWSBackupJob.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC8CC20518C76007AEB0F /* OWSBackupJob.m */; };
340FC8D0205BF2FA007AEB0F /* OWSBackupIO.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC8CE205BF2FA007AEB0F /* OWSBackupIO.m */; };
341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */ = {isa = PBXBuildFile; fileRef = 341F2C0E1F2B8AE700D07D6B /* DebugUIMisc.m */; };
34277A5E20751BDC006049F2 /* OWSQuotedMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34277A5C20751BDC006049F2 /* OWSQuotedMessageView.m */; };
3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3430FE171F7751D4000EC51B /* GiphyAPI.swift */; };
34330A5A1E7875FB00DF2FB9 /* fontawesome-webfont.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A591E7875FB00DF2FB9 /* fontawesome-webfont.ttf */; };
34330A5C1E787A9800DF2FB9 /* dripicons-v2.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A5B1E787A9800DF2FB9 /* dripicons-v2.ttf */; };
@ -602,6 +603,8 @@
341458471FBE11C4005ABCF9 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = translations/fa.lproj/Localizable.strings; sourceTree = "<group>"; };
341F2C0D1F2B8AE700D07D6B /* DebugUIMisc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIMisc.h; sourceTree = "<group>"; };
341F2C0E1F2B8AE700D07D6B /* DebugUIMisc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIMisc.m; sourceTree = "<group>"; };
34277A5C20751BDC006049F2 /* OWSQuotedMessageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSQuotedMessageView.m; sourceTree = "<group>"; };
34277A5D20751BDC006049F2 /* OWSQuotedMessageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSQuotedMessageView.h; sourceTree = "<group>"; };
3430FE171F7751D4000EC51B /* GiphyAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GiphyAPI.swift; sourceTree = "<group>"; };
34330A591E7875FB00DF2FB9 /* fontawesome-webfont.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "fontawesome-webfont.ttf"; sourceTree = "<group>"; };
34330A5B1E787A9800DF2FB9 /* dripicons-v2.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "dripicons-v2.ttf"; sourceTree = "<group>"; };
@ -1665,6 +1668,8 @@
34D1F0A21F867BFC0066283D /* OWSMessageCell.m */,
34DBF000206BD5A400025978 /* OWSMessageTextView.h */,
34DBEFFF206BD5A400025978 /* OWSMessageTextView.m */,
34277A5D20751BDC006049F2 /* OWSQuotedMessageView.h */,
34277A5C20751BDC006049F2 /* OWSQuotedMessageView.m */,
34D1F0A51F867BFC0066283D /* OWSSystemMessageCell.h */,
34D1F0A61F867BFC0066283D /* OWSSystemMessageCell.m */,
34D1F0A71F867BFC0066283D /* OWSUnreadIndicatorCell.h */,
@ -3251,6 +3256,7 @@
34D1F0C01F8EC1760066283D /* MessageRecipientStatusUtils.swift in Sources */,
45F659731E1BD99C00444429 /* CallKitCallUIAdaptee.swift in Sources */,
45BB93381E688E14001E3939 /* UIDevice+featureSupport.swift in Sources */,
34277A5E20751BDC006049F2 /* OWSQuotedMessageView.m in Sources */,
458DE9D61DEE3FD00071BB03 /* PeerConnectionClient.swift in Sources */,
45F32C242057297A00A300D5 /* MessageDetailViewController.swift in Sources */,
34D1F0841F8678AA0066283D /* ConversationInputToolbar.m in Sources */,

@ -2,19 +2,17 @@
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSBubbleView.h"
NS_ASSUME_NONNULL_BEGIN
@class OWSBubbleView;
@interface OWSBubbleStrokeView : UIView
@property (nonatomic, weak) OWSBubbleView *bubbleView;
@interface OWSBubbleStrokeView : UIView <OWSBubbleViewPartner>
@property (nonatomic) UIColor *strokeColor;
@property (nonatomic) CGFloat strokeThickness;
- (void)updateLayers;
@end
NS_ASSUME_NONNULL_END

@ -12,6 +12,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) CAShapeLayer *shapeLayer;
@property (nonatomic, weak) OWSBubbleView *bubbleView;
@end
#pragma mark -
@ -31,8 +33,6 @@ NS_ASSUME_NONNULL_BEGIN
self.shapeLayer = [CAShapeLayer new];
[self.layer addSublayer:self.shapeLayer];
[self updateLayers];
return self;
}
@ -56,7 +56,7 @@ NS_ASSUME_NONNULL_BEGIN
[super setFrame:frame];
if (didChange || !self.shapeLayer) {
if (didChange) {
[self updateLayers];
}
}
@ -67,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN
[super setBounds:bounds];
if (didChange || !self.shapeLayer) {
if (didChange) {
[self updateLayers];
}
}

@ -2,6 +2,8 @@
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSBubbleView.h"
NS_ASSUME_NONNULL_BEGIN
extern const CGFloat kOWSMessageCellCornerRadius;
@ -13,11 +15,19 @@ extern const CGFloat kBubbleThornVInset;
extern const CGFloat kBubbleTextHInset;
extern const CGFloat kBubbleTextVInset;
@class OWSBubbleStrokeView;
@class OWSBubbleView;
@interface OWSBubbleView : UIView
@protocol OWSBubbleViewPartner <NSObject>
- (void)updateLayers;
- (void)setBubbleView:(OWSBubbleView *)bubbleView;
@end
@property (nonatomic, weak, nullable) OWSBubbleStrokeView *bubbleStrokeView;
#pragma mark -
@interface OWSBubbleView : UIView
@property (nonatomic) BOOL isOutgoing;
@property (nonatomic) BOOL hideTail;
@ -27,6 +37,14 @@ extern const CGFloat kBubbleTextVInset;
- (UIBezierPath *)maskPath;
#pragma mark - Coordination
- (void)addPartnerView:(id<OWSBubbleViewPartner>)view;
- (void)clearPartnerViews;
- (void)updatePartnerViews;
@end
NS_ASSUME_NONNULL_END

@ -3,7 +3,6 @@
//
#import "OWSBubbleView.h"
#import "OWSBubbleStrokeView.h"
#import <SignalMessaging/UIView+OWS.h>
NS_ASSUME_NONNULL_BEGIN
@ -22,6 +21,8 @@ const CGFloat kBubbleTextVInset = 10.f;
@property (nonatomic) CAShapeLayer *maskLayer;
@property (nonatomic) CAShapeLayer *shapeLayer;
@property (nonatomic, readonly) NSMutableArray<id<OWSBubbleViewPartner>> *partnerViews;
@end
#pragma mark -
@ -41,7 +42,7 @@ const CGFloat kBubbleTextVInset = 10.f;
self.maskLayer = [CAShapeLayer new];
self.layer.mask = self.maskLayer;
[self updateLayers];
_partnerViews = [NSMutableArray new];
return self;
}
@ -52,7 +53,7 @@ const CGFloat kBubbleTextVInset = 10.f;
_isOutgoing = isOutgoing;
if (didChange || !self.shapeLayer) {
if (didChange) {
[self updateLayers];
}
}
@ -63,7 +64,7 @@ const CGFloat kBubbleTextVInset = 10.f;
_hideTail = hideTail;
if (didChange || !self.shapeLayer) {
if (didChange) {
[self updateLayers];
}
}
@ -74,7 +75,7 @@ const CGFloat kBubbleTextVInset = 10.f;
_isTruncated = isTruncated;
if (didChange || !self.shapeLayer) {
if (didChange) {
[self updateLayers];
}
}
@ -87,13 +88,13 @@ const CGFloat kBubbleTextVInset = 10.f;
[super setFrame:frame];
if (didChangeSize || !self.shapeLayer) {
if (didChangeSize) {
[self updateLayers];
}
// We always need to inform the "bubble stroke view" (if any) if our
// frame/bounds/center changes. Its contents are not in local coordinates.
[self.bubbleStrokeView updateLayers];
[self updatePartnerViews];
}
- (void)setBounds:(CGRect)bounds
@ -104,13 +105,13 @@ const CGFloat kBubbleTextVInset = 10.f;
[super setBounds:bounds];
if (didChangeSize || !self.shapeLayer) {
if (didChangeSize) {
[self updateLayers];
}
// We always need to inform the "bubble stroke view" (if any) if our
// frame/bounds/center changes. Its contents are not in local coordinates.
[self.bubbleStrokeView updateLayers];
[self updatePartnerViews];
}
- (void)setCenter:(CGPoint)center
@ -119,7 +120,7 @@ const CGFloat kBubbleTextVInset = 10.f;
// We always need to inform the "bubble stroke view" (if any) if our
// frame/bounds/center changes. Its contents are not in local coordinates.
[self.bubbleStrokeView updateLayers];
[self updatePartnerViews];
}
- (void)setBubbleColor:(nullable UIColor *)bubbleColor
@ -211,6 +212,33 @@ const CGFloat kBubbleTextVInset = 10.f;
return bezierPath;
}
#pragma mark - Coordination
- (void)addPartnerView:(id<OWSBubbleViewPartner>)partnerView
{
OWSAssert(self.partnerViews);
[partnerView setBubbleView:self];
[self.partnerViews addObject:partnerView];
}
- (void)clearPartnerViews
{
OWSAssert(self.partnerViews);
[self.partnerViews removeAllObjects];
}
- (void)updatePartnerViews
{
[self layoutIfNeeded];
for (id<OWSBubbleViewPartner> partnerView in self.partnerViews) {
[partnerView updateLayers];
}
}
@end
NS_ASSUME_NONNULL_END

@ -13,6 +13,7 @@
#import "OWSExpirationTimerView.h"
#import "OWSGenericAttachmentView.h"
#import "OWSMessageTextView.h"
#import "OWSQuotedMessageView.h"
#import "Signal-Swift.h"
#import "UIColor+OWS.h"
#import <JSQMessagesViewController/JSQMessagesTimestampFormatter.h>
@ -21,11 +22,6 @@
NS_ASSUME_NONNULL_BEGIN
CG_INLINE CGSize CGSizeCeil(CGSize size)
{
return CGSizeMake((CGFloat)ceil(size.width), (CGFloat)ceil(size.height));
}
@interface OWSMessageCell ()
// The nullable properties are created as needed.
@ -266,23 +262,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
return self.viewItem.hasQuotedAttachment;
}
- (BOOL)hasQuotedAttachmentThumbnail
{
// This should always be valid for the appropriate cell types.
OWSAssert(self.viewItem);
return (self.viewItem.hasQuotedAttachment &&
[TSAttachmentStream hasThumbnailForMimeType:self.viewItem.quotedAttachmentMimetype]);
}
- (nullable DisplayableText *)displayableQuotedText
{
// This should always be valid for the appropriate cell types.
OWSAssert(self.viewItem.displayableQuotedText);
return self.viewItem.displayableQuotedText;
}
- (TSMessage *)message
{
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
@ -382,34 +361,30 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
if (self.isQuotedReply) {
OWSAssert(!lastSubview);
UIView *quotedMessageView = [self createQuotedMessageView];
TSMessage *message = (TSMessage *)self.viewItem.interaction;
OWSQuotedMessageView *quotedMessageView = [OWSQuotedMessageView
quotedMessageViewForConversation:message.quotedMessage
displayableQuotedText:(self.viewItem.hasQuotedText ? self.viewItem.displayableQuotedText : nil)];
[quotedMessageView createContents];
[self.bubbleView addSubview:quotedMessageView];
CGFloat leadingMargin = self.quotedBubbleLeadingMargin;
CGFloat trailingMargin = self.quotedBubbleTrailingMargin;
CGFloat bubbleLeadingMargin = (self.isIncoming ? kBubbleThornSideInset : 0.f);
CGFloat bubbleTrailingMargin = (self.isIncoming ? 0.f : kBubbleThornSideInset);
[self.viewConstraints addObjectsFromArray:@[
[quotedMessageView autoPinLeadingToSuperviewMarginWithInset:leadingMargin],
[quotedMessageView autoPinTrailingToSuperviewMarginWithInset:trailingMargin],
[quotedMessageView autoPinLeadingToSuperviewMarginWithInset:bubbleLeadingMargin],
[quotedMessageView autoPinTrailingToSuperviewMarginWithInset:bubbleTrailingMargin],
]];
if (lastSubview) {
[self.viewConstraints addObject:[quotedMessageView autoPinEdge:ALEdgeTop
toEdge:ALEdgeBottom
ofView:lastSubview
withOffset:self.quotedMessageTopInset]];
[self.viewConstraints
addObject:[quotedMessageView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:lastSubview]];
} else {
[self.viewConstraints addObject:[quotedMessageView autoPinEdgeToSuperviewEdge:ALEdgeTop
withInset:self.quotedMessageTopInset]];
[self.viewConstraints addObject:[quotedMessageView autoPinEdgeToSuperviewEdge:ALEdgeTop]];
}
lastSubview = quotedMessageView;
bottomMargin = 0;
[self.bubbleView logFrameLaterWithLabel:@"bubbleView"];
[quotedMessageView logFrameLaterWithLabel:@"quotedMessageView"];
// TODO: Consider stroking the quoted thumbnail.
[self.bubbleView addPartnerView:quotedMessageView.boundsStrokeView];
}
UIView *_Nullable bodyMediaView = nil;
@ -493,15 +468,14 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
OWSBubbleStrokeView *bubbleStrokeView = [OWSBubbleStrokeView new];
bubbleStrokeView.strokeThickness = 1.f;
bubbleStrokeView.strokeColor = [UIColor colorWithWhite:0.f alpha:0.1f];
bubbleStrokeView.bubbleView = self.bubbleView;
[self.bubbleView addSubview:bubbleStrokeView];
[bubbleStrokeView autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:bodyMediaView];
[bubbleStrokeView autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:bodyMediaView];
[bubbleStrokeView autoPinEdge:ALEdgeLeft toEdge:ALEdgeLeft ofView:bodyMediaView];
[bubbleStrokeView autoPinEdge:ALEdgeRight toEdge:ALEdgeRight ofView:bodyMediaView];
self.bubbleView.bubbleStrokeView = bubbleStrokeView;
OWSAssert(self.bubbleView.bubbleStrokeView);
[self.bubbleView addPartnerView:bubbleStrokeView];
}
}
@ -786,19 +760,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
return [UIFont systemFontOfSize:12.0f];
}
- (UILabel *)createQuotedTextLabel
{
UILabel *quotedTextLabel = [UILabel new];
quotedTextLabel.numberOfLines = 3;
quotedTextLabel.lineBreakMode = NSLineBreakByWordWrapping;
quotedTextLabel.text = self.displayableQuotedText.displayText;
quotedTextLabel.textColor = self.quotedTextColor;
// Honor dynamic type in the message bodies.
quotedTextLabel.font = self.textMessageFont;
return quotedTextLabel;
}
- (OWSMessageTextView *)configureBodyTextView
{
OWSAssert(self.hasBodyText);
@ -864,78 +825,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
return tapForMoreLabel;
}
- (UIView *)createQuotedMessageView
{
OWSAssert(self.isQuotedReply);
UIView *quotedMessageView = [UIView containerView];
quotedMessageView.userInteractionEnabled = NO;
quotedMessageView.clipsToBounds = YES;
// TODO:
quotedMessageView.layer.cornerRadius = 3.f;
quotedMessageView.backgroundColor = [UIColor colorWithRGBHex:0xe2f7fa];
UIView *quoteStripView = [UIView containerView];
quoteStripView.backgroundColor = (self.isIncoming ? [UIColor whiteColor] : [UIColor colorWithRGBHex:0x007884]);
quoteStripView.userInteractionEnabled = NO;
[quotedMessageView addSubview:quoteStripView];
[quoteStripView autoPinHeightToSuperview];
[quoteStripView autoPinLeadingToSuperviewMargin];
[quoteStripView autoSetDimension:ALDimensionWidth toSize:self.quotedReplyStripeThickness];
UIView *_Nullable quotedThumbnailView = nil;
if (self.hasQuotedAttachmentThumbnail) {
// TODO:
quotedThumbnailView = [UIView containerView];
quotedThumbnailView.userInteractionEnabled = NO;
quotedThumbnailView.backgroundColor = [UIColor redColor];
[quotedMessageView addSubview:quotedThumbnailView];
[quotedThumbnailView autoPinTopToSuperviewMargin];
[quotedThumbnailView autoPinTrailingToSuperviewMargin];
[quotedThumbnailView autoSetDimension:ALDimensionWidth toSize:self.quotedThumbnailSize];
[quotedThumbnailView autoSetDimension:ALDimensionHeight toSize:self.quotedThumbnailSize];
}
OWSContactsManager *contactsManager = Environment.current.contactsManager;
NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.viewItem.quotedRecipientId];
UILabel *quotedAuthorLabel = [UILabel new];
quotedAuthorLabel.text = quotedAuthor;
quotedAuthorLabel.font = self.quotedAuthorFont;
quotedAuthorLabel.textColor
= (self.isIncoming ? [UIColor colorWithRGBHex:0xd84315] : [UIColor colorWithRGBHex:0x007884]);
quotedAuthorLabel.numberOfLines = 1;
quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[quotedMessageView addSubview:quotedAuthorLabel];
[quotedAuthorLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.quotedContentTopInset];
[quotedAuthorLabel autoPinLeadingToTrailingEdgeOfView:quoteStripView offset:self.quotedReplyStripeHSpacing];
if (quotedThumbnailView) {
[quotedAuthorLabel autoPinTrailingToEdgeOfView:quotedThumbnailView offset:self.quotedThumbnailHSpacing];
} else {
[quotedAuthorLabel autoPinTrailingToSuperviewMarginWithInset:self.quotedContentTrailingMargin];
}
if (self.hasQuotedText) {
UILabel *quotedTextLabel = [self createQuotedTextLabel];
[quotedMessageView addSubview:quotedTextLabel];
[quotedTextLabel autoPinEdge:ALEdgeTop
toEdge:ALEdgeBottom
ofView:quotedAuthorLabel
withOffset:self.quotedAuthorBottomSpacing];
[quotedTextLabel autoPinLeadingToTrailingEdgeOfView:quoteStripView offset:self.quotedReplyStripeHSpacing];
if (quotedThumbnailView) {
[quotedTextLabel autoPinLeadingToTrailingEdgeOfView:quotedThumbnailView
offset:self.quotedThumbnailHSpacing];
} else {
[quotedTextLabel autoPinTrailingToSuperviewMarginWithInset:self.quotedContentTrailingMargin];
}
[quotedTextLabel autoPinBottomToSuperviewMarginWithInset:self.quotedContentBottomInset];
}
return quotedMessageView;
}
- (UIView *)loadViewForStillImage
{
OWSAssert(self.attachmentStream);
@ -1284,73 +1173,17 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
OWSAssert(self.viewItem);
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
CGSize result = CGSizeZero;
if (!self.isQuotedReply) {
return result;
}
result.width += self.quotedMessageHInset;
result.width += self.quotedReplyStripeThickness;
result.width += self.quotedReplyStripeHSpacing;
result.height += self.quotedMessageTopInset;
CGFloat thumbnailHeight = 0.f;
if (self.hasQuotedAttachmentThumbnail) {
result.width += self.quotedThumbnailHSpacing;
result.width += self.quotedThumbnailSize;
thumbnailHeight = self.quotedThumbnailSize;
} else {
result.width += self.quotedContentTrailingMargin;
return CGSizeZero;
}
result.width += self.quotedMessageHInset;
// Once we've determined everything _except_ the size of the text
// content (i.e. the quoted author and the quoted text (if any)),
// we can determine the size of the text content.
TSMessage *message = (TSMessage *)self.viewItem.interaction;
OWSQuotedMessageView *quotedMessageView = [OWSQuotedMessageView
quotedMessageViewForConversation:message.quotedMessage
displayableQuotedText:(self.hasQuotedText ? self.viewItem.displayableQuotedText : nil)];
const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth];
CGFloat maxTextWidth = (maxMessageWidth - (self.textTrailingMargin + self.textLeadingMargin + result.width));
CGFloat textWidth = 0.f;
// Author
{
OWSContactsManager *contactsManager = Environment.current.contactsManager;
NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.viewItem.quotedRecipientId];
UILabel *quotedAuthorLabel = [UILabel new];
quotedAuthorLabel.text = quotedAuthor;
quotedAuthorLabel.font = self.quotedAuthorFont;
quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail;
quotedAuthorLabel.numberOfLines = 1;
CGSize quotedAuthorSize = CGSizeCeil([quotedAuthorLabel sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]);
textWidth = MAX(textWidth, quotedAuthorSize.width);
result.height += self.quotedContentTopInset;
result.height += self.quotedAuthorHeight;
}
if (self.hasQuotedText) {
UILabel *quotedTextLabel = [self createQuotedTextLabel];
CGSize textSize = CGSizeCeil([quotedTextLabel sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]);
textWidth = MAX(textWidth, textSize.width);
result.height += self.quotedAuthorBottomSpacing;
result.height += textSize.height;
}
result.width += textWidth;
result.height += self.quotedContentBottomInset;
result.height = MAX(result.height, thumbnailHeight);
if (includeMargins) {
result.width += kBubbleThornSideInset;
}
CGSize result = [quotedMessageView sizeForMaxWidth:maxMessageWidth - kBubbleThornSideInset];
result.width += kBubbleThornSideInset;
return result;
}
@ -1412,82 +1245,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
return (CGFloat)ceil([self tapForMoreFont].lineHeight * 1.25);
}
// TODO:
- (UIFont *)quotedAuthorFont
{
return [UIFont ows_regularFontWithSize:10.f];
}
// TODO:
- (CGFloat)quotedAuthorHeight
{
return (CGFloat)ceil([self quotedAuthorFont].lineHeight * 1.25);
}
// TODO:
- (CGFloat)quotedAuthorBottomSpacing
{
return 2.f;
}
// TODO:
- (CGFloat)quotedContentTopInset
{
return 3.f;
}
// TODO:
- (CGFloat)quotedContentBottomInset
{
return 3.f;
}
// Distance from top edge of "quoted message" bubble to top of message bubble.
// TODO:
- (CGFloat)quotedMessageTopInset
{
return 3.f;
}
// Distance from side of "quoted message" bubble to side of message bubble.
// TODO:
- (CGFloat)quotedMessageHInset
{
return 3.f;
}
// TODO:
- (CGFloat)quotedReplyStripeThickness
{
return 3.f;
}
// The spacing between the vertical "quoted reply stripe"
// and the quoted message content.
// TODO:
- (CGFloat)quotedReplyStripeHSpacing
{
return 10.f;
}
// TODO:
- (CGFloat)quotedThumbnailSize
{
return 30.f;
}
// TODO:
- (CGFloat)quotedThumbnailHSpacing
{
return 10.f;
}
// TODO:
- (CGFloat)quotedContentTrailingMargin
{
return 10.f;
}
#pragma mark -
- (BOOL)isIncoming
@ -1518,24 +1275,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
return result;
}
- (CGFloat)quotedBubbleLeadingMargin
{
CGFloat result = self.quotedMessageHInset;
if (self.isIncoming) {
result += kBubbleThornSideInset;
}
return result;
}
- (CGFloat)quotedBubbleTrailingMargin
{
CGFloat result = self.quotedMessageHInset;
if (!self.isIncoming) {
result += kBubbleThornSideInset;
}
return result;
}
- (CGFloat)textTopMargin
{
return kBubbleTextVInset;
@ -1551,11 +1290,6 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
return self.isIncoming ? [UIColor blackColor] : [UIColor whiteColor];
}
- (UIColor *)quotedTextColor
{
return [UIColor blackColor];
}
- (BOOL)isMediaBeingSent
{
if (self.isIncoming) {
@ -1595,7 +1329,7 @@ CG_INLINE CGSize CGSizeCeil(CGSize size)
self.bubbleView.hidden = YES;
self.bubbleView.bubbleColor = nil;
self.bubbleView.bubbleStrokeView = nil;
[self.bubbleView clearPartnerViews];
for (UIView *subview in self.bubbleView.subviews) {
[subview removeFromSuperview];

@ -0,0 +1,32 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@class DisplayableText;
@class OWSBubbleStrokeView;
@class TSQuotedMessage;
@interface OWSQuotedMessageView : UIView
@property (nonatomic, nullable, readonly) OWSBubbleStrokeView *boundsStrokeView;
- (instancetype)init NS_UNAVAILABLE;
// Only needs to be called if we're going to render this instance.
- (void)createContents;
// Measurement
- (CGSize)sizeForMaxWidth:(CGFloat)maxWidth;
// Factory method for "message bubble" views.
+ (OWSQuotedMessageView *)quotedMessageViewForConversation:(TSQuotedMessage *)quotedMessage
displayableQuotedText:(nullable DisplayableText *)displayableQuotedText;
// Factory method for "message compose" views.
+ (OWSQuotedMessageView *)quotedMessageViewForPreview:(TSQuotedMessage *)quotedMessage;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,383 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSQuotedMessageView.h"
#import "ConversationViewItem.h"
#import "Environment.h"
#import "OWSBubbleStrokeView.h"
#import "Signal-Swift.h"
#import <SignalMessaging/OWSContactsManager.h>
#import <SignalMessaging/SignalMessaging-Swift.h>
#import <SignalMessaging/UIColor+OWS.h>
#import <SignalMessaging/UIView+OWS.h>
#import <SignalServiceKit/TSAttachmentStream.h>
#import <SignalServiceKit/TSMessage.h>
#import <SignalServiceKit/TSQuotedMessage.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWSQuotedMessageView ()
@property (nonatomic, readonly) TSQuotedMessage *quotedMessage;
@property (nonatomic, nullable, readonly) DisplayableText *displayableQuotedText;
@property (nonatomic, readonly) UIFont *textMessageFont;
@property (nonatomic, nullable) OWSBubbleStrokeView *boundsStrokeView;
@end
#pragma mark -
@implementation OWSQuotedMessageView
+ (OWSQuotedMessageView *)quotedMessageViewForConversation:(TSQuotedMessage *)quotedMessage
displayableQuotedText:(nullable DisplayableText *)displayableQuotedText
{
OWSAssert(quotedMessage);
return
[[OWSQuotedMessageView alloc] initWithQuotedMessage:quotedMessage displayableQuotedText:displayableQuotedText];
}
+ (OWSQuotedMessageView *)quotedMessageViewForPreview:(TSQuotedMessage *)quotedMessage
{
OWSAssert(quotedMessage);
DisplayableText *_Nullable displayableQuotedText = nil;
if (quotedMessage.body.length > 0) {
displayableQuotedText = [DisplayableText displayableText:quotedMessage.body];
}
return
[[OWSQuotedMessageView alloc] initWithQuotedMessage:quotedMessage displayableQuotedText:displayableQuotedText];
}
- (instancetype)initWithQuotedMessage:(TSQuotedMessage *)quotedMessage
displayableQuotedText:(nullable DisplayableText *)displayableQuotedText
{
self = [super init];
if (!self) {
return self;
}
OWSAssert(quotedMessage);
_quotedMessage = quotedMessage;
_displayableQuotedText = displayableQuotedText;
_textMessageFont = [UIFont ows_dynamicTypeBodyFont];
return self;
}
- (BOOL)hasQuotedAttachment
{
return (self.quotedMessage.contentType.length > 0
&& ![OWSMimeTypeOversizeTextMessage isEqualToString:self.quotedMessage.contentType]);
}
- (BOOL)hasQuotedAttachmentThumbnail
{
return (self.quotedMessage.contentType.length > 0
&& ![OWSMimeTypeOversizeTextMessage isEqualToString:self.quotedMessage.contentType] &&
[TSAttachmentStream hasThumbnailForMimeType:self.quotedMessage.contentType]);
}
- (NSString *)quotedSnippet
{
if (self.displayableQuotedText.displayText.length > 0) {
return self.displayableQuotedText.displayText;
} else {
NSString *mimeType = self.quotedMessage.contentType;
if (mimeType.length > 0) {
return [TSAttachment emojiForMimeType:mimeType];
}
}
return @"";
}
#pragma mark -
- (void)createContents
{
self.backgroundColor = [UIColor whiteColor];
self.userInteractionEnabled = NO;
self.layoutMargins = UIEdgeInsetsZero;
self.clipsToBounds = YES;
self.boundsStrokeView = [OWSBubbleStrokeView new];
self.boundsStrokeView.strokeColor = OWSMessagesBubbleImageFactory.bubbleColorIncoming;
self.boundsStrokeView.strokeThickness = 1.f;
[self addSubview:self.boundsStrokeView];
[self.boundsStrokeView autoPinToSuperviewEdges];
[self.boundsStrokeView setContentHuggingLow];
[self.boundsStrokeView setCompressionResistanceLow];
UIView *_Nullable quotedAttachmentView = nil;
// TODO:
if (self.hasQuotedAttachment) {
// TODO:
quotedAttachmentView = [UIView containerView];
quotedAttachmentView.userInteractionEnabled = NO;
quotedAttachmentView.backgroundColor = [UIColor redColor];
[self addSubview:quotedAttachmentView];
[quotedAttachmentView autoPinTrailingToSuperviewMarginWithInset:self.quotedContentHInset];
[quotedAttachmentView autoVCenterInSuperview];
[quotedAttachmentView autoSetDimension:ALDimensionWidth toSize:self.quotedAttachmentSize];
[quotedAttachmentView autoSetDimension:ALDimensionHeight toSize:self.quotedAttachmentSize];
[quotedAttachmentView setContentHuggingHigh];
[quotedAttachmentView setCompressionResistanceHigh];
if (quotedAttachmentView) {
quotedAttachmentView.layer.borderColor = [UIColor colorWithWhite:0.f alpha:0.1f].CGColor;
quotedAttachmentView.layer.borderWidth = 1.f;
quotedAttachmentView.layer.cornerRadius = 2.f;
}
}
OWSContactsManager *contactsManager = Environment.current.contactsManager;
NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.quotedMessage.authorId];
UILabel *quotedAuthorLabel = [UILabel new];
{
quotedAuthorLabel.text = quotedAuthor;
quotedAuthorLabel.font = self.quotedAuthorFont;
// TODO:
quotedAuthorLabel.textColor = [UIColor ows_darkGrayColor];
quotedAuthorLabel.numberOfLines = 1;
quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[self addSubview:quotedAuthorLabel];
[quotedAuthorLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.quotedAuthorTopInset];
[quotedAuthorLabel autoPinLeadingToSuperviewMarginWithInset:self.quotedContentHInset];
if (quotedAttachmentView) {
[quotedAuthorLabel autoPinTrailingToLeadingEdgeOfView:quotedAttachmentView
offset:self.quotedAttachmentHSpacing];
} else {
[quotedAuthorLabel autoPinTrailingToSuperviewMarginWithInset:self.quotedContentHInset];
}
[quotedAuthorLabel autoSetDimension:ALDimensionHeight toSize:self.quotedAuthorHeight];
[quotedAuthorLabel setContentHuggingLow];
[quotedAuthorLabel setCompressionResistanceLow];
}
{
// Stripe and text container.
UIView *stripeAndTextContainer = [UIView containerView];
[self addSubview:stripeAndTextContainer];
[stripeAndTextContainer autoPinEdge:ALEdgeTop
toEdge:ALEdgeBottom
ofView:quotedAuthorLabel
withOffset:self.quotedAuthorBottomSpacing];
[stripeAndTextContainer autoPinLeadingToSuperviewMarginWithInset:self.quotedContentHInset];
if (quotedAttachmentView) {
[stripeAndTextContainer autoPinTrailingToLeadingEdgeOfView:quotedAttachmentView
offset:self.quotedAttachmentHSpacing];
} else {
[stripeAndTextContainer autoPinTrailingToSuperviewMarginWithInset:self.quotedContentHInset];
}
[stripeAndTextContainer autoPinBottomToSuperviewMarginWithInset:self.quotedTextBottomInset];
[stripeAndTextContainer setContentHuggingLow];
[stripeAndTextContainer setCompressionResistanceLow];
// Stripe.
BOOL isIncomingQuote
= ![NSObject isNullableObject:self.quotedMessage.authorId equalTo:TSAccountManager.localNumber];
UIColor *stripeColor = (isIncomingQuote ? OWSMessagesBubbleImageFactory.bubbleColorIncoming
: OWSMessagesBubbleImageFactory.bubbleColorOutgoingSent);
UIView *quoteStripView = [UIView containerView];
quoteStripView.backgroundColor = stripeColor;
quoteStripView.userInteractionEnabled = NO;
[stripeAndTextContainer addSubview:quoteStripView];
[quoteStripView autoPinHeightToSuperview];
[quoteStripView autoPinLeadingToSuperviewMargin];
[quoteStripView autoSetDimension:ALDimensionWidth toSize:self.quotedReplyStripeThickness];
[quoteStripView setContentHuggingVerticalLow];
[quoteStripView setContentHuggingHorizontalHigh];
[quoteStripView setCompressionResistanceHigh];
// Text.
UILabel *quotedTextLabel = [self createQuotedTextLabel];
[stripeAndTextContainer addSubview:quotedTextLabel];
[quotedTextLabel autoPinTopToSuperviewMarginWithInset:self.quotedReplyStripeVExtension];
[quotedTextLabel autoPinBottomToSuperviewMarginWithInset:self.quotedReplyStripeVExtension];
[quotedTextLabel autoPinLeadingToTrailingEdgeOfView:quoteStripView offset:self.quotedReplyStripeHSpacing];
[quotedTextLabel autoPinTrailingToSuperviewMargin];
[quotedTextLabel setContentHuggingLow];
[quotedTextLabel setCompressionResistanceLow];
}
}
#pragma mark - Measurement
- (CGSize)sizeForMaxWidth:(CGFloat)maxWidth
{
CGSize result = CGSizeZero;
result.width += self.quotedContentHInset;
CGFloat thumbnailHeight = 0.f;
if (self.hasQuotedAttachment) {
result.width += self.quotedAttachmentHSpacing;
result.width += self.quotedAttachmentSize;
thumbnailHeight += self.quotedAttachmentMinVInset;
thumbnailHeight += self.quotedAttachmentSize;
thumbnailHeight += self.quotedAttachmentMinVInset;
}
result.width += self.quotedContentHInset;
// Quoted Author
CGFloat quotedAuthorWidth = 0.f;
{
CGFloat maxQuotedAuthorWidth = maxWidth - result.width;
OWSContactsManager *contactsManager = Environment.current.contactsManager;
NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.quotedMessage.authorId];
UILabel *quotedAuthorLabel = [UILabel new];
quotedAuthorLabel.text = quotedAuthor;
quotedAuthorLabel.font = self.quotedAuthorFont;
quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail;
quotedAuthorLabel.numberOfLines = 1;
CGSize quotedAuthorSize
= CGSizeCeil([quotedAuthorLabel sizeThatFits:CGSizeMake(maxQuotedAuthorWidth, CGFLOAT_MAX)]);
quotedAuthorWidth = quotedAuthorSize.width;
result.height += self.quotedAuthorTopInset;
result.height += self.quotedAuthorHeight;
result.height += self.quotedAuthorBottomSpacing;
}
CGFloat quotedTextWidth = 0.f;
{
CGFloat maxQuotedTextWidth
= (maxWidth - (result.width + self.quotedReplyStripeThickness + self.quotedReplyStripeHSpacing));
UILabel *quotedTextLabel = [self createQuotedTextLabel];
CGSize textSize = CGSizeCeil([quotedTextLabel sizeThatFits:CGSizeMake(maxQuotedTextWidth, CGFLOAT_MAX)]);
quotedTextWidth = textSize.width + self.quotedReplyStripeThickness + self.quotedReplyStripeHSpacing;
result.height += textSize.height + self.quotedReplyStripeVExtension * 2;
}
CGFloat textWidth = MAX(quotedAuthorWidth, quotedTextWidth);
result.width += textWidth;
result.height += self.quotedTextBottomInset;
result.height = MAX(result.height, thumbnailHeight);
return result;
}
- (UILabel *)createQuotedTextLabel
{
UILabel *quotedTextLabel = [UILabel new];
quotedTextLabel.numberOfLines = 3;
quotedTextLabel.lineBreakMode = NSLineBreakByWordWrapping;
quotedTextLabel.text = self.quotedSnippet;
quotedTextLabel.textColor = self.quotedTextColor;
// Honor dynamic type in the message bodies.
quotedTextLabel.font = self.textMessageFont;
return quotedTextLabel;
}
- (UIColor *)quotedTextColor
{
return [UIColor blackColor];
}
// TODO:
- (UIFont *)quotedAuthorFont
{
return [UIFont ows_regularFontWithSize:10.f];
}
// TODO:
- (CGFloat)quotedAuthorHeight
{
return (CGFloat)ceil([self quotedAuthorFont].lineHeight * 1.f);
}
// TODO:
- (CGFloat)quotedAuthorTopInset
{
return 4.f;
}
// TODO:
- (CGFloat)quotedAuthorBottomSpacing
{
return 2.f;
}
// TODO:
- (CGFloat)quotedTextBottomInset
{
return 5.f;
}
// TODO:
- (CGFloat)quotedReplyStripeThickness
{
return 2.f;
}
// TODO:
- (CGFloat)quotedReplyStripeVExtension
{
return 5.f;
}
// The spacing between the vertical "quoted reply stripe"
// and the quoted message content.
// TODO:
- (CGFloat)quotedReplyStripeHSpacing
{
return 8.f;
}
// Distance from top edge of "quoted message" bubble to top of message bubble.
// TODO:
- (CGFloat)quotedAttachmentMinVInset
{
return 10.f;
}
// TODO:
- (CGFloat)quotedAttachmentSize
{
return 30.f;
}
// TODO:
- (CGFloat)quotedAttachmentHSpacing
{
return 10.f;
}
// Distance from sides of the quoted content to the sides of the message bubble.
// TODO:
- (CGFloat)quotedContentHInset
{
return 8.f;
}
#pragma mark -
- (CGSize)sizeThatFits:(CGSize)size
{
return [self sizeForMaxWidth:CGFLOAT_MAX];
}
@end
NS_ASSUME_NONNULL_END

@ -284,15 +284,6 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
}
[self updateBarButtonItems];
dispatch_async(dispatch_get_main_queue(), ^{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
TSThread *thread = [self threadForIndexPath:indexPath];
if (!thread) {
return;
}
[self presentThread:thread keyboardOnViewAppearing:NO callOnViewAppearing:NO];
});
}
- (void)viewDidAppear:(BOOL)animated

@ -18,6 +18,8 @@ NS_ASSUME_NONNULL_BEGIN
+ (UIFont *)ows_boldFontWithSize:(CGFloat)size;
+ (UIFont *)ows_dynamicTypeBodyFont:(CGFloat)size;
#pragma mark - Icon Fonts
+ (UIFont *)ows_fontAwesomeFont:(CGFloat)size;

@ -33,6 +33,11 @@ NS_ASSUME_NONNULL_BEGIN
return [UIFont boldSystemFontOfSize:size];
}
+ (UIFont *)ows_dynamicTypeBodyFont:(CGFloat)size
{
return [UIFont ows_dynamicTypeBodyFont];
}
#pragma mark - Icon Fonts
+ (UIFont *)ows_fontAwesomeFont:(CGFloat)size

@ -125,4 +125,11 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value);
@end
#pragma mark - Macros
CG_INLINE CGSize CGSizeCeil(CGSize size)
{
return CGSizeMake((CGFloat)ceil(size.width), (CGFloat)ceil(size.height));
}
NS_ASSUME_NONNULL_END

@ -9,7 +9,8 @@ import SignalServiceKit
@objc
public class OWSMessagesBubbleImageFactory: NSObject {
static let shared = OWSMessagesBubbleImageFactory()
@objc
public static let shared = OWSMessagesBubbleImageFactory()
private let jsqFactory = JSQMessagesBubbleImageFactory()!
@ -57,12 +58,16 @@ public class OWSMessagesBubbleImageFactory: NSObject {
}
}
@objc
public static let bubbleColorIncoming = UIColor.jsq_messageBubbleLightGray()!
@objc
public static let bubbleColorOutgoingUnsent = UIColor.gray
@objc
public static let bubbleColorOutgoingSending = UIColor.ows_fadedBlue
@objc
public static let bubbleColorOutgoingSent = UIColor.ows_materialBlue
public func bubbleColor(message: TSMessage) -> UIColor {

Loading…
Cancel
Save