mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-18 15:38:28 +02:00
Merge MatrixKit develop with commit hash: b85b736313bec0592bd1cabc68035d97f5331137
This commit is contained in:
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
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 "MXKRoomMemberListDataSource.h"
|
||||
|
||||
@import MatrixSDK.MXCallManager;
|
||||
|
||||
#import "MXKRoomMemberCellData.h"
|
||||
|
||||
|
||||
#pragma mark - Constant definitions
|
||||
NSString *const kMXKRoomMemberCellIdentifier = @"kMXKRoomMemberCellIdentifier";
|
||||
|
||||
|
||||
@interface MXKRoomMemberListDataSource ()
|
||||
{
|
||||
/**
|
||||
The room in which members are listed.
|
||||
*/
|
||||
MXRoom *mxRoom;
|
||||
|
||||
/**
|
||||
Cache for loaded room state.
|
||||
*/
|
||||
MXRoomState *mxRoomState;
|
||||
|
||||
/**
|
||||
The members events listener.
|
||||
*/
|
||||
id membersListener;
|
||||
|
||||
/**
|
||||
The typing notification listener in the room.
|
||||
*/
|
||||
id typingNotifListener;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MXKRoomMemberListDataSource
|
||||
|
||||
- (instancetype)initWithRoomId:(NSString*)roomId andMatrixSession:(MXSession*)mxSession
|
||||
{
|
||||
self = [super initWithMatrixSession:mxSession];
|
||||
if (self)
|
||||
{
|
||||
_roomId = roomId;
|
||||
|
||||
cellDataArray = [NSMutableArray array];
|
||||
filteredCellDataArray = nil;
|
||||
|
||||
// Consider the shared app settings by default
|
||||
_settings = [MXKAppSettings standardAppSettings];
|
||||
|
||||
// Set default data class
|
||||
[self registerCellDataClass:MXKRoomMemberCellData.class forCellIdentifier:kMXKRoomMemberCellIdentifier];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
cellDataArray = nil;
|
||||
filteredCellDataArray = nil;
|
||||
|
||||
if (membersListener)
|
||||
{
|
||||
[self.mxSession removeListener:membersListener];
|
||||
membersListener = nil;
|
||||
}
|
||||
|
||||
if (typingNotifListener)
|
||||
{
|
||||
MXWeakify(self);
|
||||
[mxRoom liveTimeline:^(MXEventTimeline *liveTimeline) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[liveTimeline removeListener:self->typingNotifListener];
|
||||
self->typingNotifListener = nil;
|
||||
}];
|
||||
}
|
||||
|
||||
[super destroy];
|
||||
}
|
||||
|
||||
- (void)didMXSessionStateChange
|
||||
{
|
||||
if (MXSessionStateStoreDataReady <= self.mxSession.state)
|
||||
{
|
||||
// Check whether the room is not already set
|
||||
if (!mxRoom)
|
||||
{
|
||||
mxRoom = [self.mxSession roomWithRoomId:_roomId];
|
||||
if (mxRoom)
|
||||
{
|
||||
MXWeakify(self);
|
||||
[mxRoom state:^(MXRoomState *roomState) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
self->mxRoomState = roomState;
|
||||
|
||||
[self loadData];
|
||||
|
||||
// Register on typing notif
|
||||
[self listenTypingNotifications];
|
||||
|
||||
// Register on members events
|
||||
[self listenMembersEvents];
|
||||
|
||||
// Update here data source state
|
||||
self->state = MXKDataSourceStateReady;
|
||||
|
||||
// Notify delegate
|
||||
if (self.delegate)
|
||||
{
|
||||
if ([self.delegate respondsToSelector:@selector(dataSource:didStateChange:)])
|
||||
{
|
||||
[self.delegate dataSource:self didStateChange:self->state];
|
||||
}
|
||||
[self.delegate dataSource:self didCellChange:nil];
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
MXLogDebug(@"[MXKRoomMemberDataSource] The user does not know the room %@", _roomId);
|
||||
|
||||
// Update here data source state
|
||||
state = MXKDataSourceStateFailed;
|
||||
|
||||
// Notify delegate
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(dataSource:didStateChange:)])
|
||||
{
|
||||
[self.delegate dataSource:self didStateChange:state];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)searchWithPatterns:(NSArray*)patternsList
|
||||
{
|
||||
if (patternsList.count)
|
||||
{
|
||||
if (filteredCellDataArray)
|
||||
{
|
||||
[filteredCellDataArray removeAllObjects];
|
||||
}
|
||||
else
|
||||
{
|
||||
filteredCellDataArray = [NSMutableArray arrayWithCapacity:cellDataArray.count];
|
||||
}
|
||||
|
||||
for (id<MXKRoomMemberCellDataStoring> cellData in cellDataArray)
|
||||
{
|
||||
for (NSString* pattern in patternsList)
|
||||
{
|
||||
if ([[cellData memberDisplayName] rangeOfString:pattern options:NSCaseInsensitiveSearch].location != NSNotFound)
|
||||
{
|
||||
[filteredCellDataArray addObject:cellData];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
filteredCellDataArray = nil;
|
||||
}
|
||||
|
||||
if (self.delegate)
|
||||
{
|
||||
[self.delegate dataSource:self didCellChange:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (id<MXKRoomMemberCellDataStoring>)cellDataAtIndex:(NSInteger)index
|
||||
{
|
||||
if (filteredCellDataArray)
|
||||
{
|
||||
return filteredCellDataArray[index];
|
||||
}
|
||||
return cellDataArray[index];
|
||||
}
|
||||
|
||||
- (CGFloat)cellHeightAtIndex:(NSInteger)index
|
||||
{
|
||||
if (self.delegate)
|
||||
{
|
||||
id<MXKRoomMemberCellDataStoring> cellData = [self cellDataAtIndex:index];
|
||||
|
||||
Class<MXKCellRendering> class = [self.delegate cellViewClassForCellData:cellData];
|
||||
return [class heightForCellData:cellData withMaximumWidth:0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma mark - Members processing
|
||||
|
||||
- (void)loadData
|
||||
{
|
||||
NSArray* membersList = [mxRoomState.members membersWithoutConferenceUser];
|
||||
|
||||
if (!_settings.showLeftMembersInRoomMemberList)
|
||||
{
|
||||
NSMutableArray* filteredMembers = [[NSMutableArray alloc] init];
|
||||
|
||||
for (MXRoomMember* member in membersList)
|
||||
{
|
||||
// Filter out left users
|
||||
if (member.membership != MXMembershipLeave)
|
||||
{
|
||||
[filteredMembers addObject:member];
|
||||
}
|
||||
}
|
||||
|
||||
membersList = filteredMembers;
|
||||
}
|
||||
|
||||
[cellDataArray removeAllObjects];
|
||||
|
||||
// Retrieve the MXKCellData class to manage the data
|
||||
Class class = [self cellDataClassForCellIdentifier:kMXKRoomMemberCellIdentifier];
|
||||
NSAssert([class conformsToProtocol:@protocol(MXKRoomMemberCellDataStoring)], @"MXKRoomMemberListDataSource only manages MXKCellData that conforms to MXKRoomMemberCellDataStoring protocol");
|
||||
|
||||
for (MXRoomMember *member in membersList)
|
||||
{
|
||||
|
||||
id<MXKRoomMemberCellDataStoring> cellData = [[class alloc] initWithRoomMember:member roomState:mxRoomState andRoomMemberListDataSource:self];
|
||||
if (cellData)
|
||||
{
|
||||
[cellDataArray addObject:cellData];
|
||||
}
|
||||
}
|
||||
|
||||
[self sortMembers];
|
||||
}
|
||||
|
||||
- (void)sortMembers
|
||||
{
|
||||
NSArray *sortedMembers = [cellDataArray sortedArrayUsingComparator:^NSComparisonResult(id<MXKRoomMemberCellDataStoring> member1, id<MXKRoomMemberCellDataStoring> member2)
|
||||
{
|
||||
|
||||
// Move banned and left members at the end of the list
|
||||
if (member1.roomMember.membership == MXMembershipLeave || member1.roomMember.membership == MXMembershipBan)
|
||||
{
|
||||
if (member2.roomMember.membership != MXMembershipLeave && member2.roomMember.membership != MXMembershipBan)
|
||||
{
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
}
|
||||
else if (member2.roomMember.membership == MXMembershipLeave || member2.roomMember.membership == MXMembershipBan)
|
||||
{
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
|
||||
// Move invited members just before left and banned members
|
||||
if (member1.roomMember.membership == MXMembershipInvite)
|
||||
{
|
||||
if (member2.roomMember.membership != MXMembershipInvite)
|
||||
{
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
}
|
||||
else if (member2.roomMember.membership == MXMembershipInvite)
|
||||
{
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
|
||||
if (self->_settings.sortRoomMembersUsingLastSeenTime)
|
||||
{
|
||||
// Get the users that correspond to these members
|
||||
MXUser *user1 = [self.mxSession userWithUserId:member1.roomMember.userId];
|
||||
MXUser *user2 = [self.mxSession userWithUserId:member2.roomMember.userId];
|
||||
|
||||
// Move users who are not online or unavailable at the end (before invited users)
|
||||
if ((user1.presence == MXPresenceOnline) || (user1.presence == MXPresenceUnavailable))
|
||||
{
|
||||
if ((user2.presence != MXPresenceOnline) && (user2.presence != MXPresenceUnavailable))
|
||||
{
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
}
|
||||
else if ((user2.presence == MXPresenceOnline) || (user2.presence == MXPresenceUnavailable))
|
||||
{
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here both users are neither online nor unavailable (the lastActive ago is useless)
|
||||
// We will sort them according to their display, by keeping in front the offline users
|
||||
if (user1.presence == MXPresenceOffline)
|
||||
{
|
||||
if (user2.presence != MXPresenceOffline)
|
||||
{
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
}
|
||||
else if (user2.presence == MXPresenceOffline)
|
||||
{
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
return [[self->mxRoomState.members memberSortedName:member1.roomMember.userId] compare:[self->mxRoomState.members memberSortedName:member2.roomMember.userId] options:NSCaseInsensitiveSearch];
|
||||
}
|
||||
|
||||
// Consider user's lastActive ago value
|
||||
if (user1.lastActiveAgo < user2.lastActiveAgo)
|
||||
{
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
else if (user1.lastActiveAgo == user2.lastActiveAgo)
|
||||
{
|
||||
return [[self->mxRoomState.members memberSortedName:member1.roomMember.userId] compare:[self->mxRoomState.members memberSortedName:member2.roomMember.userId] options:NSCaseInsensitiveSearch];
|
||||
}
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move user without display name at the end (before invited users)
|
||||
if (member1.roomMember.displayname.length)
|
||||
{
|
||||
if (!member2.roomMember.displayname.length)
|
||||
{
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
}
|
||||
else if (member2.roomMember.displayname.length)
|
||||
{
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
|
||||
return [[self->mxRoomState.members memberSortedName:member1.roomMember.userId] compare:[self->mxRoomState.members memberSortedName:member2.roomMember.userId] options:NSCaseInsensitiveSearch];
|
||||
}
|
||||
}];
|
||||
|
||||
cellDataArray = [NSMutableArray arrayWithArray:sortedMembers];
|
||||
}
|
||||
|
||||
- (void)listenMembersEvents
|
||||
{
|
||||
// Remove the previous live listener
|
||||
if (membersListener)
|
||||
{
|
||||
[self.mxSession removeListener:membersListener];
|
||||
}
|
||||
|
||||
// Register a listener for events that concern room members
|
||||
NSArray *mxMembersEvents = @[
|
||||
kMXEventTypeStringRoomMember,
|
||||
kMXEventTypeStringRoomPowerLevels,
|
||||
kMXEventTypeStringPresence
|
||||
];
|
||||
membersListener = [self.mxSession listenToEventsOfTypes:mxMembersEvents onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject)
|
||||
{
|
||||
// consider only live event
|
||||
if (direction == MXTimelineDirectionForwards)
|
||||
{
|
||||
// Check the room Id (if any)
|
||||
if (event.roomId && [event.roomId isEqualToString:self->mxRoom.roomId] == NO)
|
||||
{
|
||||
// This event does not concern the current room members
|
||||
return;
|
||||
}
|
||||
|
||||
// refresh the whole members list. TODO GFO refresh only the updated members.
|
||||
[self loadData];
|
||||
|
||||
if (self.delegate)
|
||||
{
|
||||
[self.delegate dataSource:self didCellChange:nil];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)listenTypingNotifications
|
||||
{
|
||||
// Remove the previous live listener
|
||||
if (self->typingNotifListener)
|
||||
{
|
||||
[mxRoom removeListener:self->typingNotifListener];
|
||||
}
|
||||
|
||||
// Add typing notification listener
|
||||
self->typingNotifListener = [mxRoom listenToEventsOfTypes:@[kMXEventTypeStringTypingNotification] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) {
|
||||
// Handle only live events
|
||||
if (direction == MXTimelineDirectionForwards)
|
||||
{
|
||||
// Retrieve typing users list
|
||||
NSMutableArray *typingUsers = [NSMutableArray arrayWithArray:self->mxRoom.typingUsers];
|
||||
// Remove typing info for the current user
|
||||
NSUInteger index = [typingUsers indexOfObject:self.mxSession.myUser.userId];
|
||||
if (index != NSNotFound)
|
||||
{
|
||||
[typingUsers removeObjectAtIndex:index];
|
||||
}
|
||||
|
||||
for (id<MXKRoomMemberCellDataStoring> cellData in self->cellDataArray)
|
||||
{
|
||||
if ([typingUsers indexOfObject:cellData.roomMember.userId] == NSNotFound)
|
||||
{
|
||||
cellData.isTyping = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
cellData.isTyping = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (self.delegate)
|
||||
{
|
||||
[self.delegate dataSource:self didCellChange:nil];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
if (filteredCellDataArray)
|
||||
{
|
||||
return filteredCellDataArray.count;
|
||||
}
|
||||
return cellDataArray.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
id<MXKRoomMemberCellDataStoring> roomData = [self cellDataAtIndex:indexPath.row];
|
||||
|
||||
if (roomData && self.delegate)
|
||||
{
|
||||
NSString *identifier = [self.delegate cellReuseIdentifierForCellData:roomData];
|
||||
if (identifier)
|
||||
{
|
||||
UITableViewCell<MXKCellRendering> *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
|
||||
|
||||
// Make the bubble display the data
|
||||
[cell render:roomData];
|
||||
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
||||
// Return a fake cell to prevent app from crashing.
|
||||
return [[UITableViewCell alloc] init];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user