diff --git a/Riot/Modules/MatrixKit/Models/RoomList/MXKSessionRecentsDataSource.m b/Riot/Modules/MatrixKit/Models/RoomList/MXKSessionRecentsDataSource.m index f0f84f5b5..afee08a82 100644 --- a/Riot/Modules/MatrixKit/Models/RoomList/MXKSessionRecentsDataSource.m +++ b/Riot/Modules/MatrixKit/Models/RoomList/MXKSessionRecentsDataSource.m @@ -25,52 +25,6 @@ #pragma mark - Constant definitions NSString *const kMXKRecentCellIdentifier = @"kMXKRecentCellIdentifier"; -static NSTimeInterval const roomSummaryChangeThrottlerDelay = .5; - - -@interface MXKSessionRecentsDataSource () -{ - MXKRoomDataSourceManager *roomDataSourceManager; - - /** - Internal array used to regulate change notifications. - Cell data changes are stored instantly in this array. - These changes are reported to the delegate only if no server sync is in progress. - */ - NSMutableArray *internalCellDataArray; - - /** - Store the current search patterns list. - */ - NSArray* searchPatternsList; - - /** - Do not react on every summary change - */ - MXThrottler *roomSummaryChangeThrottler; - - /** - Last received suggested rooms per space ID - */ - NSMutableDictionary *> *lastSuggestedRooms; - - /** - Event listener of the current space used to update the UI if an event occurs. - */ - id spaceEventsListener; - - /** - Observer used to reload data when the space service is initialised - */ - id spaceServiceDidInitialiseObserver; -} - -/** - Additional suggestedRooms related to the current selected Space - */ -@property (nonatomic, strong) NSArray *suggestedRooms; - -@end @implementation MXKSessionRecentsDataSource @@ -79,478 +33,52 @@ static NSTimeInterval const roomSummaryChangeThrottlerDelay = .5; self = [super initWithMatrixSession:matrixSession]; if (self) { - roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mxSession]; + // Update here data source state + if (state != MXKDataSourceStateReady) + { + state = MXKDataSourceStateReady; + if (self.delegate && [self.delegate respondsToSelector:@selector(dataSource:didStateChange:)]) + { + [self.delegate dataSource:self didStateChange:state]; + } + } - internalCellDataArray = [NSMutableArray array]; - filteredCellDataArray = nil; - - lastSuggestedRooms = [NSMutableDictionary new]; - - // Set default data and view classes - [self registerCellDataClass:MXKRecentCellData.class forCellIdentifier:kMXKRecentCellIdentifier]; - - roomSummaryChangeThrottler = [[MXThrottler alloc] initWithMinimumDelay:roomSummaryChangeThrottlerDelay]; - - [[MXKAppSettings standardAppSettings] addObserver:self forKeyPath:@"showAllRoomsInHomeSpace" options:0 context:nil]; + // And inform the delegate about the update + [self.delegate dataSource:self didCellChange:nil]; } return self; } -- (void)destroy -{ - [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXRoomSummaryDidChangeNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXKRoomDataSourceSyncStatusChanged object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionNewRoomNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionDidLeaveRoomNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionDirectRoomsDidChangeNotification object:nil]; - - if (spaceServiceDidInitialiseObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:spaceServiceDidInitialiseObserver]; - } - - [roomSummaryChangeThrottler cancelAll]; - roomSummaryChangeThrottler = nil; - - cellDataArray = nil; - internalCellDataArray = nil; - filteredCellDataArray = nil; - lastSuggestedRooms = nil; - - searchPatternsList = nil; - - [[MXKAppSettings standardAppSettings] removeObserver:self forKeyPath:@"showAllRoomsInHomeSpace" context:nil]; - - [super destroy]; -} - -- (void)didMXSessionStateChange -{ - if (MXSessionStateStoreDataReady <= self.mxSession.state) - { - // Check whether some data have been already load - if (0 == internalCellDataArray.count) - { - [self loadData]; - } - else if (!roomDataSourceManager.isServerSyncInProgress) - { - // Sort cell data and notify the delegate - [self sortCellDataAndNotifyChanges]; - } - } -} - -- (void)setCurrentSpace:(MXSpace *)currentSpace -{ - if (_currentSpace == currentSpace) - { - return; - } - - if (_currentSpace && spaceEventsListener) - { - [_currentSpace.room removeListener:spaceEventsListener]; - } - - _currentSpace = currentSpace; - - self.suggestedRooms = _currentSpace ? lastSuggestedRooms[_currentSpace.spaceId] : nil; - [self updateSuggestedRooms]; - - MXWeakify(self); - spaceEventsListener = [self.currentSpace.room listenToEvents:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { - MXStrongifyAndReturnIfNil(self); - [self updateSuggestedRooms]; - }]; -} - --(void)setSuggestedRooms:(NSArray *)suggestedRooms -{ - _suggestedRooms = suggestedRooms; - [self loadData]; -} - --(void)updateSuggestedRooms -{ - if (self.currentSpace) - { - NSString *currentSpaceId = self.currentSpace.spaceId; - MXWeakify(self); - [self.mxSession.spaceService getSpaceChildrenForSpaceWithId:currentSpaceId suggestedOnly:YES limit:5 maxDepth:1 paginationToken:nil success:^(MXSpaceChildrenSummary * _Nonnull childrenSummary) { - MXLogDebug(@"[MXKSessionRecentsDataSource] getSpaceChildrenForSpaceWithId %@: %ld found", self.currentSpace.spaceId, childrenSummary.childInfos.count); - MXStrongifyAndReturnIfNil(self); - self->lastSuggestedRooms[currentSpaceId] = childrenSummary.childInfos; - if ([self.currentSpace.spaceId isEqual:currentSpaceId]) { - self.suggestedRooms = childrenSummary.childInfos; - } - } failure:^(NSError * _Nonnull error) { - MXLogError(@"[MXKSessionRecentsDataSource] getSpaceChildrenForSpaceWithId failed with error: %@", error); - }]; - } -} - #pragma mark - - (NSInteger)numberOfCells { - if (filteredCellDataArray) - { - return filteredCellDataArray.count; - } - return cellDataArray.count; + return 0; } - (BOOL)hasUnread { - // Check all current cells - // Use numberOfRowsInSection methods so that we take benefit of the filtering - for (NSUInteger i = 0; i < self.numberOfCells; i++) - { - id cellData = [self cellDataAtIndex:i]; - if (cellData.hasUnread) - { - return YES; - } - } return NO; } - (void)searchWithPatterns:(NSArray*)patternsList { - if (patternsList.count) - { - searchPatternsList = patternsList; - - if (filteredCellDataArray) - { - [filteredCellDataArray removeAllObjects]; - } - else - { - filteredCellDataArray = [NSMutableArray arrayWithCapacity:cellDataArray.count]; - } - - for (id cellData in cellDataArray) - { - for (NSString* pattern in patternsList) - { - if (cellData.roomDisplayname && [cellData.roomDisplayname rangeOfString:pattern options:NSCaseInsensitiveSearch].location != NSNotFound) - { - [filteredCellDataArray addObject:cellData]; - break; - } - } - } - } - else - { - filteredCellDataArray = nil; - searchPatternsList = nil; - } - - [self.delegate dataSource:self didCellChange:nil]; } - (id)cellDataAtIndex:(NSInteger)index { - if (filteredCellDataArray) - { - if (index < filteredCellDataArray.count) - { - return filteredCellDataArray[index]; - } - } - else if (index < cellDataArray.count) - { - return cellDataArray[index]; - } - return nil; } - (CGFloat)cellHeightAtIndex:(NSInteger)index { - if (self.delegate) - { - id cellData = [self cellDataAtIndex:index]; - - Class class = [self.delegate cellViewClassForCellData:cellData]; - return [class heightForCellData:cellData withMaximumWidth:0]; - } - return 0; } -#pragma mark - Events processing - -/** - Filtering in this method won't have any effect anymore. This class is not maintained. - */ -- (void)loadData -{ - [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXRoomSummaryDidChangeNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXKRoomDataSourceSyncStatusChanged object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionNewRoomNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionDidLeaveRoomNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionDirectRoomsDidChangeNotification object:nil]; - - if (!self.mxSession.spaceService.isInitialised && !spaceServiceDidInitialiseObserver) { - MXWeakify(self); - spaceServiceDidInitialiseObserver = [[NSNotificationCenter defaultCenter] addObserverForName:MXSpaceService.didInitialise object:self.mxSession.spaceService queue:nil usingBlock:^(NSNotification * _Nonnull note) { - MXStrongifyAndReturnIfNil(self); - [self loadData]; - }]; - } - - // Reset the table - [internalCellDataArray removeAllObjects]; - - // Retrieve the MXKCellData class to manage the data - Class class = [self cellDataClassForCellIdentifier:kMXKRecentCellIdentifier]; - NSAssert([class conformsToProtocol:@protocol(MXKRecentCellDataStoring)], @"MXKSessionRecentsDataSource only manages MXKCellData that conforms to MXKRecentCellDataStoring protocol"); - - NSDate *startDate = [NSDate date]; - - for (MXRoom *room in self.mxSession.rooms) - { - MXRoomSummary *summary = room.summary; - if (summary) - { - // Filter out private rooms with conference users - if (!summary.isConferenceUserRoom // @TODO Abstract this condition with roomSummary.hiddenFromUser - && !summary.hiddenFromUser) - { - id cellData = [[class alloc] initWithRoomSummary:summary dataSource:self]; - if (cellData) - { - [internalCellDataArray addObject:cellData]; - } - } - } - } - - for (MXSpaceChildInfo *childInfo in _suggestedRooms) - { - id summary = [[MXRoomSummary alloc] initWithSpaceChildInfo:childInfo]; - id cellData = [[class alloc] initWithRoomSummary:summary - dataSource:self]; - if (cellData) - { - [internalCellDataArray addObject:cellData]; - } - } - - MXLogDebug(@"[MXKSessionRecentsDataSource] Loaded %tu recents in %.3fms", self.mxSession.rooms.count, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); - - // Make sure all rooms have a last message - [self.mxSession fixRoomsSummariesLastMessage]; - - // Report loaded array except if sync is in progress - if (!roomDataSourceManager.isServerSyncInProgress) - { - [self sortCellDataAndNotifyChanges]; - } - - // Listen to MXSession rooms count changes - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didMXSessionHaveNewRoom:) name:kMXSessionNewRoomNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didMXSessionDidLeaveRoom:) name:kMXSessionDidLeaveRoomNotification object:nil]; - - // Listen to the direct rooms list - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didDirectRoomsChange:) name:kMXSessionDirectRoomsDidChangeNotification object:nil]; - - // Listen to MXRoomSummary - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRoomSummaryChanged:) name:kMXRoomSummaryDidChangeNotification object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didMXSessionStateChange) name:kMXKRoomDataSourceSyncStatusChanged object:nil]; -} - -- (void)didDirectRoomsChange:(NSNotification *)notif -{ - // Inform the delegate about the update - [self.delegate dataSource:self didCellChange:nil]; -} - -- (void)didRoomSummaryChanged:(NSNotification *)notif -{ - [roomSummaryChangeThrottler throttle:^{ - [self didRoomSummaryChanged2:notif]; - }]; -} - -- (void)didRoomSummaryChanged2:(NSNotification *)notif -{ - MXRoomSummary *roomSummary = notif.object; - if (roomSummary.mxSession == self.mxSession && internalCellDataArray.count) - { - // Find the index of the related cell data - NSInteger index = NSNotFound; - for (index = 0; index < internalCellDataArray.count; index++) - { - id theRoomData = [internalCellDataArray objectAtIndex:index]; - if (theRoomData.roomSummary == roomSummary) - { - break; - } - } - - if (index < internalCellDataArray.count) - { - if (roomSummary.hiddenFromUser) - { - [internalCellDataArray removeObjectAtIndex:index]; - } - else - { - // Create a new instance to not modify the content of 'cellDataArray' (the copy is not a deep copy). - Class class = [self cellDataClassForCellIdentifier:kMXKRecentCellIdentifier]; - id cellData = [[class alloc] initWithRoomSummary:roomSummary dataSource:self]; - if (cellData) - { - [internalCellDataArray replaceObjectAtIndex:index withObject:cellData]; - } - } - - // Report change except if sync is in progress - if (!roomDataSourceManager.isServerSyncInProgress) - { - [self sortCellDataAndNotifyChanges]; - } - } - else - { - MXLogDebug(@"[MXKSessionRecentsDataSource] didRoomLastMessageChanged: Cannot find the changed room summary for %@ (%@). It is probably not managed by this recents data source", roomSummary.roomId, roomSummary); - } - } - else - { - // Inform the delegate that all the room summaries have been updated. - [self.delegate dataSource:self didCellChange:nil]; - } -} - -- (void)didMXSessionHaveNewRoom:(NSNotification *)notif -{ - MXSession *mxSession = notif.object; - if (mxSession == self.mxSession) - { - NSString *roomId = notif.userInfo[kMXSessionNotificationRoomIdKey]; - - // Add the room if there is not yet a cell for it - id roomData = [self cellDataWithRoomId:roomId]; - if (nil == roomData) - { - MXLogDebug(@"MXKSessionRecentsDataSource] Add newly joined room: %@", roomId); - - // Retrieve the MXKCellData class to manage the data - Class class = [self cellDataClassForCellIdentifier:kMXKRecentCellIdentifier]; - - MXRoomSummary *roomSummary = [mxSession roomSummaryWithRoomId:roomId]; - id cellData = [[class alloc] initWithRoomSummary:roomSummary dataSource:self]; - if (cellData) - { - [internalCellDataArray addObject:cellData]; - - // Report change except if sync is in progress - if (!roomDataSourceManager.isServerSyncInProgress) - { - [self sortCellDataAndNotifyChanges]; - } - } - } - } -} - -- (void)didMXSessionDidLeaveRoom:(NSNotification *)notif -{ - MXSession *mxSession = notif.object; - if (mxSession == self.mxSession) - { - NSString *roomId = notif.userInfo[kMXSessionNotificationRoomIdKey]; - id roomData = [self cellDataWithRoomId:roomId]; - - if (roomData) - { - MXLogDebug(@"MXKSessionRecentsDataSource] Remove left room: %@", roomId); - - [internalCellDataArray removeObject:roomData]; - - // Report change except if sync is in progress - if (!roomDataSourceManager.isServerSyncInProgress) - { - [self sortCellDataAndNotifyChanges]; - } - } - } -} - -// Order cells -- (void)sortCellDataAndNotifyChanges -{ - // Order them by origin_server_ts - [internalCellDataArray sortUsingComparator:^NSComparisonResult(id cellData1, id cellData2) - { - return [cellData1.roomSummary.lastMessage compareOriginServerTs:cellData2.roomSummary.lastMessage]; - }]; - - // Snapshot the cell data array - cellDataArray = [internalCellDataArray copy]; - - // Update search result if any - if (searchPatternsList) - { - [self searchWithPatterns:searchPatternsList]; - } - - // Update here data source state - if (state != MXKDataSourceStateReady) - { - state = MXKDataSourceStateReady; - if (self.delegate && [self.delegate respondsToSelector:@selector(dataSource:didStateChange:)]) - { - [self.delegate dataSource:self didStateChange:state]; - } - } - - // And inform the delegate about the update - [self.delegate dataSource:self didCellChange:nil]; -} - // Find the cell data that stores information about the given room id - (id)cellDataWithRoomId:(NSString*)roomId { - id theRoomData; - - NSMutableArray *dataArray = internalCellDataArray; - if (!roomDataSourceManager.isServerSyncInProgress) - { - dataArray = cellDataArray; - } - - for (id roomData in dataArray) - { - if ([roomData.roomSummary.roomId isEqualToString:roomId]) - { - theRoomData = roomData; - break; - } - } - return theRoomData; -} - -#pragma mark - KVO - -- (void)observeValueForKeyPath:(NSString *)keyPath - ofObject:(id)object - change:(NSDictionary *)change - context:(void *)context -{ - if (object == [MXKAppSettings standardAppSettings] && [keyPath isEqualToString:@"showAllRoomsInHomeSpace"]) - { - if (self.currentSpace == nil) - { - [self loadData]; - } - } + return nil; } @end