/* Copyright 2018-2024 New Vector Ltd. Copyright 2017 Vector Creations Ltd Copyright 2016 OpenMarket Ltd SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ #import "MatrixKit.h" #import "HomeMessagesSearchDataSource.h" #import "RoomBubbleCellData.h" #import "ThemeService.h" #import "GeneratedInterface-Swift.h" #import "MXKRoomBubbleTableViewCell+Riot.h" @implementation HomeMessagesSearchDataSource - (void)destroy { [super destroy]; } - (void)convertHomeserverResultsIntoCells:(MXSearchRoomEventResults *)roomEventResults onComplete:(dispatch_block_t)onComplete { MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mxSession]; dispatch_group_t group = dispatch_group_create(); // Convert the HS results into `RoomViewController` cells for (MXSearchResult *result in roomEventResults.results) { // Retrieve the local room data source thanks to the room identifier // Note: if no local room data source exist the result is ignored. NSString *roomId = result.result.roomId; if (roomId) { dispatch_group_enter(group); // Check whether the user knows this room to create the room data source if it doesn't exist. MXRoom *room = [self.mxSession roomWithRoomId:roomId]; [roomDataSourceManager roomDataSourceForRoom:roomId create:(room != nil) onComplete:^(MXKRoomDataSource *roomDataSource) { if (roomDataSource) { void(^continueBlock)(void) = ^{ // Prepare text font used to highlight the search pattern. UIFont *patternFont = [roomDataSource.eventFormatter bingTextFont]; // Let the `RoomViewController` ecosystem do the job // The search result contains only room message events, no state events. // Thus, passing the current room state is not a huge problem. Only // the user display name and his avatar may be wrong. RoomBubbleCellData *cellData = [[RoomBubbleCellData alloc] initWithEvent:result.result andRoomState:roomDataSource.roomState andRoomDataSource:roomDataSource]; if (cellData) { // Highlight the search pattern [cellData highlightPatternInTextMessage:self.searchText withBackgroundColor:ThemeService.shared.theme.searchResultHighlightColor foregroundColor:ThemeService.shared.theme.textPrimaryColor andFont:patternFont]; // Use profile information as data to display MXSearchUserProfile *userProfile = result.context.profileInfo[result.result.sender]; cellData.senderDisplayName = userProfile.displayName; cellData.senderAvatarUrl = userProfile.avatarUrl; [self->cellDataArray insertObject:cellData atIndex:0]; } dispatch_group_leave(group); }; if (RiotSettings.shared.enableThreads) { if (result.result.isInThread) { continueBlock(); } else if (result.result.unsignedData.relations.thread) { continueBlock(); } else if (room) { [room liveTimeline:^(id liveTimeline) { [liveTimeline paginate:NSUIntegerMax direction:MXTimelineDirectionBackwards onlyFromStore:YES complete:^{ [liveTimeline resetPagination]; continueBlock(); } failure:^(NSError * _Nonnull error) { continueBlock(); }]; }]; } else { continueBlock(); } } else { continueBlock(); } } else { dispatch_group_leave(group); } }]; } } dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // In case of successive messages from the same room, // we use the pagination flag to display the room name only on the first message. NSString *currentRoomId; for (RoomBubbleCellData *cellData in self->cellDataArray) { if (currentRoomId && [currentRoomId isEqualToString:cellData.roomId]) { cellData.isPaginationFirstBubble = NO; } else { cellData.isPaginationFirstBubble = YES; currentRoomId = cellData.roomId; } } onComplete(); }); } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath]; // Finalize cell view customization here if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { MXKRoomBubbleTableViewCell *bubbleCell = (MXKRoomBubbleTableViewCell*)cell; // Display date for each message [bubbleCell addDateLabel]; if (RiotSettings.shared.enableThreads) { RoomBubbleCellData *cellData = (RoomBubbleCellData*)[self cellDataAtIndex:indexPath.row]; MXEvent *event = cellData.events.firstObject; if (event) { if (cellData.hasThreadRoot) { id thread = cellData.bubbleComponents.firstObject.thread; ThreadSummaryView *threadSummaryView = [[ThreadSummaryView alloc] initWithThread:thread session:self.mxSession]; [bubbleCell.tmpSubviews addObject:threadSummaryView]; threadSummaryView.translatesAutoresizingMaskIntoConstraints = NO; [bubbleCell.contentView addSubview:threadSummaryView]; CGFloat leftMargin = PlainRoomCellLayoutConstants.reactionsViewLeftMargin; CGFloat height = [ThreadSummaryView contentViewHeightForThread:thread fitting:cellData.maxTextViewWidth]; CGRect bubbleComponentFrame = [bubbleCell componentFrameInContentViewForIndex:0]; CGFloat bottomPositionY = bubbleComponentFrame.origin.y + bubbleComponentFrame.size.height; // Set constraints for the summary view [NSLayoutConstraint activateConstraints: @[ [threadSummaryView.leadingAnchor constraintEqualToAnchor:threadSummaryView.superview.leadingAnchor constant:leftMargin], [threadSummaryView.topAnchor constraintEqualToAnchor:threadSummaryView.superview.topAnchor constant:bottomPositionY + PlainRoomCellLayoutConstants.threadSummaryViewTopMargin], [threadSummaryView.heightAnchor constraintEqualToConstant:height], [threadSummaryView.trailingAnchor constraintLessThanOrEqualToAnchor:threadSummaryView.superview.trailingAnchor constant:-PlainRoomCellLayoutConstants.reactionsViewRightMargin] ]]; } else if (event.isInThread) { FromAThreadView *fromAThreadView = [FromAThreadView instantiate]; [bubbleCell.tmpSubviews addObject:fromAThreadView]; fromAThreadView.translatesAutoresizingMaskIntoConstraints = NO; [bubbleCell.contentView addSubview:fromAThreadView]; CGFloat leftMargin = PlainRoomCellLayoutConstants.reactionsViewLeftMargin; CGFloat height = [FromAThreadView contentViewHeightForEvent:event fitting:cellData.maxTextViewWidth]; CGRect bubbleComponentFrame = [bubbleCell componentFrameInContentViewForIndex:0]; CGFloat bottomPositionY = bubbleComponentFrame.origin.y + bubbleComponentFrame.size.height; // Set constraints for the summary view [NSLayoutConstraint activateConstraints: @[ [fromAThreadView.leadingAnchor constraintEqualToAnchor:fromAThreadView.superview.leadingAnchor constant:leftMargin], [fromAThreadView.topAnchor constraintEqualToAnchor:fromAThreadView.superview.topAnchor constant:bottomPositionY + PlainRoomCellLayoutConstants.fromAThreadViewTopMargin], [fromAThreadView.heightAnchor constraintEqualToConstant:height], [fromAThreadView.trailingAnchor constraintLessThanOrEqualToAnchor:fromAThreadView.superview.trailingAnchor constant:-PlainRoomCellLayoutConstants.reactionsViewRightMargin] ]]; } } } } return cell; } @end