element-ios/Riot/Modules/PublicRoomList/DataSources/PublicRoomsDirectoryDataSource.m

438 lines
12 KiB
Objective-C

/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations 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 "PublicRoomsDirectoryDataSource.h"
#import "PublicRoomTableViewCell.h"
#import "GeneratedInterface-Swift.h"
#pragma mark - Constants definitions
// Time in seconds from which public rooms data is considered as obsolete
double const kPublicRoomsDirectoryDataExpiration = 10;
static NSString *const kNSFWKeyword = @"nsfw";
#pragma mark - PublicRoomsDirectoryDataSource
@interface PublicRoomsDirectoryDataSource ()
{
// The pending request to refresh public rooms data.
MXHTTPOperation *publicRoomsRequest;
/**
All public rooms fetched so far.
*/
NSMutableArray<MXPublicRoom*> *rooms;
/**
The next token to use for pagination.
*/
NSString *nextBatch;
}
@end
@implementation PublicRoomsDirectoryDataSource
- (instancetype)init
{
self = [super init];
if (self)
{
rooms = [NSMutableArray array];
_paginationLimit = 20;
}
return self;
}
- (NSString *)directoryServerDisplayname
{
NSString *directoryServerDisplayname;
if (_homeserver)
{
directoryServerDisplayname = _homeserver;
}
else if (_thirdpartyProtocolInstance)
{
directoryServerDisplayname = _thirdpartyProtocolInstance.desc;
}
else
{
if (_includeAllNetworks)
{
// We display all rooms, included bridged ones, of the user's HS
directoryServerDisplayname = self.mxSession.matrixRestClient.credentials.homeServerName;
}
else
{
// We display only Matrix rooms of the user's HS
directoryServerDisplayname = [VectorL10n matrix];
}
}
return directoryServerDisplayname;
}
- (void)setHomeserver:(NSString *)homeserver
{
if ([homeserver isEqualToString:self.mxSession.matrixRestClient.credentials.homeServerName])
{
// The CS API does not like we pass the user's HS as parameter
homeserver = nil;
}
_thirdpartyProtocolInstance = nil;
if (homeserver != _homeserver)
{
_homeserver = homeserver;
// Reset data
[self resetPagination];
}
}
- (void)setIncludeAllNetworks:(BOOL)includeAllNetworks
{
if (includeAllNetworks != _includeAllNetworks)
{
_includeAllNetworks = includeAllNetworks;
// Reset data
[self resetPagination];
}
}
- (void)setThirdpartyProtocolInstance:(MXThirdPartyProtocolInstance *)thirdpartyProtocolInstance
{
if (thirdpartyProtocolInstance != _thirdpartyProtocolInstance)
{
_homeserver = nil;
_includeAllNetworks = NO;
_thirdpartyProtocolInstance = thirdpartyProtocolInstance;
// Reset data
[self resetPagination];
}
}
- (void)setSearchPattern:(NSString *)searchPattern
{
if (searchPattern)
{
if (![searchPattern isEqualToString:_searchPattern])
{
_searchPattern = searchPattern;
[self resetPagination];
}
}
else
{
// Refresh if the previous search was not nil
// or if it is the first time we make a search
if (_searchPattern || rooms.count == 0)
{
_searchPattern = searchPattern;
[self resetPagination];
}
}
}
- (void)setShowNSFWRooms:(BOOL)showNSFWRooms
{
if (showNSFWRooms != _showNSFWRooms)
{
_showNSFWRooms = showNSFWRooms;
[self resetPagination];
}
}
- (NSUInteger)roomsCount
{
return rooms.count;
}
- (NSIndexPath*)cellIndexPathWithRoomId:(NSString*)roomId andMatrixSession:(MXSession*)matrixSession
{
NSIndexPath *indexPath = nil;
// Look for the public room
for (NSInteger index = 0; index < rooms.count; index ++)
{
MXPublicRoom *room = rooms[index];
if ([roomId isEqualToString:room.roomId])
{
// Got it
indexPath = [NSIndexPath indexPathForRow:index inSection:0];
break;
}
}
return indexPath;
}
- (MXPublicRoom *)roomAtIndexPath:(NSIndexPath *)indexPath
{
MXPublicRoom *room;
if (indexPath.row < rooms.count)
{
room = rooms[indexPath.row];
}
return room;
}
- (CGFloat)cellHeightAtIndexPath:(NSIndexPath*)indexPath
{
if (indexPath.row < rooms.count)
{
return PublicRoomTableViewCell.cellHeight;
}
return 50.0;
}
- (void)resetPagination
{
// Cancel the previous request
if (publicRoomsRequest)
{
[publicRoomsRequest cancel];
}
// Reset all pagination vars
[rooms removeAllObjects];
nextBatch = nil;
_searchResultsCount = 0;
_searchResultsCountIsLimited = NO;
_hasReachedPaginationEnd = NO;
}
- (MXHTTPOperation *)paginate:(void (^)(NSUInteger))complete failure:(void (^)(NSError *))failure
{
if (_hasReachedPaginationEnd)
{
if (complete)
{
complete(0);
}
return nil;
}
[self setState:MXKDataSourceStatePreparing];
__weak typeof(self) weakSelf = self;
// Get the public rooms from the server
MXHTTPOperation *newPublicRoomsRequest;
newPublicRoomsRequest = [self.mxSession.matrixRestClient publicRoomsOnServer:_homeserver limit:_paginationLimit since:nextBatch filter:_searchPattern thirdPartyInstanceId:_thirdpartyProtocolInstance.instanceId includeAllNetworks:_includeAllNetworks success:^(MXPublicRoomsResponse *publicRoomsResponse) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->publicRoomsRequest = nil;
NSArray<MXPublicRoom*> *publicRooms;
if (self.showNSFWRooms)
{
publicRooms = publicRoomsResponse.chunk;
}
else
{
publicRooms = [self filterPublicRooms:publicRoomsResponse.chunk containingKeyword:kNSFWKeyword];
}
[self->rooms addObjectsFromArray:publicRooms];
self->nextBatch = publicRoomsResponse.nextBatch;
if (!self->_searchPattern)
{
// When there is no search, we can use totalRoomCountEstimate returned by the server
self->_searchResultsCount = publicRoomsResponse.totalRoomCountEstimate;
self->_searchResultsCountIsLimited = NO;
}
else
{
// Else we can only display something like ">20 matching rooms"
self->_searchResultsCount = self->rooms.count;
self->_searchResultsCountIsLimited = publicRoomsResponse.nextBatch ? YES : NO;
}
// Detect pagination end
if (!publicRoomsResponse.nextBatch)
{
self->_hasReachedPaginationEnd = YES;
}
[self setState:MXKDataSourceStateReady];
if (complete)
{
complete(publicRoomsResponse.chunk.count);
}
}
} failure:^(NSError *error) {
if (weakSelf)
{
typeof(self) self = weakSelf;
if (error.domain == NSURLErrorDomain && error.code == NSURLErrorCancelled)
{
// Do not take into account error coming from a cancellation
return;
}
self->publicRoomsRequest = nil;
MXLogDebug(@"[PublicRoomsDirectoryDataSource] Failed to fecth public rooms.");
[self setState:MXKDataSourceStateFailed];
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
if (failure)
{
failure(error);
}
}
}];
publicRoomsRequest = newPublicRoomsRequest;
return publicRoomsRequest;
}
#pragma mark - Private methods
// Update the MXKDataSource state and the delegate
- (void)setState:(MXKDataSourceState)newState
{
state = newState;
if (self.delegate && [self.delegate respondsToSelector:@selector(dataSource:didStateChange:)])
{
[self.delegate dataSource:self didStateChange:state];
}
}
- (NSArray<MXPublicRoom*>*)filterPublicRooms:(NSArray<MXPublicRoom*>*)publicRooms containingKeyword:(NSString*)keyword
{
NSMutableArray *filteredRooms = [NSMutableArray new];
for (MXPublicRoom *publicRoom in publicRooms)
{
if (NO == [[publicRoom.name lowercaseString] containsString:keyword]
&& NO == [[publicRoom.topic lowercaseString] containsString:keyword])
{
[filteredRooms addObject:publicRoom];
}
}
return filteredRooms;
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Display a default cell when no rooms is available.
return rooms.count ? rooms.count : 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Sanity check
if (indexPath.row < rooms.count)
{
PublicRoomTableViewCell *publicRoomCell = [tableView dequeueReusableCellWithIdentifier:[PublicRoomTableViewCell defaultReuseIdentifier]];
if (!publicRoomCell)
{
publicRoomCell = [[PublicRoomTableViewCell alloc] init];
}
[publicRoomCell render:rooms[indexPath.row] withMatrixSession:self.mxSession];
return publicRoomCell;
}
else
{
MXKTableViewCell *tableViewCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier]];
if (!tableViewCell)
{
tableViewCell = [[MXKTableViewCell alloc] init];
tableViewCell.textLabel.textColor = ThemeService.shared.theme.textSecondaryColor;
tableViewCell.textLabel.font = [UIFont systemFontOfSize:15.0];
tableViewCell.selectionStyle = UITableViewCellSelectionStyleNone;
}
if (state == MXKDataSourceStateReady)
{
if (_searchPattern.length)
{
tableViewCell.textLabel.text = [VectorL10n searchNoResult];
}
else
{
tableViewCell.textLabel.text = [VectorL10n roomDirectoryNoPublicRoom];
}
}
else
{
if (_searchPattern.length)
{
tableViewCell.textLabel.text = [VectorL10n searchInProgress];
}
else
{
// Show nothing in other cases
tableViewCell.textLabel.text = @"";
}
}
return tableViewCell;
}
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{
PublicRoomsDirectoryDataSource *source = [[[self class] allocWithZone:zone] initWithMatrixSession:self.mxSession];
source.homeserver = [self.homeserver copyWithZone:zone];
source.includeAllNetworks = self.includeAllNetworks;
if (self.thirdpartyProtocolInstance)
{
source.thirdpartyProtocolInstance = [MXThirdPartyProtocolInstance modelFromJSON:self.thirdpartyProtocolInstance.JSONDictionary];
}
source.paginationLimit = self.paginationLimit;
source.searchPattern = [self.searchPattern copyWithZone:zone];
source->rooms = [rooms mutableCopyWithZone:zone];
source->nextBatch = [nextBatch copyWithZone:zone];
return source;
}
@end