diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 5c51a1466..0ee0209c3 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -101,7 +101,7 @@ 34D1F0B71F87F8850066283D /* OWSGenericAttachmentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0B61F87F8850066283D /* OWSGenericAttachmentView.m */; }; 34D1F0BA1F8800D90066283D /* OWSAudioMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0B91F8800D90066283D /* OWSAudioMessageView.m */; }; 34D1F0BD1F8D108C0066283D /* AttachmentUploadView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0BC1F8D108C0066283D /* AttachmentUploadView.m */; }; - 34D1F0C01F8EC1760066283D /* MessageRecipientState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0BF1F8EC1760066283D /* MessageRecipientState.swift */; }; + 34D1F0C01F8EC1760066283D /* MessageRecipientStatusUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0BF1F8EC1760066283D /* MessageRecipientStatusUtils.swift */; }; 34D5CC961EA6AFAD005515DB /* OWSContactsSyncing.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CC951EA6AFAD005515DB /* OWSContactsSyncing.m */; }; 34D5CCA91EAE3D30005515DB /* AvatarViewHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CCA81EAE3D30005515DB /* AvatarViewHelper.m */; }; 34D5CCB11EAE7E7F005515DB /* SelectRecipientViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CCB01EAE7E7F005515DB /* SelectRecipientViewController.m */; }; @@ -569,7 +569,7 @@ 34D1F0B91F8800D90066283D /* OWSAudioMessageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSAudioMessageView.m; sourceTree = ""; }; 34D1F0BB1F8D108C0066283D /* AttachmentUploadView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AttachmentUploadView.h; sourceTree = ""; }; 34D1F0BC1F8D108C0066283D /* AttachmentUploadView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AttachmentUploadView.m; sourceTree = ""; }; - 34D1F0BF1F8EC1760066283D /* MessageRecipientState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageRecipientState.swift; sourceTree = ""; }; + 34D1F0BF1F8EC1760066283D /* MessageRecipientStatusUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageRecipientStatusUtils.swift; sourceTree = ""; }; 34D5CC941EA6AFAD005515DB /* OWSContactsSyncing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactsSyncing.h; sourceTree = ""; }; 34D5CC951EA6AFAD005515DB /* OWSContactsSyncing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSyncing.m; sourceTree = ""; }; 34D5CCA71EAE3D30005515DB /* AvatarViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvatarViewHelper.h; sourceTree = ""; }; @@ -1182,7 +1182,7 @@ 34D1F0BE1F8EC1760066283D /* Utils */ = { isa = PBXGroup; children = ( - 34D1F0BF1F8EC1760066283D /* MessageRecipientState.swift */, + 34D1F0BF1F8EC1760066283D /* MessageRecipientStatusUtils.swift */, ); path = Utils; sourceTree = ""; @@ -2346,7 +2346,7 @@ 34BECE2E1F7ABCE000D7438D /* GifPickerViewController.swift in Sources */, 34B3F8811E8DF1700035BE1A /* LockInteractionController.m in Sources */, 34CCAF3B1F0C2748004084F4 /* OWSAddToContactViewController.m in Sources */, - 34D1F0C01F8EC1760066283D /* MessageRecipientState.swift in Sources */, + 34D1F0C01F8EC1760066283D /* MessageRecipientStatusUtils.swift in Sources */, 45F659731E1BD99C00444429 /* CallKitCallUIAdaptee.swift in Sources */, 45BB93381E688E14001E3939 /* UIDevice+featureSupport.swift in Sources */, 458DE9D61DEE3FD00071BB03 /* PeerConnectionClient.swift in Sources */, diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index ea80b765e..6c0ece618 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -276,7 +276,7 @@ NS_ASSUME_NONNULL_BEGIN if (hasExpirationTimer) { showFooter = YES; } else if (!self.isIncoming) { - showFooter = YES; + showFooter = !self.viewItem.shouldHideRecipientStatus; } else if (self.viewItem.isGroupThread) { showFooter = YES; } else { @@ -297,10 +297,12 @@ NS_ASSUME_NONNULL_BEGIN BOOL hasExpirationTimer = message.shouldStartExpireTimer; NSAttributedString *attributedText = nil; if (!self.isIncoming) { - TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)message; - NSString *statusMessage = - [MessageRecipientStatusUtils statusMessageWithOutgoingMessage:outgoingMessage referenceView:self]; - attributedText = [[NSAttributedString alloc] initWithString:statusMessage attributes:@{}]; + if (!self.viewItem.shouldHideRecipientStatus || hasExpirationTimer) { + TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)message; + NSString *statusMessage = + [MessageRecipientStatusUtils statusMessageWithOutgoingMessage:outgoingMessage referenceView:self]; + attributedText = [[NSAttributedString alloc] initWithString:statusMessage attributes:@{}]; + } } else if (self.viewItem.isGroupThread) { TSIncomingMessage *incomingMessage = (TSIncomingMessage *)self.viewItem.interaction; attributedText = [self.delegate attributedContactOrProfileNameForPhoneIdentifier:incomingMessage.authorId]; @@ -633,6 +635,9 @@ NS_ASSUME_NONNULL_BEGIN cellSize.height += self.dateHeaderHeight; cellSize.height += self.footerHeight; + cellSize.width = ceil(cellSize.width); + cellSize.height = ceil(cellSize.height); + return cellSize; } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m index b7b4acc2c..3576b0e86 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m @@ -1138,6 +1138,18 @@ NS_ASSUME_NONNULL_BEGIN [textView resignFirstResponder]; } +- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text +{ + if (range.length > 0) { + return YES; + } + if ([text isEqualToString:@"\n"]) { + [self sendButtonPressed]; + return NO; + } + return YES; +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index bb6f1063c..cd371e089 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -3885,67 +3885,42 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { } shouldShowDateOnNextViewItem = NO; } - if (viewItem.shouldShowDate != shouldShowDate) { - // If this is an existing view item and it has changed size, - // note that so that we can reload this cell while doing - // incremental updates. - if (viewItem.lastRow != NSNotFound) { - [rowsThatChangedSize addObject:@(viewItem.lastRow)]; - } + + // If this is an existing view item and it has changed size, + // note that so that we can reload this cell while doing + // incremental updates. + if (viewItem.shouldShowDate != shouldShowDate && viewItem.lastRow != NSNotFound) { + [rowsThatChangedSize addObject:@(viewItem.lastRow)]; } viewItem.shouldShowDate = shouldShowDate; + previousViewItemTimestamp = viewItem.interaction.timestampForSorting; } // Update the "shouldShowDate" property of the view items. OWSInteractionType lastInteractionType = OWSInteractionType_Unknown; - for (ConversationViewItem *viewItem in viewItems) { + MessageRecipientStatus lastRecipientStatus = MessageRecipientStatusUploading; + for (ConversationViewItem *viewItem in viewItems.reverseObjectEnumerator) { + BOOL shouldHideRecipientStatus = NO; OWSInteractionType interactionType = viewItem.interaction.interactionType; + + if (interactionType == OWSInteractionType_OutgoingMessage) { + TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)viewItem.interaction; + MessageRecipientStatus recipientStatus = + [MessageRecipientStatusUtils recipientStatusWithOutgoingMessage:outgoingMessage]; + shouldHideRecipientStatus + = (interactionType == lastInteractionType && recipientStatus == lastRecipientStatus); + lastRecipientStatus = recipientStatus; + } lastInteractionType = interactionType; - // BOOL canShowDate = NO; - // switch (viewItem.interaction.interactionType) { - // case OWSInteractionType_Unknown: - // case OWSInteractionType_UnreadIndicator: - // case OWSInteractionType_Offer: - // canShowDate = NO; - // break; - // case OWSInteractionType_IncomingMessage: - // case OWSInteractionType_OutgoingMessage: - // case OWSInteractionType_Error: - // case OWSInteractionType_Info: - // case OWSInteractionType_Call: - // canShowDate = YES; - // break; - // } - // - // BOOL shouldShowDate = NO; - // if (!canShowDate) { - // shouldShowDate = NO; - // shouldShowDateOnNextViewItem = YES; - // } else if (shouldShowDateOnNextViewItem) { - // shouldShowDate = YES; - // shouldShowDateOnNextViewItem = NO; - // } else { - // uint64_t viewItemTimestamp = viewItem.interaction.timestampForSorting; - // OWSAssert(viewItemTimestamp > 0); - // OWSAssert(previousViewItemTimestamp > 0); - // uint64_t timeDifferenceMs = viewItemTimestamp - previousViewItemTimestamp; - // static const uint64_t kShowTimeIntervalMs = 5 * kMinuteInMs; - // if (timeDifferenceMs > kShowTimeIntervalMs) { - // shouldShowDate = YES; - // } - // shouldShowDateOnNextViewItem = NO; - // } - // if (viewItem.shouldShowDate != shouldShowDate) { - // // If this is an existing view item and it has changed size, - // // note that so that we can reload this cell while doing - // // incremental updates. - // if (viewItem.lastRow != NSNotFound) { - // [rowsThatChangedSize addObject:@(viewItem.lastRow)]; - // } - // } - // viewItem.shouldShowDate = shouldShowDate; - // previousViewItemTimestamp = viewItem.interaction.timestampForSorting; + + // If this is an existing view item and it has changed size, + // note that so that we can reload this cell while doing + // incremental updates. + if (viewItem.shouldHideRecipientStatus != shouldHideRecipientStatus && viewItem.lastRow != NSNotFound) { + [rowsThatChangedSize addObject:@(viewItem.lastRow)]; + } + viewItem.shouldHideRecipientStatus = shouldHideRecipientStatus; } self.viewItems = viewItems; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index 780b4beb2..c8456faac 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -42,6 +42,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @property (nonatomic, readonly) BOOL isGroupThread; @property (nonatomic) BOOL shouldShowDate; +@property (nonatomic) BOOL shouldHideRecipientStatus; @property (nonatomic) NSInteger row; @property (nonatomic) NSInteger lastRow; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index b517b843f..0fab7251d 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -104,6 +104,17 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) [self clearCachedLayoutState]; } +- (void)setShouldHideRecipientStatus:(BOOL)shouldHideRecipientStatus +{ + if (_shouldHideRecipientStatus == shouldHideRecipientStatus) { + return; + } + + _shouldHideRecipientStatus = shouldHideRecipientStatus; + + [self clearCachedLayoutState]; +} + - (void)clearCachedLayoutState { self.cachedCellSize = nil; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewLayout.m b/Signal/src/ViewControllers/ConversationView/ConversationViewLayout.m index 7162ea378..e819280bf 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewLayout.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewLayout.m @@ -82,7 +82,7 @@ NS_ASSUME_NONNULL_BEGIN // TODO: Remove this log statement after we've reduced the invalidation churn. DDLogVerbose(@"%@ prepareLayout", self.tag); - const int vInset = 15; + const int vInset = 8; const int hInset = 10; const int vSpacing = 5; const int viewWidth = (int)floor(self.collectionView.bounds.size.width); diff --git a/Signal/src/ViewControllers/Utils/MessageRecipientState.swift b/Signal/src/ViewControllers/Utils/MessageRecipientStatusUtils.swift similarity index 86% rename from Signal/src/ViewControllers/Utils/MessageRecipientState.swift rename to Signal/src/ViewControllers/Utils/MessageRecipientStatusUtils.swift index 286c7570f..7bc1a105c 100644 --- a/Signal/src/ViewControllers/Utils/MessageRecipientState.swift +++ b/Signal/src/ViewControllers/Utils/MessageRecipientStatusUtils.swift @@ -4,7 +4,7 @@ import Foundation -enum MessageRecipientStatus { +@objc enum MessageRecipientStatus: Int { case uploading case sending case sent @@ -132,4 +132,33 @@ class MessageRecipientStatusUtils: NSObject { comment:"message status while message is sending.") } } + + public class func recipientStatus(outgoingMessage: TSOutgoingMessage) -> MessageRecipientStatus { + let recipientReadMap = outgoingMessage.recipientReadMap + if recipientReadMap.count > 0 { + assert(outgoingMessage.messageState == .sentToService) + return .read + } + + let recipientDeliveryMap = outgoingMessage.recipientDeliveryMap + if recipientDeliveryMap.count > 0 { + return .delivered + } + + if outgoingMessage.wasDelivered { + return .delivered + } + + if outgoingMessage.messageState == .unsent { + return .failed + } else if outgoingMessage.messageState == .sentToService { + return .sent + } else if outgoingMessage.hasAttachments() { + assert(outgoingMessage.messageState == .attemptingOut) + return .uploading + } else { + assert(outgoingMessage.messageState == .attemptingOut) + return .sending + } + } }