|
|
|
@ -4847,9 +4847,11 @@ typedef enum : NSUInteger {
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
// Update the "shouldShowDate" property of the view items.
|
|
|
|
|
// Update the "break" properties (shouldShowDate and unreadIndicator) of the view items.
|
|
|
|
|
BOOL shouldShowDateOnNextViewItem = YES;
|
|
|
|
|
uint64_t previousViewItemTimestamp = 0;
|
|
|
|
|
OWSUnreadIndicator *_Nullable unreadIndicator = self.dynamicInteractions.unreadIndicator;
|
|
|
|
|
BOOL hasPlacedUnreadIndicator = NO;
|
|
|
|
|
for (ConversationViewItem *viewItem in viewItems) {
|
|
|
|
|
BOOL canShowDate = NO;
|
|
|
|
|
switch (viewItem.interaction.interactionType) {
|
|
|
|
@ -4884,12 +4886,47 @@ typedef enum : NSUInteger {
|
|
|
|
|
viewItem.shouldShowDate = shouldShowDate;
|
|
|
|
|
|
|
|
|
|
previousViewItemTimestamp = viewItemTimestamp;
|
|
|
|
|
|
|
|
|
|
// When a conversation without unread messages receives an incoming message,
|
|
|
|
|
// we call ensureDynamicInteractions to ensure that the unread indicator (etc.)
|
|
|
|
|
// state is updated accordingly. However this is done in a separate transaction.
|
|
|
|
|
// We don't want to show the incoming message _without_ an unread indicator and
|
|
|
|
|
// then immediately re-render it _with_ an unread indicator.
|
|
|
|
|
//
|
|
|
|
|
// To avoid this, we use a temporary instance of OWSUnreadIndicator whenever
|
|
|
|
|
// we find an unread message that _should_ have an unread indicator, but no
|
|
|
|
|
// unread indicator exists yet on dynamicInteractions.
|
|
|
|
|
BOOL isItemUnread = ([viewItem.interaction conformsToProtocol:@protocol(OWSReadTracking)]
|
|
|
|
|
&& !((id<OWSReadTracking>)viewItem.interaction).wasRead);
|
|
|
|
|
if (isItemUnread && !unreadIndicator && !hasPlacedUnreadIndicator) {
|
|
|
|
|
|
|
|
|
|
unreadIndicator =
|
|
|
|
|
[[OWSUnreadIndicator alloc] initUnreadIndicatorWithTimestamp:viewItem.interaction.timestamp
|
|
|
|
|
hasMoreUnseenMessages:NO
|
|
|
|
|
missingUnseenSafetyNumberChangeCount:0
|
|
|
|
|
unreadIndicatorPosition:0
|
|
|
|
|
firstUnseenInteractionTimestamp:viewItem.interaction.timestamp];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Place the unread indicator onto the first appropriate view item,
|
|
|
|
|
// if any.
|
|
|
|
|
if (unreadIndicator && viewItem.interaction.timestampForSorting >= unreadIndicator.timestamp) {
|
|
|
|
|
viewItem.unreadIndicator = unreadIndicator;
|
|
|
|
|
unreadIndicator = nil;
|
|
|
|
|
hasPlacedUnreadIndicator = YES;
|
|
|
|
|
} else {
|
|
|
|
|
viewItem.unreadIndicator = nil;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (unreadIndicator) {
|
|
|
|
|
// This isn't necessarily a bug - all of the interactions after the
|
|
|
|
|
// unread indicator may have disappeared or been deleted.
|
|
|
|
|
DDLogWarn(@"%@ Couldn't find an interaction to hang the unread indicator on.", self.logTag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update the properties of the view items.
|
|
|
|
|
//
|
|
|
|
|
// NOTE: This logic uses shouldShowDate which is set in the previous pass.
|
|
|
|
|
OWSUnreadIndicator *_Nullable unreadIndicator = self.dynamicInteractions.unreadIndicator;
|
|
|
|
|
// NOTE: This logic uses the break properties which are set in the previous pass.
|
|
|
|
|
for (NSUInteger i = 0; i < viewItems.count; i++) {
|
|
|
|
|
ConversationViewItem *viewItem = viewItems[i];
|
|
|
|
|
ConversationViewItem *_Nullable previousViewItem = (i > 0 ? viewItems[i - 1] : nil);
|
|
|
|
@ -4900,15 +4937,6 @@ typedef enum : NSUInteger {
|
|
|
|
|
BOOL isLastInCluster = YES;
|
|
|
|
|
NSAttributedString *_Nullable senderName = nil;
|
|
|
|
|
|
|
|
|
|
// Place the unread indicator onto the first appropriate view item,
|
|
|
|
|
// if any.
|
|
|
|
|
if (unreadIndicator && viewItem.interaction.timestampForSorting >= unreadIndicator.timestamp) {
|
|
|
|
|
viewItem.unreadIndicator = unreadIndicator;
|
|
|
|
|
unreadIndicator = nil;
|
|
|
|
|
} else {
|
|
|
|
|
viewItem.unreadIndicator = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OWSInteractionType interactionType = viewItem.interaction.interactionType;
|
|
|
|
|
NSString *timestampText = [DateUtil formatTimestampShort:viewItem.interaction.timestamp];
|
|
|
|
|
|
|
|
|
@ -4930,14 +4958,14 @@ typedef enum : NSUInteger {
|
|
|
|
|
// ...and always show the "disappearing messages" animation.
|
|
|
|
|
shouldHideFooter
|
|
|
|
|
= ([timestampText isEqualToString:nextTimestampText] && receiptStatus == nextReceiptStatus
|
|
|
|
|
&& outgoingMessage.messageState != TSOutgoingMessageStateFailed && !nextViewItem.shouldShowDate
|
|
|
|
|
&& outgoingMessage.messageState != TSOutgoingMessageStateFailed && !nextViewItem.hasCellHeader
|
|
|
|
|
&& !isDisappearingMessage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// clustering
|
|
|
|
|
if (previousViewItem == nil) {
|
|
|
|
|
isFirstInCluster = YES;
|
|
|
|
|
} else if (viewItem.shouldShowDate) {
|
|
|
|
|
} else if (viewItem.hasCellHeader) {
|
|
|
|
|
isFirstInCluster = YES;
|
|
|
|
|
} else {
|
|
|
|
|
isFirstInCluster = previousViewItem.interaction.interactionType != OWSInteractionType_OutgoingMessage;
|
|
|
|
@ -4945,7 +4973,7 @@ typedef enum : NSUInteger {
|
|
|
|
|
|
|
|
|
|
if (nextViewItem == nil) {
|
|
|
|
|
isLastInCluster = YES;
|
|
|
|
|
} else if (nextViewItem.shouldShowDate) {
|
|
|
|
|
} else if (nextViewItem.hasCellHeader) {
|
|
|
|
|
isLastInCluster = YES;
|
|
|
|
|
} else {
|
|
|
|
|
isLastInCluster = nextViewItem.interaction.interactionType != OWSInteractionType_OutgoingMessage;
|
|
|
|
@ -4969,7 +4997,7 @@ typedef enum : NSUInteger {
|
|
|
|
|
// We can skip the "incoming message status" footer in a cluster if the next message
|
|
|
|
|
// has the same footer and no "date break" separates us.
|
|
|
|
|
// ...but always show the "disappearing messages" animation.
|
|
|
|
|
shouldHideFooter = ([timestampText isEqualToString:nextTimestampText] && !nextViewItem.shouldShowDate &&
|
|
|
|
|
shouldHideFooter = ([timestampText isEqualToString:nextTimestampText] && !nextViewItem.hasCellHeader &&
|
|
|
|
|
[NSObject isNullableObject:nextIncomingSenderId equalTo:incomingSenderId]
|
|
|
|
|
&& !isDisappearingMessage);
|
|
|
|
|
}
|
|
|
|
@ -4977,7 +5005,7 @@ typedef enum : NSUInteger {
|
|
|
|
|
// clustering
|
|
|
|
|
if (previousViewItem == nil) {
|
|
|
|
|
isFirstInCluster = YES;
|
|
|
|
|
} else if (viewItem.shouldShowDate) {
|
|
|
|
|
} else if (viewItem.hasCellHeader) {
|
|
|
|
|
isFirstInCluster = YES;
|
|
|
|
|
} else if (previousViewItem.interaction.interactionType != OWSInteractionType_IncomingMessage) {
|
|
|
|
|
isFirstInCluster = YES;
|
|
|
|
@ -4990,7 +5018,7 @@ typedef enum : NSUInteger {
|
|
|
|
|
isLastInCluster = YES;
|
|
|
|
|
} else if (nextViewItem.interaction.interactionType != OWSInteractionType_IncomingMessage) {
|
|
|
|
|
isLastInCluster = YES;
|
|
|
|
|
} else if (nextViewItem.shouldShowDate) {
|
|
|
|
|
} else if (nextViewItem.hasCellHeader) {
|
|
|
|
|
isLastInCluster = YES;
|
|
|
|
|
} else {
|
|
|
|
|
TSIncomingMessage *nextIncomingMessage = (TSIncomingMessage *)nextViewItem.interaction;
|
|
|
|
@ -5010,7 +5038,7 @@ typedef enum : NSUInteger {
|
|
|
|
|
|
|
|
|
|
shouldShowSenderName
|
|
|
|
|
= (![NSObject isNullableObject:previousIncomingSenderId equalTo:incomingSenderId]
|
|
|
|
|
|| viewItem.shouldShowDate);
|
|
|
|
|
|| viewItem.hasCellHeader);
|
|
|
|
|
}
|
|
|
|
|
if (shouldShowSenderName) {
|
|
|
|
|
senderName = [self.contactsManager
|
|
|
|
@ -5027,7 +5055,7 @@ typedef enum : NSUInteger {
|
|
|
|
|
shouldShowSenderAvatar = YES;
|
|
|
|
|
if (nextViewItem && nextViewItem.interaction.interactionType == interactionType) {
|
|
|
|
|
shouldShowSenderAvatar = (![NSObject isNullableObject:nextIncomingSenderId equalTo:incomingSenderId]
|
|
|
|
|
|| nextViewItem.shouldShowDate);
|
|
|
|
|
|| nextViewItem.hasCellHeader);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -5038,11 +5066,6 @@ typedef enum : NSUInteger {
|
|
|
|
|
viewItem.shouldHideFooter = shouldHideFooter;
|
|
|
|
|
viewItem.senderName = senderName;
|
|
|
|
|
}
|
|
|
|
|
if (unreadIndicator) {
|
|
|
|
|
// This isn't necessarily a bug - all of the interactions after the
|
|
|
|
|
// unread indicator may have disappeared or been deleted.
|
|
|
|
|
DDLogWarn(@"%@ Couldn't find an interaction to hang the unread indicator on.", self.logTag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.viewItems = viewItems;
|
|
|
|
|
self.viewItemCache = viewItemCache;
|
|
|
|
|