diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 0abe0763f..d815afea6 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -22,6 +22,8 @@ NS_ASSUME_NONNULL_BEGIN // * footerView (below message) @property (nonatomic) OWSMessageBubbleView *messageBubbleView; +@property (nonatomic) UIView *dateHeaderView; +@property (nonatomic) UIView *dateStrokeView; @property (nonatomic) UILabel *dateHeaderLabel; @property (nonatomic) AvatarImageView *avatarView; @@ -55,22 +57,28 @@ NS_ASSUME_NONNULL_BEGIN self.messageBubbleView = [OWSMessageBubbleView new]; [self.contentView addSubview:self.messageBubbleView]; + self.dateHeaderView = [UIView new]; + + self.dateStrokeView = [UIView new]; + self.dateStrokeView.backgroundColor = [UIColor lightGrayColor]; + [self.dateHeaderView addSubview:self.dateStrokeView]; + self.dateHeaderLabel = [UILabel new]; self.dateHeaderLabel.font = self.dateHeaderDateFont; self.dateHeaderLabel.textAlignment = NSTextAlignmentCenter; self.dateHeaderLabel.textColor = [UIColor lightGrayColor]; - [self.contentView addSubview:self.dateHeaderLabel]; + [self.dateHeaderView addSubview:self.dateHeaderLabel]; + + [self.dateStrokeView autoPinWidthToSuperview]; + [self.dateStrokeView autoSetDimension:ALDimensionHeight toSize:1.f]; + [self.dateHeaderLabel autoPinWidthToSuperview]; + [self.dateHeaderLabel autoVCenterInSuperview]; + [self.dateStrokeView autoPinEdge:ALEdgeBottom toEdge:ALEdgeTop ofView:self.dateHeaderLabel]; self.avatarView = [[AvatarImageView alloc] init]; - [self.contentView addSubview:self.avatarView]; [self.avatarView autoSetDimension:ALDimensionWidth toSize:self.avatarSize]; [self.avatarView autoSetDimension:ALDimensionHeight toSize:self.avatarSize]; - // Hide these views by default. - self.dateHeaderLabel.hidden = YES; - self.avatarView.hidden = YES; - - [self.messageBubbleView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.dateHeaderLabel]; [self.messageBubbleView autoPinBottomToSuperviewMarginWithInset:0]; self.contentView.userInteractionEnabled = YES; @@ -180,8 +188,6 @@ NS_ASSUME_NONNULL_BEGIN ofView:self.avatarView withOffset:avatarBottomMargin], ]]; - [self.messageBubbleView logFrameLaterWithLabel:@"messageBubbleView"]; - [self.avatarView logFrameLaterWithLabel:@"avatarView"]; } } @@ -225,7 +231,7 @@ NS_ASSUME_NONNULL_BEGIN NSString *timeString = [dateHeaderTimeFormatter stringFromDate:date]; NSAttributedString *attributedText = [NSAttributedString new]; - attributedText = [attributedText rtlSafeAppend:dateString + attributedText = [attributedText rtlSafeAppend:dateString.uppercaseString attributes:@{ NSFontAttributeName : self.dateHeaderDateFont, NSForegroundColorAttributeName : [UIColor lightGrayColor], @@ -244,20 +250,19 @@ NS_ASSUME_NONNULL_BEGIN referenceView:self]; self.dateHeaderLabel.attributedText = attributedText; - self.dateHeaderLabel.hidden = NO; + [self.contentView addSubview:self.dateHeaderView]; [self.viewConstraints addObjectsFromArray:@[ - // TODO: Are data headers symmetric or are they asymmetric? gutters are asymmetric? - [self.dateHeaderLabel autoPinLeadingToSuperviewMarginWithInset:self.conversationStyle.gutterLeading], - [self.dateHeaderLabel autoPinTrailingToSuperviewMarginWithInset:self.conversationStyle.gutterTrailing], - [self.dateHeaderLabel autoPinEdgeToSuperviewEdge:ALEdgeTop], - [self.dateHeaderLabel autoSetDimension:ALDimensionHeight toSize:self.dateHeaderHeight], + [self.dateHeaderView autoPinLeadingToSuperviewMarginWithInset:self.conversationStyle.gutterLeading], + [self.dateHeaderView autoPinTrailingToSuperviewMarginWithInset:self.conversationStyle.gutterTrailing], + [self.dateHeaderView autoPinEdgeToSuperviewEdge:ALEdgeTop], + [self.dateHeaderView autoSetDimension:ALDimensionHeight toSize:self.dateHeaderHeight], + + [self.messageBubbleView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.dateHeaderView], ]]; } else { - self.dateHeaderLabel.hidden = YES; [self.viewConstraints addObjectsFromArray:@[ - [self.dateHeaderLabel autoSetDimension:ALDimensionHeight toSize:0], - [self.dateHeaderLabel autoPinEdgeToSuperviewEdge:ALEdgeTop], + [self.messageBubbleView autoPinEdgeToSuperviewEdge:ALEdgeTop], ]]; } } @@ -298,7 +303,7 @@ NS_ASSUME_NONNULL_BEGIN diameter:self.avatarSize contactsManager:contactsManager]; self.avatarView.image = [avatarBuilder build]; - self.avatarView.hidden = NO; + [self.contentView addSubview:self.avatarView]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherUsersProfileDidChange:) @@ -365,11 +370,16 @@ NS_ASSUME_NONNULL_BEGIN return cellSize; } +- (CGFloat)dateHeaderVSpacing +{ + return 24.f; +} + - (CGFloat)dateHeaderHeight { if (self.viewItem.shouldShowDate) { - // Add 5pt spacing above and below the date header. - return (CGFloat)ceil(MAX(self.dateHeaderDateFont.lineHeight, self.dateHeaderTimeFont.lineHeight) + 10.f); + CGFloat textHeight = MAX(self.dateHeaderDateFont.capHeight, self.dateHeaderTimeFont.capHeight); + return (CGFloat)ceil(textHeight + self.dateHeaderVSpacing * 2); } else { return 0.f; } @@ -387,10 +397,10 @@ NS_ASSUME_NONNULL_BEGIN [self.messageBubbleView prepareForReuse]; [self.messageBubbleView unloadContent]; - self.dateHeaderLabel.text = nil; - self.dateHeaderLabel.hidden = YES; + [self.dateHeaderView removeFromSuperview]; + self.avatarView.image = nil; - self.avatarView.hidden = YES; + [self.avatarView removeFromSuperview]; [self hideMenuControllerIfNecessary]; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSUnreadIndicatorCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSUnreadIndicatorCell.m index c8d5918b1..46ec6d38f 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSUnreadIndicatorCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSUnreadIndicatorCell.m @@ -17,6 +17,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nullable) TSUnreadIndicatorInteraction *interaction; @property (nonatomic) UILabel *titleLabel; +@property (nonatomic) UILabel *subtitleLabel; @property (nonatomic) UIView *strokeView; @property (nonatomic) NSArray *layoutConstraints; @@ -45,15 +46,24 @@ NS_ASSUME_NONNULL_BEGIN self.strokeView = [UIView new]; // TODO: color. - self.strokeView.backgroundColor = [UIColor colorWithRGBHex:0xf6eee3]; + self.strokeView.backgroundColor = [UIColor blackColor]; [self.contentView addSubview:self.strokeView]; self.titleLabel = [UILabel new]; // TODO: color. - self.titleLabel.textColor = [UIColor colorWithRGBHex:0x403e3b]; + self.titleLabel.textColor = [UIColor blackColor]; self.titleLabel.textAlignment = NSTextAlignmentCenter; [self.contentView addSubview:self.titleLabel]; + self.subtitleLabel = [UILabel new]; + // TODO: color. + self.subtitleLabel.textColor = [UIColor lightGrayColor]; + // 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]; } @@ -62,7 +72,8 @@ NS_ASSUME_NONNULL_BEGIN // Update cell to reflect changes in dynamic text. // // TODO: Font size. - self.titleLabel.font = UIFont.ows_dynamicTypeSubheadlineFont; + self.titleLabel.font = UIFont.ows_dynamicTypeCaption1Font.ows_mediumWeight; + self.subtitleLabel.font = UIFont.ows_dynamicTypeCaption1Font; } + (NSString *)cellReuseIdentifier @@ -81,6 +92,7 @@ NS_ASSUME_NONNULL_BEGIN TSUnreadIndicatorInteraction *interaction = (TSUnreadIndicatorInteraction *)self.viewItem.interaction; self.titleLabel.text = [self titleForInteraction:interaction]; + self.subtitleLabel.text = [self subtitleForInteraction:interaction]; self.backgroundColor = [UIColor whiteColor]; @@ -90,11 +102,17 @@ NS_ASSUME_NONNULL_BEGIN [self.titleLabel autoPinLeadingToSuperviewMarginWithInset:self.conversationStyle.fullWidthGutterLeading], [self.titleLabel autoPinTrailingToSuperviewMarginWithInset:self.conversationStyle.fullWidthGutterTrailing], - // TODO: offset. - [self.strokeView autoPinEdge:ALEdgeBottom toEdge:ALEdgeTop ofView:self.titleLabel withOffset:0.f], + [self.strokeView autoPinEdge:ALEdgeBottom toEdge:ALEdgeTop ofView:self.titleLabel], [self.strokeView autoPinLeadingToSuperviewMarginWithInset:self.conversationStyle.fullWidthGutterLeading], [self.strokeView autoPinTrailingToSuperviewMarginWithInset:self.conversationStyle.fullWidthGutterTrailing], [self.strokeView autoSetDimension:ALDimensionHeight toSize:1.f], + + [self.subtitleLabel autoPinEdge:ALEdgeTop + toEdge:ALEdgeBottom + ofView:self.titleLabel + withOffset:self.subtitleVSpacing], + [self.subtitleLabel autoPinLeadingToSuperviewMarginWithInset:self.conversationStyle.fullWidthGutterLeading], + [self.subtitleLabel autoPinTrailingToSuperviewMarginWithInset:self.conversationStyle.fullWidthGutterTrailing], ]; } @@ -104,6 +122,23 @@ NS_ASSUME_NONNULL_BEGIN .uppercaseString; } +- (NSString *)subtitleForInteraction:(TSUnreadIndicatorInteraction *)interaction +{ + if (!interaction.hasMoreUnseenMessages) { + return nil; + } + return (interaction.missingUnseenSafetyNumberChangeCount > 0 + ? NSLocalizedString(@"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES", + @"Messages that indicates that there are more unseen messages.") + : NSLocalizedString(@"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_AND_SAFETY_NUMBER_CHANGES", + @"Messages that indicates that there are more unseen messages including safety number changes.")); +} + +- (CGFloat)subtitleVSpacing +{ + return 3.f; +} + - (CGSize)cellSizeWithTransaction:(YapDatabaseReadTransaction *)transaction { OWSAssert(self.conversationStyle); @@ -117,6 +152,15 @@ NS_ASSUME_NONNULL_BEGIN CGSize result = CGSizeMake(self.conversationStyle.fullWidthContentWidth, self.titleLabel.font.lineHeight + vOffset * 2); + TSUnreadIndicatorInteraction *interaction = (TSUnreadIndicatorInteraction *)self.viewItem.interaction; + self.subtitleLabel.text = [self subtitleForInteraction:interaction]; + if (self.subtitleLabel.text.length > 0) { + result.height += self.subtitleVSpacing; + result.height += ceil( + [self.subtitleLabel sizeThatFits:CGSizeMake(self.conversationStyle.fullWidthContentWidth, CGFLOAT_MAX)] + .height); + } + return CGSizeCeil(result); } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 20d1ace60..674cc0466 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1112,9 +1112,6 @@ /* table cell label in conversation settings */ "LIST_GROUP_MEMBERS_ACTION" = "Group Members"; -/* Label for button that loads more messages in conversation view. */ -"load_earlier_messages" = "Load Earlier Messages"; - /* No comment provided by engineer. */ "LOGGING_SECTION" = "Logging"; @@ -1257,13 +1254,13 @@ "MESSAGES_VIEW_TITLE_SUBTITLE" = "Tap here for settings"; /* Indicator that separates read from unread messages. */ -"MESSAGES_VIEW_UNREAD_INDICATOR" = "Unread Messages"; +"MESSAGES_VIEW_UNREAD_INDICATOR" = "New Messages"; -/* 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}}. */ -"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_AND_SAFETY_NUMBER_CHANGES_FORMAT" = "There are more unread messages (including safety number changes) above. Tap \"%@\" to see them."; +/* Messages that indicates that there are more unseen messages. */ +"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES" = "There are more unread messages."; -/* 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}} */ -"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_FORMAT" = "There are more unread messages above. Tap \"%@\" to see them."; +/* Messages that indicates that there are more unseen messages including safety number changes. */ +"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_AND_SAFETY_NUMBER_CHANGES" = "There are more unread messages (including safety number changes)."; /* notification title */ "MISSED_CALL" = "Missed call";