2015-08-21 18:00:39 +00:00
|
|
|
/*
|
|
|
|
Copyright 2015 OpenMarket Ltd
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#import "RoomDataSource.h"
|
|
|
|
|
|
|
|
#import "EventFormatter.h"
|
2015-08-25 09:19:49 +00:00
|
|
|
#import "RoomBubbleCellData.h"
|
2015-08-21 18:00:39 +00:00
|
|
|
|
2015-12-11 13:01:56 +00:00
|
|
|
#import "MXKRoomBubbleTableViewCell+Vector.h"
|
2015-12-15 16:45:16 +00:00
|
|
|
#import "AvatarGenerator.h"
|
2015-12-11 13:01:56 +00:00
|
|
|
|
2015-08-21 18:00:39 +00:00
|
|
|
@implementation RoomDataSource
|
|
|
|
|
|
|
|
- (instancetype)initWithRoomId:(NSString *)roomId andMatrixSession:(MXSession *)matrixSession
|
|
|
|
{
|
|
|
|
self = [super initWithRoomId:roomId andMatrixSession:matrixSession];
|
|
|
|
if (self)
|
|
|
|
{
|
2015-08-25 09:19:49 +00:00
|
|
|
// Replace default Cell data class
|
|
|
|
[self registerCellDataClass:RoomBubbleCellData.class forCellIdentifier:kMXKRoomBubbleCellDataIdentifier];
|
2015-08-21 18:00:39 +00:00
|
|
|
|
|
|
|
// Replace event formatter
|
|
|
|
self.eventFormatter = [[EventFormatter alloc] initWithMatrixSession:self.mxSession];
|
2016-08-26 14:00:32 +00:00
|
|
|
self.eventFormatter.treatMatrixUserIdAsLink = YES;
|
|
|
|
self.eventFormatter.treatMatrixRoomIdAsLink = YES;
|
|
|
|
self.eventFormatter.treatMatrixRoomAliasAsLink = YES;
|
|
|
|
|
2015-12-11 13:01:56 +00:00
|
|
|
// Handle timestamp and read receips display at Vector app level (see [tableView: cellForRowAtIndexPath:])
|
2015-10-14 14:29:31 +00:00
|
|
|
self.useCustomDateTimeLabel = YES;
|
2015-12-15 16:45:16 +00:00
|
|
|
self.useCustomReceipts = YES;
|
2016-01-25 10:34:23 +00:00
|
|
|
self.useCustomUnsentButton = YES;
|
2015-10-14 14:29:31 +00:00
|
|
|
|
2015-08-21 18:00:39 +00:00
|
|
|
// TODO custom here self.eventsFilterForMessages according to Vector requirements
|
|
|
|
|
|
|
|
// Set bubble pagination
|
|
|
|
self.bubblesPagination = MXKRoomDataSourceBubblesPaginationPerDay;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2015-12-15 16:45:16 +00:00
|
|
|
- (void)didReceiveReceiptEvent:(MXEvent *)receiptEvent roomState:(MXRoomState *)roomState
|
|
|
|
{
|
|
|
|
// Override this callback to force rendering of each cell with read receipts information.
|
|
|
|
@synchronized(bubbles)
|
|
|
|
{
|
|
|
|
for (RoomBubbleCellData *cellData in bubbles)
|
|
|
|
{
|
2016-03-09 17:29:39 +00:00
|
|
|
cellData.hasReadReceipts = NO;
|
2015-12-15 16:45:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NSArray *readEventIds = receiptEvent.readReceiptEventIds;
|
|
|
|
for (NSString* eventId in readEventIds)
|
|
|
|
{
|
2016-03-09 17:29:39 +00:00
|
|
|
RoomBubbleCellData *cellData = [self cellDataOfEventWithEventId:eventId];
|
|
|
|
cellData.hasReadReceipts = YES;
|
2015-12-15 16:45:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Let super handle this receipt
|
|
|
|
[super didReceiveReceiptEvent:receiptEvent roomState:roomState];
|
|
|
|
}
|
|
|
|
|
2016-03-09 17:29:39 +00:00
|
|
|
#pragma mark -
|
|
|
|
|
2016-03-03 16:39:10 +00:00
|
|
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
|
|
|
{
|
|
|
|
NSInteger count = [super tableView:tableView numberOfRowsInSection:section];
|
|
|
|
|
|
|
|
if (count)
|
|
|
|
{
|
2016-03-09 17:29:39 +00:00
|
|
|
// Look for the cell data which contains the last message.
|
2016-03-03 16:39:10 +00:00
|
|
|
|
2016-03-09 17:29:39 +00:00
|
|
|
// Reset first the flag in each cell data
|
|
|
|
@synchronized(bubbles)
|
2016-03-03 16:39:10 +00:00
|
|
|
{
|
2016-03-09 17:29:39 +00:00
|
|
|
for (RoomBubbleCellData *cellData in bubbles)
|
2016-03-03 16:39:10 +00:00
|
|
|
{
|
2016-03-09 17:29:39 +00:00
|
|
|
cellData.containsLastMessage = NO;
|
2016-03-03 16:39:10 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-09 17:29:39 +00:00
|
|
|
|
|
|
|
// Set the flag in the right cell data
|
|
|
|
MXEvent *lastMessage = self.lastMessage;
|
|
|
|
if (lastMessage.eventId)
|
|
|
|
{
|
|
|
|
RoomBubbleCellData *cellData = [self cellDataOfEventWithEventId:lastMessage.eventId];
|
|
|
|
cellData.containsLastMessage = YES;
|
|
|
|
}
|
2016-03-03 16:39:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2015-12-11 13:01:56 +00:00
|
|
|
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
|
|
{
|
|
|
|
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
|
|
|
|
|
|
|
|
// Finalize cell view customization here
|
|
|
|
if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class])
|
|
|
|
{
|
|
|
|
MXKRoomBubbleTableViewCell *bubbleCell = (MXKRoomBubbleTableViewCell*)cell;
|
2015-12-15 16:45:16 +00:00
|
|
|
RoomBubbleCellData *cellData = (RoomBubbleCellData*)bubbleCell.bubbleData;
|
|
|
|
|
2016-03-09 17:29:39 +00:00
|
|
|
// Display timestamp of the last message
|
2016-03-03 16:39:10 +00:00
|
|
|
if (cellData.containsLastMessage)
|
2015-12-11 13:01:56 +00:00
|
|
|
{
|
2016-03-09 17:29:39 +00:00
|
|
|
[bubbleCell addTimestampLabelForComponent:cellData.mostRecentComponentIndex];
|
2015-12-15 16:45:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Handle read receipts display.
|
|
|
|
if (cellData.hasReadReceipts && self.showBubbleReceipts)
|
|
|
|
{
|
|
|
|
// Read receipts container are inserted here on the right side into the overlay container.
|
|
|
|
// Some vertical whitespaces are added in message text view (see RoomBubbleCellData class) to insert correctly multiple receipts.
|
|
|
|
bubbleCell.bubbleOverlayContainer.backgroundColor = [UIColor clearColor];
|
|
|
|
bubbleCell.bubbleOverlayContainer.alpha = 1;
|
|
|
|
bubbleCell.bubbleOverlayContainer.userInteractionEnabled = NO;
|
|
|
|
bubbleCell.bubbleOverlayContainer.hidden = NO;
|
|
|
|
|
|
|
|
NSInteger index = cellData.bubbleComponents.count;
|
|
|
|
CGFloat bottomPositionY = bubbleCell.frame.size.height;
|
|
|
|
while (index--)
|
|
|
|
{
|
|
|
|
MXKRoomBubbleComponent *component = cellData.bubbleComponents[index];
|
|
|
|
|
|
|
|
if (component.event.mxkState != MXKEventStateSendingFailed)
|
|
|
|
{
|
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
// Retrieve the corresponding room members
|
|
|
|
roomMembers = [[NSMutableArray alloc] initWithCapacity:receipts.count];
|
|
|
|
placeholders = [[NSMutableArray alloc] initWithCapacity:receipts.count];
|
|
|
|
|
|
|
|
for (MXReceiptData* data in receipts)
|
|
|
|
{
|
|
|
|
MXRoomMember * roomMember = [self.room.state memberWithUserId:data.userId];
|
|
|
|
if (roomMember)
|
|
|
|
{
|
|
|
|
[roomMembers addObject:roomMember];
|
2016-09-09 13:11:12 +00:00
|
|
|
[placeholders addObject:[AvatarGenerator generateAvatarForMatrixItem:roomMember.userId withDisplayName:roomMember.displayname]];
|
2015-12-15 16:45:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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).
|
2016-01-14 09:05:38 +00:00
|
|
|
MXKReceiptSendersContainer* avatarsContainer = [[MXKReceiptSendersContainer alloc] initWithFrame:CGRectMake(bubbleCell.frame.size.width - 156, bottomPositionY - 13, 150, 12) andRestClient:self.mxSession.matrixRestClient];
|
2015-12-15 16:45:16 +00:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
[bubbleCell.bubbleOverlayContainer addSubview:avatarsContainer];
|
|
|
|
|
|
|
|
// Force receipts container size
|
|
|
|
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
|
2015-12-18 14:20:57 +00:00
|
|
|
attribute:NSLayoutAttributeWidth
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
toItem:nil
|
|
|
|
attribute:NSLayoutAttributeNotAnAttribute
|
|
|
|
multiplier:1.0
|
|
|
|
constant:150];
|
2015-12-15 16:45:16 +00:00
|
|
|
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
|
|
|
|
attribute:NSLayoutAttributeHeight
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
toItem:nil
|
|
|
|
attribute:NSLayoutAttributeNotAnAttribute
|
|
|
|
multiplier:1.0
|
|
|
|
constant:12];
|
|
|
|
|
|
|
|
// Force receipts container position
|
2015-12-18 14:20:57 +00:00
|
|
|
NSLayoutConstraint *trailingConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
|
|
|
|
attribute:NSLayoutAttributeTrailing
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
toItem:bubbleCell.bubbleOverlayContainer
|
|
|
|
attribute:NSLayoutAttributeTrailing
|
|
|
|
multiplier:1.0
|
|
|
|
constant:-6];
|
2015-12-15 16:45:16 +00:00
|
|
|
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer
|
|
|
|
attribute:NSLayoutAttributeTop
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
toItem:bubbleCell.bubbleOverlayContainer
|
|
|
|
attribute:NSLayoutAttributeTop
|
|
|
|
multiplier:1.0
|
2016-01-14 09:05:38 +00:00
|
|
|
constant:bottomPositionY - 13];
|
2015-12-15 16:45:16 +00:00
|
|
|
|
2015-12-18 14:20:57 +00:00
|
|
|
// Available on iOS 8 and later
|
|
|
|
[NSLayoutConstraint activateConstraints:@[widthConstraint, heightConstraint, topConstraint, trailingConstraint]];
|
2015-12-15 16:45:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prepare the bottom position for the next read receipt container (if any)
|
|
|
|
bottomPositionY = bubbleCell.msgTextViewTopConstraint.constant + component.position.y;
|
2015-12-11 13:01:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check whether an event is currently selected: the other messages are then blurred
|
|
|
|
if (_selectedEventId)
|
|
|
|
{
|
2016-03-09 17:29:39 +00:00
|
|
|
// Check whether the selected event belongs to this bubble
|
|
|
|
NSInteger selectedComponentIndex = cellData.selectedComponentIndex;
|
|
|
|
if (selectedComponentIndex != NSNotFound)
|
2015-12-11 13:01:56 +00:00
|
|
|
{
|
2016-03-09 17:29:39 +00:00
|
|
|
[bubbleCell selectComponent:cellData.selectedComponentIndex];
|
2015-12-11 13:01:56 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-03-09 17:29:39 +00:00
|
|
|
bubbleCell.blurred = YES;
|
2015-12-11 13:01:56 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-21 16:18:10 +00:00
|
|
|
|
2016-06-22 14:24:50 +00:00
|
|
|
// Reset the marker if any
|
|
|
|
if (bubbleCell.markerView)
|
|
|
|
{
|
|
|
|
[bubbleCell.markerView removeFromSuperview];
|
|
|
|
}
|
|
|
|
|
2016-06-21 16:18:10 +00:00
|
|
|
// Manage initial event (case of permalink or search result)
|
|
|
|
if (self.timeline.initialEventId)
|
|
|
|
{
|
|
|
|
// Check if the cell contains this initial event
|
|
|
|
for (NSUInteger index = 0; index < cellData.bubbleComponents.count; index++)
|
|
|
|
{
|
|
|
|
MXKRoomBubbleComponent *component = cellData.bubbleComponents[index];
|
|
|
|
|
|
|
|
if ([component.event.eventId isEqualToString:self.timeline.initialEventId])
|
|
|
|
{
|
|
|
|
// If yes, mark the event
|
|
|
|
[bubbleCell markComponent:index];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-12-11 13:01:56 +00:00
|
|
|
}
|
2016-06-21 16:18:10 +00:00
|
|
|
|
2015-12-11 13:01:56 +00:00
|
|
|
return cell;
|
|
|
|
}
|
|
|
|
|
2016-03-09 17:29:39 +00:00
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
- (void)setSelectedEventId:(NSString *)selectedEventId
|
|
|
|
{
|
|
|
|
// Cancel the current selection (if any)
|
|
|
|
if (_selectedEventId)
|
|
|
|
{
|
|
|
|
RoomBubbleCellData *cellData = [self cellDataOfEventWithEventId:_selectedEventId];
|
|
|
|
cellData.selectedEventId = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectedEventId.length)
|
|
|
|
{
|
|
|
|
RoomBubbleCellData *cellData = [self cellDataOfEventWithEventId:selectedEventId];
|
|
|
|
cellData.selectedEventId = selectedEventId;
|
|
|
|
}
|
|
|
|
|
|
|
|
_selectedEventId = selectedEventId;
|
|
|
|
}
|
|
|
|
|
2015-08-21 18:00:39 +00:00
|
|
|
@end
|