Read markers should be synchronised across clients

The read marker may be defined on an event which is not displayed in the room history.
The MatrixKit has been updated to handle this kind of event in the MXKRoomBubbleCellData object.
Previously these events were ignored during the bubbles rendering. Now a bubble component is created even if the event has no actual display.
This component is useful to display the read marker in the room history, and to allow the user to jump on this read marker.

vector-im/riot-meta#8
This commit is contained in:
Giom Foret
2017-06-13 17:43:32 +02:00
parent 3c66e60d32
commit 7459f529e6
8 changed files with 327 additions and 196 deletions
+90 -75
View File
@@ -41,14 +41,15 @@
self.eventFormatter.treatMatrixUserIdAsLink = YES;
self.eventFormatter.treatMatrixRoomIdAsLink = YES;
self.eventFormatter.treatMatrixRoomAliasAsLink = YES;
// Apply the event types filter to display only the wanted event types.
self.eventFormatter.eventTypesFilterForMessages = [MXKAppSettings standardAppSettings].eventsFilterForMessages;
// Handle timestamp and read receips display at Vector app level (see [tableView: cellForRowAtIndexPath:])
self.useCustomDateTimeLabel = YES;
self.useCustomReceipts = YES;
self.useCustomUnsentButton = YES;
// TODO custom here self.eventsFilterForMessages according to Vector requirements
// Set bubble pagination
self.bubblesPagination = MXKRoomDataSourceBubblesPaginationPerDay;
@@ -72,7 +73,8 @@
for (NSString* eventId in readEventIds)
{
RoomBubbleCellData *cellData = [self cellDataOfEventWithEventId:eventId];
cellData.hasReadReceipts = YES;
// Ignore the read receipts on the events without an actual display.
cellData.hasReadReceipts = (cellData.attributedTextMessage != nil);
}
[super didReceiveReceiptEvent:receiptEvent roomState:roomState];
@@ -95,9 +97,17 @@
cellData.containsLastMessage = NO;
}
// The cell containing the last message is the last one
RoomBubbleCellData *cellData = bubbles.lastObject;
cellData.containsLastMessage = YES;
// The cell containing the last message is the last one with an actual display.
NSInteger index = bubbles.count;
while (index--)
{
RoomBubbleCellData *cellData = bubbles[index];
if (cellData.attributedTextMessage)
{
cellData.containsLastMessage = YES;
break;
}
}
}
}
@@ -122,6 +132,7 @@
}
// Handle read receipts and read marker display.
// Ignore the read receipts on the bubble without actual display.
if ((self.showBubbleReceipts && cellData.hasReadReceipts) || self.showReadMarker)
{
// Read receipts container are inserted here on the right side into the overlay container.
@@ -139,82 +150,86 @@
if (component.event.sentState != MXEventSentStateFailed)
{
// Get the events receipts by ignoring the current user receipt.
NSArray* receipts = [self.room getEventReceipts:component.event.eventId sorted:YES];
NSMutableArray *roomMembers;
NSMutableArray *placeholders;
// Check whether some receipts are found
if (receipts.count)
// Handle read receipts (if any)
if (self.showBubbleReceipts && cellData.hasReadReceipts)
{
// Retrieve the corresponding room members
roomMembers = [[NSMutableArray alloc] initWithCapacity:receipts.count];
placeholders = [[NSMutableArray alloc] initWithCapacity:receipts.count];
// Get the events receipts by ignoring the current user receipt.
NSArray* receipts = [self.room getEventReceipts:component.event.eventId sorted:YES];
NSMutableArray *roomMembers;
NSMutableArray *placeholders;
for (MXReceiptData* data in receipts)
// Check whether some receipts are found
if (receipts.count)
{
MXRoomMember * roomMember = [self.room.state memberWithUserId:data.userId];
if (roomMember)
// Retrieve the corresponding room members
roomMembers = [[NSMutableArray alloc] initWithCapacity:receipts.count];
placeholders = [[NSMutableArray alloc] initWithCapacity:receipts.count];
for (MXReceiptData* data in receipts)
{
[roomMembers addObject:roomMember];
[placeholders addObject:[AvatarGenerator generateAvatarForMatrixItem:roomMember.userId withDisplayName:roomMember.displayname]];
MXRoomMember * roomMember = [self.room.state memberWithUserId:data.userId];
if (roomMember)
{
[roomMembers addObject:roomMember];
[placeholders addObject:[AvatarGenerator generateAvatarForMatrixItem:roomMember.userId withDisplayName:roomMember.displayname]];
}
}
}
}
// Check whether some receipts are found
if (roomMembers.count)
{
// Define the read receipts container, positioned on the right border of the bubble cell (Note the right margin 6 pts).
MXKReceiptSendersContainer* avatarsContainer = [[MXKReceiptSendersContainer alloc] initWithFrame:CGRectMake(bubbleCell.frame.size.width - 156, bottomPositionY - 13, 150, 12) andRestClient:self.mxSession.matrixRestClient];
// Custom avatar display
avatarsContainer.maxDisplayedAvatars = 5;
avatarsContainer.avatarMargin = 6;
// Set the container tag to be able to retrieve read receipts container from component index (see component selection in MXKRoomBubbleTableViewCell (Vector) category).
avatarsContainer.tag = index;
[avatarsContainer refreshReceiptSenders:roomMembers withPlaceHolders:placeholders andAlignment:ReadReceiptAlignmentRight];
avatarsContainer.translatesAutoresizingMaskIntoConstraints = NO;
avatarsContainer.accessibilityIdentifier = @"readReceiptsContainer";
[bubbleCell.bubbleOverlayContainer addSubview:avatarsContainer];
// Force receipts container size
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:150];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:12];
// Force receipts container position
NSLayoutConstraint *trailingConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:bubbleCell.bubbleOverlayContainer
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:-6];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:bubbleCell.bubbleOverlayContainer
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:bottomPositionY - 13];
// Available on iOS 8 and later
[NSLayoutConstraint activateConstraints:@[widthConstraint, heightConstraint, topConstraint, trailingConstraint]];
// Check whether some receipts are found
if (roomMembers.count)
{
// Define the read receipts container, positioned on the right border of the bubble cell (Note the right margin 6 pts).
MXKReceiptSendersContainer* avatarsContainer = [[MXKReceiptSendersContainer alloc] initWithFrame:CGRectMake(bubbleCell.frame.size.width - 156, bottomPositionY - 13, 150, 12) andRestClient:self.mxSession.matrixRestClient];
// Custom avatar display
avatarsContainer.maxDisplayedAvatars = 5;
avatarsContainer.avatarMargin = 6;
// Set the container tag to be able to retrieve read receipts container from component index (see component selection in MXKRoomBubbleTableViewCell (Vector) category).
avatarsContainer.tag = index;
[avatarsContainer refreshReceiptSenders:roomMembers withPlaceHolders:placeholders andAlignment:ReadReceiptAlignmentRight];
avatarsContainer.translatesAutoresizingMaskIntoConstraints = NO;
avatarsContainer.accessibilityIdentifier = @"readReceiptsContainer";
[bubbleCell.bubbleOverlayContainer addSubview:avatarsContainer];
// Force receipts container size
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:150];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:12];
// Force receipts container position
NSLayoutConstraint *trailingConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:bubbleCell.bubbleOverlayContainer
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:-6];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:bubbleCell.bubbleOverlayContainer
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:bottomPositionY - 13];
// Available on iOS 8 and later
[NSLayoutConstraint activateConstraints:@[widthConstraint, heightConstraint, topConstraint, trailingConstraint]];
}
}
// Check whether the read marker must be displayed here.