diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.h index c2ddcc92b..c02a4b70e 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.h @@ -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 @property (nonatomic) UIColor *strokeColor; @property (nonatomic) CGFloat strokeThickness; -- (void)updateLayers; - @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.m index 368f44fef..425bed494 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleStrokeView.m @@ -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]; } } diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.h index b7ea6b871..4ef469937 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.h @@ -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,12 +15,20 @@ extern const CGFloat kBubbleThornVInset; extern const CGFloat kBubbleTextHInset; extern const CGFloat kBubbleTextVInset; -@class OWSBubbleStrokeView; +@class OWSBubbleView; + +@protocol OWSBubbleViewPartner + +- (void)updateLayers; + +- (void)setBubbleView:(OWSBubbleView *)bubbleView; + +@end + +#pragma mark - @interface OWSBubbleView : UIView -@property (nonatomic, weak, nullable) OWSBubbleStrokeView *bubbleStrokeView; - @property (nonatomic) BOOL isOutgoing; @property (nonatomic) BOOL hideTail; @property (nonatomic) BOOL isTruncated; @@ -27,6 +37,14 @@ extern const CGFloat kBubbleTextVInset; - (UIBezierPath *)maskPath; +#pragma mark - Coordination + +- (void)addPartnerView:(id)view; + +- (void)clearPartnerViews; + +- (void)updatePartnerViews; + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m index c8f3acf69..4cb83dd3f 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSBubbleView.m @@ -3,7 +3,6 @@ // #import "OWSBubbleView.h" -#import "OWSBubbleStrokeView.h" #import 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> *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)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 partnerView in self.partnerViews) { + [partnerView updateLayers]; + } +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h index 789b94fbb..1a52f7555 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h @@ -10,6 +10,8 @@ NS_ASSUME_NONNULL_BEGIN + (NSString *)cellReuseIdentifier; ++ (UIFont *)defaultTextMessageFont; + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 8e31d7194..f81e766ba 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -147,11 +147,16 @@ NS_ASSUME_NONNULL_BEGIN return NSStringFromClass([self class]); } ++ (UIFont *)defaultTextMessageFont +{ + return [UIFont ows_dynamicTypeBodyFont]; +} + - (UIFont *)textMessageFont { OWSAssert(DisplayableText.kMaxJumbomojiCount == 5); - CGFloat basePointSize = [UIFont ows_dynamicTypeBodyFont].pointSize; + CGFloat basePointSize = self.class.defaultTextMessageFont.pointSize; switch (self.displayableBodyText.jumbomojiCount) { case 0: break; @@ -361,8 +366,10 @@ NS_ASSUME_NONNULL_BEGIN if (self.isQuotedReply) { OWSAssert(!lastSubview); + TSMessage *message = (TSMessage *)self.viewItem.interaction; OWSQuotedMessageView *quotedMessageView = - [[OWSQuotedMessageView alloc] initWithViewItem:self.viewItem textMessageFont:self.textMessageFont]; + [OWSQuotedMessageView quotedMessageViewForConversation:message.quotedMessage + displayableQuotedText:self.viewItem.displayableQuotedText]; [quotedMessageView createContents]; [self.bubbleView addSubview:quotedMessageView]; @@ -381,6 +388,8 @@ NS_ASSUME_NONNULL_BEGIN } lastSubview = quotedMessageView; bottomMargin = 0; + + [self.bubbleView addPartnerView:quotedMessageView]; } UIView *_Nullable bodyMediaView = nil; @@ -471,8 +480,8 @@ NS_ASSUME_NONNULL_BEGIN [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]; } } @@ -1174,8 +1183,10 @@ NS_ASSUME_NONNULL_BEGIN return CGSizeZero; } + TSMessage *message = (TSMessage *)self.viewItem.interaction; OWSQuotedMessageView *quotedMessageView = - [[OWSQuotedMessageView alloc] initWithViewItem:self.viewItem textMessageFont:self.textMessageFont]; + [OWSQuotedMessageView quotedMessageViewForConversation:message.quotedMessage + displayableQuotedText:self.viewItem.displayableQuotedText]; const int maxMessageWidth = [self maxMessageWidthForContentWidth:contentWidth]; CGSize result = [quotedMessageView sizeForMaxWidth:maxMessageWidth - kBubbleThornSideInset]; result.width += kBubbleThornSideInset; @@ -1324,7 +1335,7 @@ NS_ASSUME_NONNULL_BEGIN self.bubbleView.hidden = YES; self.bubbleView.bubbleColor = nil; - self.bubbleView.bubbleStrokeView = nil; + [self.bubbleView clearPartnerViews]; for (UIView *subview in self.bubbleView.subviews) { [subview removeFromSuperview]; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h index 45cc7ce14..f65109e8c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.h @@ -2,25 +2,28 @@ // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // +#import "OWSBubbleView.h" + NS_ASSUME_NONNULL_BEGIN -@class ConversationViewItem; +@class DisplayableText; @class TSQuotedMessage; -@interface OWSQuotedMessageView : UIView +@interface OWSQuotedMessageView : UIView - (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithViewItem:(ConversationViewItem *)viewItem - // quotedMessage:(TSQuotedMessage *)quotedMessage - textMessageFont:(UIFont *)textMessageFont; - // Only needs to be called if we're going to render this instance. - (void)createContents; // Measurement - (CGSize)sizeForMaxWidth:(CGFloat)maxWidth; ++ (OWSQuotedMessageView *)quotedMessageViewForConversation:(TSQuotedMessage *)quotedMessage + displayableQuotedText:(nullable DisplayableText *)displayableQuotedText; + ++ (OWSQuotedMessageView *)quotedMessageViewForPreview:(TSQuotedMessage *)quotedMessage; + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index 39d5a0b67..e8a956b16 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -5,6 +5,7 @@ #import "OWSQuotedMessageView.h" #import "ConversationViewItem.h" #import "Environment.h" +#import "OWSMessageCell.h" #import "Signal-Swift.h" #import #import @@ -18,16 +19,47 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSQuotedMessageView () -@property (nonatomic, readonly) ConversationViewItem *viewItem; +@property (nonatomic, readonly) TSQuotedMessage *quotedMessage; +@property (nonatomic, nullable, readonly) DisplayableText *displayableQuotedText; + @property (nonatomic, readonly) UIFont *textMessageFont; +@property (nonatomic, readonly) UIColor *strokeColor; +@property (nonatomic, readonly) CGFloat strokeThickness; + +// TODO: Replace with a bubble stroke view. +@property (nonatomic) CAShapeLayer *shapeLayer; + +@property (nonatomic, weak) OWSBubbleView *bubbleView; + @end @implementation OWSQuotedMessageView -- (instancetype)initWithViewItem:(ConversationViewItem *)viewItem - // quotedMessage:(TSQuotedMessage *)quotedMessage - textMessageFont:(UIFont *)textMessageFont ++ (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]; @@ -35,37 +67,31 @@ NS_ASSUME_NONNULL_BEGIN return self; } - OWSAssert(viewItem); - // OWSAssert(quotedMessage); - OWSAssert(textMessageFont); + OWSAssert(quotedMessage); + OWSAssert(displayableQuotedText); - _viewItem = viewItem; - // _quotedMessage = quotedMessage; - _textMessageFont = textMessageFont; + _quotedMessage = quotedMessage; + _displayableQuotedText = displayableQuotedText; + _textMessageFont = OWSMessageCell.defaultTextMessageFont; + _strokeColor = OWSMessagesBubbleImageFactory.bubbleColorIncoming; + _strokeThickness = 1.f; + + self.shapeLayer = [CAShapeLayer new]; + [self.layer addSublayer:self.shapeLayer]; return self; } -- (BOOL)isIncoming -{ - return self.viewItem.interaction.interactionType == OWSInteractionType_IncomingMessage; -} - - (BOOL)hasQuotedAttachmentThumbnail { - // This should always be valid for the appropriate cell types. - OWSAssert(self.viewItem); - - return (self.viewItem.hasQuotedAttachment && - [TSAttachmentStream hasThumbnailForMimeType:self.viewItem.quotedAttachmentMimetype]); + return (self.quotedMessage.contentType.length > 0 && + [TSAttachmentStream hasThumbnailForMimeType:self.quotedMessage.contentType]); } #pragma mark - - (void)createContents { - OWSAssert(self.viewItem.isQuotedReply); - self.backgroundColor = [UIColor whiteColor]; self.userInteractionEnabled = NO; self.layoutMargins = UIEdgeInsetsZero; @@ -91,14 +117,15 @@ NS_ASSUME_NONNULL_BEGIN } OWSContactsManager *contactsManager = Environment.current.contactsManager; - NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.viewItem.quotedRecipientId]; + NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.quotedMessage.authorId]; UILabel *quotedAuthorLabel = [UILabel new]; { quotedAuthorLabel.text = quotedAuthor; quotedAuthorLabel.font = self.quotedAuthorFont; - quotedAuthorLabel.textColor - = (self.isIncoming ? [UIColor colorWithRGBHex:0xd84315] : [UIColor colorWithRGBHex:0x007884]); + // TODO: + quotedAuthorLabel.textColor = [UIColor ows_darkGrayColor]; + // = (self.isIncoming ? [UIColor colorWithRGBHex:0xd84315] : [UIColor colorWithRGBHex:0x007884]); quotedAuthorLabel.numberOfLines = 1; quotedAuthorLabel.lineBreakMode = NSLineBreakByTruncatingTail; [self addSubview:quotedAuthorLabel]; @@ -135,8 +162,12 @@ NS_ASSUME_NONNULL_BEGIN [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 = (self.isIncoming ? [UIColor whiteColor] : [UIColor colorWithRGBHex:0x007884]); + quoteStripView.backgroundColor = stripeColor; quoteStripView.userInteractionEnabled = NO; [stripeAndTextContainer addSubview:quoteStripView]; [quoteStripView autoPinHeightToSuperview]; @@ -163,15 +194,8 @@ NS_ASSUME_NONNULL_BEGIN // TODO: Class method? - (CGSize)sizeForMaxWidth:(CGFloat)maxWidth { - OWSAssert(self.viewItem); - OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]); - CGSize result = CGSizeZero; - if (!self.viewItem.isQuotedReply) { - return result; - } - result.width += self.quotedContentHInset; CGFloat thumbnailHeight = 0.f; @@ -192,7 +216,7 @@ NS_ASSUME_NONNULL_BEGIN CGFloat maxQuotedAuthorWidth = maxWidth - result.width; OWSContactsManager *contactsManager = Environment.current.contactsManager; - NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.viewItem.quotedRecipientId]; + NSString *quotedAuthor = [contactsManager displayNameForPhoneIdentifier:self.quotedMessage.authorId]; UILabel *quotedAuthorLabel = [UILabel new]; quotedAuthorLabel.text = quotedAuthor; @@ -253,10 +277,10 @@ NS_ASSUME_NONNULL_BEGIN - (NSString *)quotedSnippet { - if (self.viewItem.hasQuotedText && self.viewItem.displayableQuotedText.displayText.length > 0) { - return self.viewItem.displayableQuotedText.displayText; + if (self.displayableQuotedText.displayText.length > 0) { + return self.displayableQuotedText.displayText; } else { - NSString *mimeType = self.viewItem.quotedAttachmentMimetype; + NSString *mimeType = self.quotedMessage.contentType; if (mimeType.length > 0) { return [TSAttachment emojiForMimeType:mimeType]; @@ -299,7 +323,7 @@ NS_ASSUME_NONNULL_BEGIN // TODO: - (CGFloat)quotedReplyStripeThickness { - return 3.f; + return 2.f; } // TODO: @@ -342,6 +366,101 @@ NS_ASSUME_NONNULL_BEGIN return 8.f; } +#pragma mark - Stroke + +//- (instancetype)init +//{ +// self = [super init]; +// if (!self) { +// return self; +// } +// +// self.opaque = NO; +// self.backgroundColor = [UIColor clearColor]; +// +// +// [self updateLayers]; +// +// return self; +//} + +- (void)setStrokeColor:(UIColor *)strokeColor +{ + _strokeColor = strokeColor; + + [self updateLayers]; +} + +- (void)setStrokeThickness:(CGFloat)strokeThickness +{ + _strokeThickness = strokeThickness; + + [self updateLayers]; +} + +- (void)setFrame:(CGRect)frame +{ + BOOL didChange = !CGRectEqualToRect(self.frame, frame); + + [super setFrame:frame]; + + if (didChange) { + [self updateLayers]; + } +} + +- (void)setBounds:(CGRect)bounds +{ + BOOL didChange = !CGRectEqualToRect(self.bounds, bounds); + + [super setBounds:bounds]; + + if (didChange) { + [self updateLayers]; + } +} + +- (void)setCenter:(CGPoint)center +{ + [super setCenter:center]; + + [self updateLayers]; +} + +- (void)updateLayers +{ + if (!self.shapeLayer) { + return; + } + + // Don't fill the shape layer; we just want a stroke around the border. + self.shapeLayer.fillColor = [UIColor clearColor].CGColor; + + self.clipsToBounds = YES; + + if (!self.bubbleView) { + return; + } + + self.shapeLayer.strokeColor = self.strokeColor.CGColor; + self.shapeLayer.lineWidth = self.strokeThickness; + self.shapeLayer.zPosition = 100.f; + + UIBezierPath *bezierPath = [UIBezierPath new]; + + UIBezierPath *boundsBezierPath = [UIBezierPath bezierPathWithRect:self.bounds]; + [bezierPath appendPath:boundsBezierPath]; + + UIBezierPath *bubbleBezierPath = [self.bubbleView maskPath]; + // We need to convert between coordinate systems using layers, not views. + CGPoint bubbleOffset = [self.layer convertPoint:CGPointZero fromLayer:self.bubbleView.layer]; + CGAffineTransform transform = CGAffineTransformMakeTranslation(bubbleOffset.x, bubbleOffset.y); + [bubbleBezierPath applyTransform:transform]; + [bezierPath appendPath:bubbleBezierPath]; + + self.shapeLayer.path = bezierPath.CGPath; +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 8cad36aaa..61b4cf186 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -2299,6 +2299,14 @@ typedef enum : NSUInteger { - (void)scrollDownButtonTapped { +#ifdef DEBUG + CGPoint contentOffset = self.collectionView.contentOffset; + contentOffset.y += self.collectionView.height + - (self.collectionView.contentInset.top + self.collectionView.contentInset.bottom); + [self.collectionView setContentOffset:contentOffset animated:NO]; + return; +#endif + NSIndexPath *indexPathOfUnreadMessagesIndicator = [self indexPathOfUnreadMessagesIndicator]; if (indexPathOfUnreadMessagesIndicator != nil) { NSInteger unreadRow = indexPathOfUnreadMessagesIndicator.row; diff --git a/SignalMessaging/utils/OWSMessagesBubbleImageFactory.swift b/SignalMessaging/utils/OWSMessagesBubbleImageFactory.swift index 7f9c65d38..83582cf4b 100644 --- a/SignalMessaging/utils/OWSMessagesBubbleImageFactory.swift +++ b/SignalMessaging/utils/OWSMessagesBubbleImageFactory.swift @@ -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 {