Merge MatrixKit develop with commit hash: b85b736313bec0592bd1cabc68035d97f5331137

This commit is contained in:
SBiOSoftWhare
2021-12-03 11:47:24 +01:00
parent af65f71177
commit f57941177e
475 changed files with 87437 additions and 0 deletions
@@ -0,0 +1,33 @@
/*
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 <Foundation/Foundation.h>
#import <MatrixSDK/MatrixSDK.h>
#import "MXKRoomMemberCellDataStoring.h"
/**
`MXKRoomMemberCellData` modelised the data for a `MXKRoomMemberTableViewCell` cell.
*/
@interface MXKRoomMemberCellData : MXKCellData <MXKRoomMemberCellDataStoring>
/**
The matrix session
*/
@property (nonatomic, readonly) MXSession *mxSession;
@end
@@ -0,0 +1,66 @@
/*
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 "MXKRoomMemberCellData.h"
#import "MXKRoomMemberListDataSource.h"
@interface MXKRoomMemberCellData ()
{
MXKRoomMemberListDataSource *roomMemberListDataSource;
}
@end
@implementation MXKRoomMemberCellData
@synthesize roomMember;
@synthesize memberDisplayName, powerLevel, isTyping;
- (instancetype)initWithRoomMember:(MXRoomMember*)member roomState:(MXRoomState*)roomState andRoomMemberListDataSource:(MXKRoomMemberListDataSource*)memberListDataSource
{
self = [self init];
if (self)
{
roomMember = member;
roomMemberListDataSource = memberListDataSource;
// Report member info from the current room state
memberDisplayName = [roomState.members memberName:roomMember.userId];
powerLevel = [roomState memberNormalizedPowerLevel:roomMember.userId];
isTyping = NO;
}
return self;
}
- (void)updateWithRoomState:(MXRoomState*)roomState
{
memberDisplayName = [roomState.members memberName:roomMember.userId];
powerLevel = [roomState memberNormalizedPowerLevel:roomMember.userId];
}
- (void)dealloc
{
roomMember = nil;
roomMemberListDataSource = nil;
}
- (MXSession*)mxSession
{
return roomMemberListDataSource.mxSession;
}
@end
@@ -0,0 +1,67 @@
/*
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 <Foundation/Foundation.h>
#import <MatrixSDK/MatrixSDK.h>
#import "MXKCellData.h"
@class MXKRoomMemberListDataSource;
/**
`MXKRoomMemberCellDataStoring` defines a protocol a class must conform in order to store room member cell data
managed by `MXKRoomMemberListDataSource`.
*/
@protocol MXKRoomMemberCellDataStoring <NSObject>
#pragma mark - Data displayed by a room member cell
/**
The member displayed by the cell.
*/
@property (nonatomic, readonly) MXRoomMember *roomMember;
/**
The member display name
*/
@property (nonatomic, readonly) NSString *memberDisplayName;
/**
The member power level
*/
@property (nonatomic, readonly) CGFloat powerLevel;
/**
YES when member is typing in the room
*/
@property (nonatomic) BOOL isTyping;
#pragma mark - Public methods
/**
Create a new `MXKCellData` object for a new member cell.
@param memberListDataSource the `MXKRoomMemberListDataSource` object that will use this instance.
@return the newly created instance.
*/
- (instancetype)initWithRoomMember:(MXRoomMember*)member roomState:(MXRoomState*)roomState andRoomMemberListDataSource:(MXKRoomMemberListDataSource*)memberListDataSource;
/**
Update the member data with the provided roon state.
*/
- (void)updateWithRoomState:(MXRoomState*)roomState;
@end
@@ -0,0 +1,97 @@
/*
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 <UIKit/UIKit.h>
#import <MatrixSDK/MatrixSDK.h>
#import "MXKDataSource.h"
#import "MXKRoomMemberCellData.h"
#import "MXKAppSettings.h"
/**
Identifier to use for cells that display a room member.
*/
extern NSString *const kMXKRoomMemberCellIdentifier;
/**
The data source for `MXKRoomMemberListViewController`.
*/
@interface MXKRoomMemberListDataSource : MXKDataSource <UITableViewDataSource> {
@protected
/**
The data for the cells served by `MXKRoomMembersDataSource`.
*/
NSMutableArray *cellDataArray;
/**
The filtered members: sub-list of `cellDataArray` defined by `searchWithPatterns:`.
*/
NSMutableArray *filteredCellDataArray;
}
/**
The id of the room managed by the data source.
*/
@property (nonatomic, readonly) NSString *roomId;
/**
The settings used to sort/display room members.
By default the shared application settings are considered.
*/
@property (nonatomic) MXKAppSettings *settings;
#pragma mark - Life cycle
/**
Initialise the data source to serve members corresponding to the passed room.
@param roomId the id of the room to get members from.
@param mxSession the Matrix session to get data from.
@return the newly created instance.
*/
- (instancetype)initWithRoomId:(NSString*)roomId andMatrixSession:(MXSession*)mxSession;
/**
Filter the current members list according to the provided patterns.
When patterns are not empty, the search result is stored in `filteredCellDataArray`,
this array provides then data for the cells served by `MXKRoomMembersDataSource`.
@param patternsList the list of patterns (`NSString` instances) to match with. Set nil to cancel search.
*/
- (void)searchWithPatterns:(NSArray*)patternsList;
/**
Get the data for the cell at the given index.
@param index the index of the cell in the array
@return the cell data
*/
- (id<MXKRoomMemberCellDataStoring>)cellDataAtIndex:(NSInteger)index;
/**
Get height of the celle at the given index.
@param index the index of the cell in the array
@return the cell height
*/
- (CGFloat)cellHeightAtIndex:(NSInteger)index;
@end
@@ -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