diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index ef4dce017..0706ac717 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -372,8 +372,8 @@ 340CB2231EAC155C0001CAA1 /* ContactsViewHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContactsViewHelper.m; sourceTree = ""; }; 340CB2251EAC25820001CAA1 /* UpdateGroupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UpdateGroupViewController.h; sourceTree = ""; }; 340CB2261EAC25820001CAA1 /* UpdateGroupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UpdateGroupViewController.m; sourceTree = ""; }; - 341207251EE19F6A00463194 /* OWSSystemMessageCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSSystemMessageCell.h; sourceTree = ""; }; - 341207261EE19F6A00463194 /* OWSSystemMessageCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSSystemMessageCell.m; sourceTree = ""; }; + 341207251EE19F6A00463194 /* OWSSystemMessageCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = OWSSystemMessageCell.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 341207261EE19F6A00463194 /* OWSSystemMessageCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = OWSSystemMessageCell.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 341BB7471DB727EE001E2975 /* JSQMediaItem+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JSQMediaItem+OWS.h"; sourceTree = ""; }; 341BB7481DB727EE001E2975 /* JSQMediaItem+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "JSQMediaItem+OWS.m"; sourceTree = ""; }; 34330A591E7875FB00DF2FB9 /* fontawesome-webfont.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "fontawesome-webfont.ttf"; sourceTree = ""; }; @@ -502,8 +502,8 @@ 34E8BF3A1EEB208E00F5F4CA /* DebugUIVerification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIVerification.m; sourceTree = ""; }; 34F3089A1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSUnreadIndicatorInteraction.h; sourceTree = ""; }; 34F3089B1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSUnreadIndicatorInteraction.m; sourceTree = ""; }; - 34F3089D1ECA580B00BB7697 /* OWSUnreadIndicatorCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSUnreadIndicatorCell.h; sourceTree = ""; }; - 34F3089E1ECA580B00BB7697 /* OWSUnreadIndicatorCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSUnreadIndicatorCell.m; sourceTree = ""; }; + 34F3089D1ECA580B00BB7697 /* OWSUnreadIndicatorCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = OWSUnreadIndicatorCell.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 34F3089E1ECA580B00BB7697 /* OWSUnreadIndicatorCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = OWSUnreadIndicatorCell.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 34F308A01ECB469700BB7697 /* OWSBezierPathView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBezierPathView.h; sourceTree = ""; }; 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBezierPathView.m; sourceTree = ""; }; 34FD936E1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = views/OWSAnyTouchGestureRecognizer.h; sourceTree = ""; }; @@ -542,7 +542,7 @@ 45387B031E36D650005D00B3 /* OWS102MoveLoggingPreferenceToUserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWS102MoveLoggingPreferenceToUserDefaults.m; path = Migrations/OWS102MoveLoggingPreferenceToUserDefaults.m; sourceTree = ""; }; 453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = ""; }; 453D28B81D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessagesBubblesSizeCalculator.h; sourceTree = ""; }; - 453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessagesBubblesSizeCalculator.m; sourceTree = ""; }; + 453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = OWSMessagesBubblesSizeCalculator.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 4542F0931EB9372700C7EE92 /* SystemContactsFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemContactsFetcher.swift; sourceTree = ""; }; 4542F0951EBB9E9A00C7EE92 /* Promise+retainUntilComplete.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Promise+retainUntilComplete.swift"; sourceTree = ""; }; 45464DBB1DFA041F001D3FD6 /* DataChannelMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataChannelMessage.swift; sourceTree = ""; }; diff --git a/Signal/src/Models/OWSMessagesBubblesSizeCalculator.m b/Signal/src/Models/OWSMessagesBubblesSizeCalculator.m index 3e48bb822..056ffd175 100644 --- a/Signal/src/Models/OWSMessagesBubblesSizeCalculator.m +++ b/Signal/src/Models/OWSMessagesBubblesSizeCalculator.m @@ -128,8 +128,8 @@ NS_ASSUME_NONNULL_BEGIN return [cachedSize CGSizeValue]; } - CGSize result = [self.referenceSystemMessageCell cellSizeForInteraction:interaction - collectionViewWidth:layout.collectionView.width]; + CGSize result = [self.referenceSystemMessageCell bubbleSizeForInteraction:interaction + collectionViewWidth:layout.collectionView.width]; [self.cache setObject:[NSValue valueWithCGSize:result] forKey:cacheKey]; @@ -148,8 +148,8 @@ NS_ASSUME_NONNULL_BEGIN return [cachedSize CGSizeValue]; } - CGSize result = [self.referenceUnreadIndicatorCell cellSizeForInteraction:interaction - collectionViewWidth:layout.collectionView.width]; + CGSize result = [self.referenceUnreadIndicatorCell bubbleSizeForInteraction:interaction + collectionViewWidth:layout.collectionView.width]; [self.cache setObject:[NSValue valueWithCGSize:result] forKey:cacheKey]; diff --git a/Signal/src/ViewControllers/ConversationView/MessagesViewController.m b/Signal/src/ViewControllers/ConversationView/MessagesViewController.m index 42cda74c6..d6c0f2d6d 100644 --- a/Signal/src/ViewControllers/ConversationView/MessagesViewController.m +++ b/Signal/src/ViewControllers/ConversationView/MessagesViewController.m @@ -111,8 +111,8 @@ typedef enum : NSUInteger { @protocol OWSMessagesCollectionViewFlowLayoutDelegate -// Returns YES for incoming and outgoing text and attachment messages. -- (BOOL)isUserMessageAtIndexPath:(NSIndexPath *)indexPath; +// Returns YES for all but the unread indicator +- (BOOL)shouldShowCellDecorationsAtIndexPath:(NSIndexPath *)indexPath; @end @@ -131,12 +131,12 @@ typedef enum : NSUInteger { - (CGSize)sizeForItemAtIndexPath:(NSIndexPath *)indexPath { // The unread indicator should be sized according to its desired size. - if (![self.delegate isUserMessageAtIndexPath:indexPath]) { + if ([self.delegate shouldShowCellDecorationsAtIndexPath:indexPath]) { + return [super sizeForItemAtIndexPath:indexPath]; + } else { CGSize messageBubbleSize = [self messageBubbleSizeForItemAtIndexPath:indexPath]; CGFloat finalHeight = messageBubbleSize.height; return CGSizeMake(CGRectGetWidth(self.collectionView.frame), ceilf((float)finalHeight)); - } else { - return [super sizeForItemAtIndexPath:indexPath]; } } @@ -1723,6 +1723,9 @@ typedef enum : NSUInteger { [self.collectionView dequeueReusableCellWithReuseIdentifier:[OWSSystemMessageCell cellReuseIdentifier] forIndexPath:indexPath]; [cell configureWithInteraction:interaction]; + cell.cellTopLabel.attributedText = + [self collectionView:self.collectionView attributedTextForCellTopLabelAtIndexPath:indexPath]; + cell.systemMessageCellDelegate = self; return cell; @@ -4094,12 +4097,13 @@ typedef enum : NSUInteger { #pragma mark - OWSMessagesCollectionViewFlowLayoutDelegate -- (BOOL)isUserMessageAtIndexPath:(NSIndexPath *)indexPath; + +- (BOOL)shouldShowCellDecorationsAtIndexPath:(NSIndexPath *)indexPath { - // TODO: Eventually it'd be nice to this in a more performant way. TSInteraction *interaction = [self interactionAtIndexPath:indexPath]; - return ( - [interaction isKindOfClass:[TSIncomingMessage class]] || [interaction isKindOfClass:[TSOutgoingMessage class]]); + + // Show any top/bottom labels for all but the unread indicator + return ![interaction isKindOfClass:[TSUnreadIndicatorInteraction class]]; } #pragma mark - Class methods diff --git a/Signal/src/views/OWSSystemMessageCell.h b/Signal/src/views/OWSSystemMessageCell.h index 839c79804..60dfc1444 100644 --- a/Signal/src/views/OWSSystemMessageCell.h +++ b/Signal/src/views/OWSSystemMessageCell.h @@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)configureWithInteraction:(TSInteraction *)interaction; -- (CGSize)cellSizeForInteraction:(TSInteraction *)interaction collectionViewWidth:(CGFloat)collectionViewWidth; +- (CGSize)bubbleSizeForInteraction:(TSInteraction *)interaction collectionViewWidth:(CGFloat)collectionViewWidth; @end diff --git a/Signal/src/views/OWSSystemMessageCell.m b/Signal/src/views/OWSSystemMessageCell.m index 0e72a5a88..1a59b3d36 100644 --- a/Signal/src/views/OWSSystemMessageCell.m +++ b/Signal/src/views/OWSSystemMessageCell.m @@ -24,6 +24,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) UIImageView *imageView; @property (nonatomic) UILabel *titleLabel; +@property (nonatomic) UILabel *cellTopLabel; @end @@ -31,6 +32,9 @@ NS_ASSUME_NONNULL_BEGIN @implementation OWSSystemMessageCell +// override from JSQMessagesCollectionViewCell +@synthesize cellTopLabel = _cellTopLabel; + // `[UIView init]` invokes `[self initWithFrame:...]`. - (instancetype)initWithFrame:(CGRect)frame { @@ -49,6 +53,12 @@ NS_ASSUME_NONNULL_BEGIN self.backgroundColor = [UIColor whiteColor]; + self.cellTopLabel = [UILabel new]; + self.cellTopLabel.textAlignment = NSTextAlignmentCenter; + self.cellTopLabel.font = self.topLabelFont; + self.cellTopLabel.textColor = [UIColor lightGrayColor]; + [self.contentView addSubview:self.cellTopLabel]; + self.imageView = [UIImageView new]; [self.contentView addSubview:self.imageView]; @@ -84,6 +94,11 @@ NS_ASSUME_NONNULL_BEGIN [self setNeedsLayout]; } +- (UIFont *)topLabelFont +{ + return [UIFont boldSystemFontOfSize:12.0f]; +} + - (UIColor *)textColor { return [UIColor colorWithRGBHex:0x303030]; @@ -235,17 +250,24 @@ NS_ASSUME_NONNULL_BEGIN CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)]; CGFloat contentWidth = ([self iconSize] + [self hSpacing] + titleSize.width); + + CGSize topLabelSize = [self.cellTopLabel sizeThatFits:CGSizeMake(self.contentView.width, CGFLOAT_MAX)]; + self.cellTopLabel.frame = CGRectMake(0, 0, self.contentView.frame.size.width, topLabelSize.height); + + CGFloat topLabelSpacing = topLabelSize.height; + self.imageView.frame = CGRectMake(round((self.contentView.width - contentWidth) * 0.5f), - round((self.contentView.height - [self iconSize]) * 0.5f), + round((self.contentView.height - [self iconSize] + topLabelSpacing) * 0.5f), [self iconSize], [self iconSize]); + self.titleLabel.frame = CGRectMake(round(self.imageView.right + [self hSpacing]), - round((self.contentView.height - titleSize.height) * 0.5f), + round((self.contentView.height - titleSize.height + topLabelSpacing) * 0.5f), ceil(titleSize.width + 1.f), ceil(titleSize.height + 1.f)); } -- (CGSize)cellSizeForInteraction:(TSInteraction *)interaction collectionViewWidth:(CGFloat)collectionViewWidth +- (CGSize)bubbleSizeForInteraction:(TSInteraction *)interaction collectionViewWidth:(CGFloat)collectionViewWidth { CGSize result = CGSizeMake(collectionViewWidth, 0); result.height += self.topVMargin; @@ -254,6 +276,7 @@ NS_ASSUME_NONNULL_BEGIN [self applyTitleForInteraction:interaction label:self.titleLabel]; CGFloat maxTitleWidth = (collectionViewWidth - ([self hMargin] * 2.f + [self hSpacing] + [self iconSize])); CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)]; + CGFloat contentHeight = ceil(MAX([self iconSize], titleSize.height)); result.height += contentHeight; diff --git a/Signal/src/views/OWSUnreadIndicatorCell.h b/Signal/src/views/OWSUnreadIndicatorCell.h index 1d013bce5..1adaa9f1a 100644 --- a/Signal/src/views/OWSUnreadIndicatorCell.h +++ b/Signal/src/views/OWSUnreadIndicatorCell.h @@ -15,8 +15,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)configureWithInteraction:(TSUnreadIndicatorInteraction *)interaction; -- (CGSize)cellSizeForInteraction:(TSUnreadIndicatorInteraction *)interaction - collectionViewWidth:(CGFloat)collectionViewWidth; +- (CGSize)bubbleSizeForInteraction:(TSUnreadIndicatorInteraction *)interaction + collectionViewWidth:(CGFloat)collectionViewWidth; @end diff --git a/Signal/src/views/OWSUnreadIndicatorCell.m b/Signal/src/views/OWSUnreadIndicatorCell.m index 3abaa0e8c..cabd09b38 100644 --- a/Signal/src/views/OWSUnreadIndicatorCell.m +++ b/Signal/src/views/OWSUnreadIndicatorCell.m @@ -200,8 +200,8 @@ NS_ASSUME_NONNULL_BEGIN } } -- (CGSize)cellSizeForInteraction:(TSUnreadIndicatorInteraction *)interaction - collectionViewWidth:(CGFloat)collectionViewWidth +- (CGSize)bubbleSizeForInteraction:(TSUnreadIndicatorInteraction *)interaction + collectionViewWidth:(CGFloat)collectionViewWidth { CGSize result = CGSizeMake(collectionViewWidth, 0); result.height += self.titleVMargin * 2.f;