Merge pull request #1943 from vector-im/async_timeline_and_state

MXRoom: Make access to liveTimeline data async
This commit is contained in:
manuroe
2018-07-24 14:33:05 +00:00
committed by GitHub
22 changed files with 1040 additions and 859 deletions
+170 -147
View File
@@ -1108,37 +1108,42 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
{
NSArray* mxAccounts = [MXKAccountManager sharedManager].activeAccounts;
MXKRoomDataSource* roomDataSource = nil;
MXKRoomDataSourceManager* manager;
for (MXKAccount* account in mxAccounts)
{
MXRoom* room = [account.mxSession roomWithRoomId:roomId];
if (room)
{
MXKRoomDataSourceManager* manager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:account.mxSession];
manager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:account.mxSession];
if (manager)
{
roomDataSource = [manager roomDataSourceForRoom:roomId create:YES];
break;
}
break;
}
}
if (roomDataSource == nil)
if (manager == nil)
{
NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: room with id %@ not found", roomId);
}
else
{
NSString* responseText = [responseInfo objectForKey:UIUserNotificationActionResponseTypedTextKey];
if (responseText != nil && responseText.length != 0)
{
NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: sending message to room: %@", roomId);
[roomDataSource sendTextMessage:responseText success:^(NSString* eventId) {} failure:^(NSError* error) {
UILocalNotification* failureNotification = [[UILocalNotification alloc] init];
failureNotification.alertBody = NSLocalizedStringFromTable(@"room_event_failed_to_send", @"Vector", nil);
failureNotification.userInfo = notification.userInfo;
[[UIApplication sharedApplication] scheduleLocalNotification: failureNotification];
NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: error sending text message: %@", error);
}];
}
[manager roomDataSourceForRoom:roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) {
NSString* responseText = [responseInfo objectForKey:UIUserNotificationActionResponseTypedTextKey];
if (responseText != nil && responseText.length != 0)
{
NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: sending message to room: %@", roomId);
[roomDataSource sendTextMessage:responseText success:^(NSString* eventId) {} failure:^(NSError* error) {
UILocalNotification* failureNotification = [[UILocalNotification alloc] init];
failureNotification.alertBody = NSLocalizedStringFromTable(@"room_event_failed_to_send", @"Vector", nil);
failureNotification.userInfo = notification.userInfo;
[[UIApplication sharedApplication] scheduleLocalNotification: failureNotification];
NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: error sending text message: %@", error);
}];
}
completionHandler();
}];
return;
}
}
}
@@ -1308,7 +1313,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: eventsToNotify: %@", eventsToNotify[@(account.mxSession.hash)]);
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: incomingPushEventIds: %@", self.incomingPushEventIds[@(account.mxSession.hash)]);
NSUInteger scheduledNotifications = 0;
__block NSUInteger scheduledNotifications = 0;
// The call invite are handled here only when the callkit is not active.
BOOL isCallKitActive = [MXCallKitAdapter callKitAvailable] && [MXKAppSettings standardAppSettings].isCallKitEnabled;
@@ -1385,54 +1390,56 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
// Prepare the local notification
MXPushRule *rule = eventDict[@"push_rule"];
NSString *notificationBody = [self notificationBodyForEvent:event pushRule:rule inAccount:account];
if (notificationBody)
{
// Printf style escape characters are stripped from the string prior to display;
// to include a percent symbol (%) in the message, use two percent symbols (%%).
notificationBody = [notificationBody stringByReplacingOccurrencesOfString:@"%" withString:@"%%"];
UILocalNotification *eventNotification = [[UILocalNotification alloc] init];
eventNotification.alertBody = notificationBody;
eventNotification.userInfo = @{
@"type": @"full",
@"room_id": event.roomId,
@"event_id": event.eventId,
@"user_id": account.mxCredentials.userId
};
BOOL isNotificationContentShown = !event.isEncrypted || RiotSettings.shared.showDecryptedContentInNotifications;
if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown)
{
eventNotification.category = @"QUICK_REPLY";
}
// Set sound name based on the value provided in action of MXPushRule
for (MXPushRuleAction *action in rule.actions)
[self notificationBodyForEvent:event pushRule:rule inAccount:account onComplete:^(NSString * _Nullable notificationBody) {
if (notificationBody)
{
if (action.actionType == MXPushRuleActionTypeSetTweak)
// Printf style escape characters are stripped from the string prior to display;
// to include a percent symbol (%) in the message, use two percent symbols (%%).
notificationBody = [notificationBody stringByReplacingOccurrencesOfString:@"%" withString:@"%%"];
UILocalNotification *eventNotification = [[UILocalNotification alloc] init];
eventNotification.alertBody = notificationBody;
eventNotification.userInfo = @{
@"type": @"full",
@"room_id": event.roomId,
@"event_id": event.eventId,
@"user_id": account.mxCredentials.userId
};
BOOL isNotificationContentShown = !event.isEncrypted || RiotSettings.shared.showDecryptedContentInNotifications;
if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown)
{
if ([action.parameters[@"set_tweak"] isEqualToString:@"sound"])
eventNotification.category = @"QUICK_REPLY";
}
// Set sound name based on the value provided in action of MXPushRule
for (MXPushRuleAction *action in rule.actions)
{
if (action.actionType == MXPushRuleActionTypeSetTweak)
{
NSString *soundName = action.parameters[@"value"];
if ([soundName isEqualToString:@"default"])
soundName = @"message.mp3";
eventNotification.soundName = soundName;
if ([action.parameters[@"set_tweak"] isEqualToString:@"sound"])
{
NSString *soundName = action.parameters[@"value"];
if ([soundName isEqualToString:@"default"])
soundName = @"message.mp3";
eventNotification.soundName = soundName;
}
}
}
}
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId);
[[UIApplication sharedApplication] scheduleLocalNotification:eventNotification];
scheduledNotifications++;
}
else
{
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationBody. Event id: %@", event.eventId);
}
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId);
[[UIApplication sharedApplication] scheduleLocalNotification:eventNotification];
scheduledNotifications++;
}
else
{
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationBody. Event id: %@", event.eventId);
}
}];
}
}
@@ -1441,120 +1448,130 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
[eventsArray removeAllObjects];
}
- (nullable NSString *)notificationBodyForEvent:(MXEvent *)event pushRule:(MXPushRule*)rule inAccount:(MXKAccount*)account
- (void)notificationBodyForEvent:(MXEvent *)event pushRule:(MXPushRule*)rule inAccount:(MXKAccount*)account onComplete:(void (^)(NSString * _Nullable notificationBody))onComplete;
{
if (!event.content || !event.content.count)
{
NSLog(@"[AppDelegate][Push] notificationBodyForEvent: empty event content");
return nil;
onComplete (nil);
return;
}
MXRoom *room = [account.mxSession roomWithRoomId:event.roomId];
MXRoomState *roomState = room.state;
NSString *notificationBody;
NSString *eventSenderName = [roomState.members memberName:event.sender];
if (event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted)
if (!room)
{
if (room.isMentionsOnly)
NSLog(@"[AppDelegate][Push] notificationBodyForEvent: Unknown room");
onComplete (nil);
return;
}
[room members:^(MXRoomMembers *roomMembers) {
NSString *notificationBody;
NSString *eventSenderName = [roomMembers memberName:event.sender];
if (event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted)
{
// A local notification will be displayed only for highlighted notification.
BOOL isHighlighted = NO;
// Check whether is there an highlight tweak on it
for (MXPushRuleAction *ruleAction in rule.actions)
if (room.isMentionsOnly)
{
if (ruleAction.actionType == MXPushRuleActionTypeSetTweak)
// A local notification will be displayed only for highlighted notification.
BOOL isHighlighted = NO;
// Check whether is there an highlight tweak on it
for (MXPushRuleAction *ruleAction in rule.actions)
{
if ([ruleAction.parameters[@"set_tweak"] isEqualToString:@"highlight"])
if (ruleAction.actionType == MXPushRuleActionTypeSetTweak)
{
// Check the highlight tweak "value"
// If not present, highlight. Else check its value before highlighting
if (nil == ruleAction.parameters[@"value"] || YES == [ruleAction.parameters[@"value"] boolValue])
if ([ruleAction.parameters[@"set_tweak"] isEqualToString:@"highlight"])
{
isHighlighted = YES;
break;
// Check the highlight tweak "value"
// If not present, highlight. Else check its value before highlighting
if (nil == ruleAction.parameters[@"value"] || YES == [ruleAction.parameters[@"value"] boolValue])
{
isHighlighted = YES;
break;
}
}
}
}
if (!isHighlighted)
{
// Ignore this notif.
NSLog(@"[AppDelegate][Push] notificationBodyForEvent: Ignore non highlighted notif in mentions only room");
onComplete(nil);
return;
}
}
if (!isHighlighted)
NSString *msgType = event.content[@"msgtype"];
NSString *content = event.content[@"body"];
if (event.isEncrypted && !RiotSettings.shared.showDecryptedContentInNotifications)
{
// Ignore this notif.
NSLog(@"[AppDelegate][Push] notificationBodyForEvent: Ignore non highlighted notif in mentions only room");
return nil;
// Hide the content
msgType = nil;
}
NSString *roomDisplayName = room.summary.displayname;
// Display the room name only if it is different than the sender name
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
{
if ([msgType isEqualToString:@"m.text"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM_WITH_CONTENT", nil), eventSenderName,roomDisplayName, content];
else if ([msgType isEqualToString:@"m.emote"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER_IN_ROOM", nil), roomDisplayName, eventSenderName, content];
else if ([msgType isEqualToString:@"m.image"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER_IN_ROOM", nil), eventSenderName, content, roomDisplayName];
else
// Encrypted messages falls here
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName];
}
else
{
if ([msgType isEqualToString:@"m.text"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_WITH_CONTENT", nil), eventSenderName, content];
else if ([msgType isEqualToString:@"m.emote"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER", nil), eventSenderName, content];
else if ([msgType isEqualToString:@"m.image"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER", nil), eventSenderName, content];
else
// Encrypted messages falls here
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName];
}
}
NSString *msgType = event.content[@"msgtype"];
NSString *content = event.content[@"body"];
if (event.isEncrypted && !RiotSettings.shared.showDecryptedContentInNotifications)
else if (event.eventType == MXEventTypeCallInvite)
{
// Hide the content
msgType = nil;
}
NSString *roomDisplayName = room.summary.displayname;
// Display the room name only if it is different than the sender name
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
{
if ([msgType isEqualToString:@"m.text"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM_WITH_CONTENT", nil), eventSenderName,roomDisplayName, content];
else if ([msgType isEqualToString:@"m.emote"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER_IN_ROOM", nil), roomDisplayName, eventSenderName, content];
else if ([msgType isEqualToString:@"m.image"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER_IN_ROOM", nil), eventSenderName, content, roomDisplayName];
NSString *sdp = event.content[@"offer"][@"sdp"];
BOOL isVideoCall = [sdp rangeOfString:@"m=video"].location != NSNotFound;
if (!isVideoCall)
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VOICE_CALL_FROM_USER", nil), eventSenderName];
else
// Encrypted messages falls here
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VIDEO_CALL_FROM_USER", nil), eventSenderName];
}
else if (event.eventType == MXEventTypeRoomMember)
{
NSString *roomDisplayName = room.summary.displayname;
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_NAMED_ROOM", nil), eventSenderName, roomDisplayName];
else
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_CHAT", nil), eventSenderName];
}
else if (event.eventType == MXEventTypeSticker)
{
NSString *roomDisplayName = room.summary.displayname;
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName];
}
else
{
if ([msgType isEqualToString:@"m.text"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_WITH_CONTENT", nil), eventSenderName, content];
else if ([msgType isEqualToString:@"m.emote"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER", nil), eventSenderName, content];
else if ([msgType isEqualToString:@"m.image"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER", nil), eventSenderName, content];
else
// Encrypted messages falls here
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName];
}
}
else if (event.eventType == MXEventTypeCallInvite)
{
NSString *sdp = event.content[@"offer"][@"sdp"];
BOOL isVideoCall = [sdp rangeOfString:@"m=video"].location != NSNotFound;
if (!isVideoCall)
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VOICE_CALL_FROM_USER", nil), eventSenderName];
else
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VIDEO_CALL_FROM_USER", nil), eventSenderName];
}
else if (event.eventType == MXEventTypeRoomMember)
{
NSString *roomDisplayName = room.summary.displayname;
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_NAMED_ROOM", nil), eventSenderName, roomDisplayName];
else
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_CHAT", nil), eventSenderName];
}
else if (event.eventType == MXEventTypeSticker)
{
NSString *roomDisplayName = room.summary.displayname;
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName];
else
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName];
}
return notificationBody;
onComplete(notificationBody);
}];
}
/**
@@ -2888,7 +2905,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
- (void)enableLocalNotificationsFromMatrixSession:(MXSession*)mxSession
{
// Prepare listener block.
MXWeakify(self);
MXOnNotification notificationListenerBlock = ^(MXEvent *event, MXRoomState *roomState, MXPushRule *rule) {
MXStrongifyAndReturnIfNil(self);
// Ignore this event if the app is not running in background.
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground)
@@ -2910,7 +2929,11 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
}
// Add it to the list of the events to notify.
[eventsToNotify[@(mxSession.hash)] addObject:@{@"event_id": event.eventId, @"room_id": event.roomId, @"push_rule": rule}];
[self->eventsToNotify[@(mxSession.hash)] addObject:@{
@"event_id": event.eventId,
@"room_id": event.roomId,
@"push_rule": rule
}];
}
else
{
+4 -4
View File
@@ -74,7 +74,7 @@ WidgetManagerErrorCode;
@param room the room to check.
@return a list of widgets.
*/
- (NSArray<Widget*> *)widgetsInRoom:(MXRoom*)room;
- (NSArray<Widget*> *)widgetsInRoom:(MXRoom*)room withRoomState:(MXRoomState*)roomState;
/**
List all active widgets of a given type in a room.
@@ -83,7 +83,7 @@ WidgetManagerErrorCode;
@param room the room to check.
@return a list of widgets.
*/
- (NSArray<Widget*> *)widgetsOfTypes:(NSArray<NSString*>*)widgetTypes inRoom:(MXRoom*)room;
- (NSArray<Widget*> *)widgetsOfTypes:(NSArray<NSString*>*)widgetTypes inRoom:(MXRoom*)room withRoomState:(MXRoomState*)roomState;
/**
List all active widgets of a given type in a room, excluding some types.
@@ -92,7 +92,7 @@ WidgetManagerErrorCode;
@param room the room to check.
@return a list of widgets.
*/
- (NSArray<Widget*> *)widgetsNotOfTypes:(NSArray<NSString*>*)notWidgetTypes inRoom:(MXRoom*)room;
- (NSArray<Widget*> *)widgetsNotOfTypes:(NSArray<NSString*>*)notWidgetTypes inRoom:(MXRoom*)room withRoomState:(MXRoomState*)roomState;
/**
List all widgets of an account.
@@ -157,7 +157,7 @@ WidgetManagerErrorCode;
@return a MXHTTPOperation instance.
*/
- (MXHTTPOperation *)closeWidget:(NSString*)widgetId inRoom:(MXRoom*)room
success:(void (^)())success
success:(void (^)(void))success
failure:(void (^)(NSError *error))failure;
+91 -61
View File
@@ -84,29 +84,29 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
return self;
}
- (NSArray<Widget *> *)widgetsInRoom:(MXRoom *)room
- (NSArray<Widget *> *)widgetsInRoom:(MXRoom*)room withRoomState:(MXRoomState*)roomState
{
return [self widgetsOfTypes:nil inRoom:room];
return [self widgetsOfTypes:nil inRoom:room withRoomState:roomState];
}
- (NSArray<Widget*> *)widgetsOfTypes:(NSArray<NSString*>*)widgetTypes inRoom:(MXRoom*)room;
- (NSArray<Widget*> *)widgetsOfTypes:(NSArray<NSString*>*)widgetTypes inRoom:(MXRoom*)room withRoomState:(MXRoomState*)roomState
{
return [self widgetsOfTypes:widgetTypes butNotTypesOf:nil inRoom:room];
return [self widgetsOfTypes:widgetTypes butNotTypesOf:nil inRoom:room withRoomState:roomState];
}
- (NSArray<Widget*> *)widgetsNotOfTypes:(NSArray<NSString*>*)notWidgetTypes inRoom:(MXRoom*)room
- (NSArray<Widget*> *)widgetsNotOfTypes:(NSArray<NSString*>*)notWidgetTypes inRoom:(MXRoom*)room withRoomState:(MXRoomState*)roomState;
{
return [self widgetsOfTypes:nil butNotTypesOf:notWidgetTypes inRoom:room];
return [self widgetsOfTypes:nil butNotTypesOf:notWidgetTypes inRoom:room withRoomState:roomState];
}
- (NSArray<Widget*> *)widgetsOfTypes:(NSArray<NSString*>*)widgetTypes butNotTypesOf:(NSArray<NSString*>*)notWidgetTypes inRoom:(MXRoom*)room;
- (NSArray<Widget*> *)widgetsOfTypes:(NSArray<NSString*>*)widgetTypes butNotTypesOf:(NSArray<NSString*>*)notWidgetTypes inRoom:(MXRoom*)room withRoomState:(MXRoomState*)roomState;
{
// Widget id -> widget
NSMutableDictionary <NSString*, Widget *> *widgets = [NSMutableDictionary dictionary];
// Get all widgets state events in the room
NSMutableArray<MXEvent*> *widgetEvents = [NSMutableArray arrayWithArray:[room.state stateEventsWithType:kWidgetMatrixEventTypeString]];
[widgetEvents addObjectsFromArray:[room.state stateEventsWithType:kWidgetModularEventTypeString]];
NSMutableArray<MXEvent*> *widgetEvents = [NSMutableArray arrayWithArray:[roomState stateEventsWithType:kWidgetMatrixEventTypeString]];
[widgetEvents addObjectsFromArray:[roomState stateEventsWithType:kWidgetModularEventTypeString]];
// There can be several widgets state events for a same widget but
// only the last one must be considered.
@@ -221,27 +221,38 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
success:(void (^)(Widget *widget))success
failure:(void (^)(NSError *error))failure
{
NSError *permissionError = [self checkWidgetPermissionInRoom:room];
if (permissionError)
{
// Create an empty operation that will be mutated later
MXHTTPOperation *operation = [[MXHTTPOperation alloc] init];
MXWeakify(self);
[self checkWidgetPermissionInRoom:room success:^{
MXStrongifyAndReturnIfNil(self);
NSString *hash = [NSString stringWithFormat:@"%p", room.mxSession];
self->successBlockForWidgetCreation[hash][widgetId] = success;
self->failureBlockForWidgetCreation[hash][widgetId] = failure;
// Send a state event with the widget data
// TODO: This API will be shortly replaced by a pure modular API
// TODO: Move to kWidgetMatrixEventTypeString ("m.widget") type but when?
MXHTTPOperation *operation2 = [room sendStateEventOfType:kWidgetModularEventTypeString
content:widgetContent
stateKey:widgetId
success:nil failure:failure];
if (operation2)
{
[operation mutateTo:operation2];
}
} failure:^(NSError *error) {
if (failure)
{
failure(permissionError);
failure(error);
}
return nil;
}
}];
NSString *hash = [NSString stringWithFormat:@"%p", room.mxSession];
successBlockForWidgetCreation[hash][widgetId] = success;
failureBlockForWidgetCreation[hash][widgetId] = failure;
// Send a state event with the widget data
// TODO: This API will be shortly replaced by a pure modular API
// TODO: Move to kWidgetMatrixEventTypeString ("m.widget") type but when?
return [room sendStateEventOfType:kWidgetModularEventTypeString
content:widgetContent
stateKey:widgetId
success:nil failure:failure];
return operation;
}
@@ -279,31 +290,40 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
failure:failure];
}
- (MXHTTPOperation *)closeWidget:(NSString *)widgetId inRoom:(MXRoom *)room success:(void (^)())success failure:(void (^)(NSError *))failure
- (MXHTTPOperation *)closeWidget:(NSString *)widgetId inRoom:(MXRoom *)room success:(void (^)(void))success failure:(void (^)(NSError *))failure
{
NSError *permissionError = [self checkWidgetPermissionInRoom:room];
if (permissionError)
{
// Create an empty operation that will be mutated later
MXHTTPOperation *operation = [[MXHTTPOperation alloc] init];
[self checkWidgetPermissionInRoom:room success:^{
// Send a state event with an empty content to disable the widget
// TODO: This API will be shortly replaced by a pure modular API
// TODO: Move to kWidgetMatrixEventTypeString ("m.widget") type but when?
MXHTTPOperation *operation2 = [room sendStateEventOfType:kWidgetModularEventTypeString
content:@{}
stateKey:widgetId
success:^(NSString *eventId)
{
if (success)
{
success();
}
} failure:failure];
if (operation2)
{
[operation mutateTo:operation2];
}
} failure:^(NSError *error) {
if (failure)
{
failure(permissionError);
failure(error);
}
return nil;
}
}];
// Send a state event with an empty content to disable the widget
// TODO: This API will be shortly replaced by a pure modular API
// TODO: Move to kWidgetMatrixEventTypeString ("m.widget") type but when?
return [room sendStateEventOfType:kWidgetModularEventTypeString
content:@{}
stateKey:widgetId
success:^(NSString *eventId)
{
if (success)
{
success();
}
} failure:failure];
return operation;
}
/**
@@ -312,25 +332,35 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain";
@param room the room to check.
@return an NSError if the user cannot act on widgets in this room. Else, nil.
*/
- (NSError *)checkWidgetPermissionInRoom:(MXRoom *)room
- (void)checkWidgetPermissionInRoom:(MXRoom *)room success:(dispatch_block_t)success failure:(void (^)(NSError *))failure
{
NSError *error;
[room state:^(MXRoomState *roomState) {
// Check user's power in the room
MXRoomPowerLevels *powerLevels = room.state.powerLevels;
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:room.mxSession.myUser.userId];
NSError *error;
// The user must be able to send state events to manage widgets
if (oneSelfPowerLevel < powerLevels.stateDefault)
{
error = [NSError errorWithDomain:WidgetManagerErrorDomain
code:WidgetManagerErrorCodeNotEnoughPower
userInfo:@{
NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"widget_no_power_to_manage", @"Vector", nil)
}];
}
// Check user's power in the room
MXRoomPowerLevels *powerLevels = roomState.powerLevels;
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:room.mxSession.myUser.userId];
return error;
// The user must be able to send state events to manage widgets
if (oneSelfPowerLevel < powerLevels.stateDefault)
{
error = [NSError errorWithDomain:WidgetManagerErrorDomain
code:WidgetManagerErrorCodeNotEnoughPower
userInfo:@{
NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"widget_no_power_to_manage", @"Vector", nil)
}];
}
if (error)
{
failure(error);
}
else
{
success();
}
}];
}
- (void)addMatrixSession:(MXSession *)mxSession
+28 -20
View File
@@ -79,37 +79,45 @@
- (void)peekInRoom:(void (^)(BOOL succeeded))completion
{
MXWeakify(self);
[_mxSession peekInRoomWithRoomId:_roomId success:^(MXPeekingRoom *peekingRoom) {
MXStrongifyAndReturnIfNil(self);
// Create the room data source
_roomDataSource = [[RoomDataSource alloc] initWithPeekingRoom:peekingRoom andInitialEventId:_eventId];
[_roomDataSource finalizeInitialization];
_roomDataSource.markTimelineInitialEvent = YES;
MXWeakify(self);
[RoomDataSource loadRoomDataSourceWithPeekingRoom:peekingRoom andInitialEventId:self.eventId onComplete:^(id roomDataSource) {
MXStrongifyAndReturnIfNil(self);
_roomName = peekingRoom.summary.displayname;
_roomAvatarUrl = peekingRoom.summary.avatar;
_roomTopic = [MXTools stripNewlineCharacters:peekingRoom.summary.topic];;
_roomAliases = peekingRoom.state.aliases;
// Room members count
// Note that room members presence/activity is not available
_numJoinedMembers = 0;
for (MXRoomMember *mxMember in peekingRoom.state.members.members)
{
if (mxMember.membership == MXMembershipJoin)
self->_roomDataSource = roomDataSource;
[self.roomDataSource finalizeInitialization];
self.roomDataSource.markTimelineInitialEvent = YES;
self->_roomName = peekingRoom.summary.displayname;
self->_roomAvatarUrl = peekingRoom.summary.avatar;
self->_roomTopic = [MXTools stripNewlineCharacters:peekingRoom.summary.topic];;
self->_roomAliases = self.roomDataSource.roomState.aliases;
// Room members count
// Note that room members presence/activity is not available
self->_numJoinedMembers = 0;
for (MXRoomMember *mxMember in self.roomDataSource.roomState.members.members)
{
_numJoinedMembers ++;
if (mxMember.membership == MXMembershipJoin)
{
self->_numJoinedMembers ++;
}
}
}
completion(YES);
completion(YES);
}];
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
_roomName = _roomId;
self->_roomName = self->_roomId;
completion(NO);
}];
}
@@ -569,13 +569,13 @@
if ([self.mxSession roomWithRoomId:roomId])
{
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mxSession];
MXKRoomDataSource *roomDataSource = [roomDataSourceManager roomDataSourceForRoom:roomId create:YES];
// Open this room
RoomViewController *roomViewController = [RoomViewController roomViewController];
roomViewController.showMissedDiscussionsBadge = NO;
[roomViewController displayRoom:roomDataSource];
[self pushViewController:roomViewController];
[roomDataSourceManager roomDataSourceForRoom:roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) {
// Open this room
RoomViewController *roomViewController = [RoomViewController roomViewController];
roomViewController.showMissedDiscussionsBadge = NO;
[roomViewController displayRoom:roomDataSource];
[self pushViewController:roomViewController];
}];
}
else
{
@@ -534,14 +534,15 @@
// Check first if the user already joined this room.
if ([self.mxSession roomWithRoomId:room.roomId])
{
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mxSession];
MXKRoomDataSource *roomDataSource = [roomDataSourceManager roomDataSourceForRoom:room.roomId create:YES];
// Open this room
RoomViewController *roomViewController = [RoomViewController roomViewController];
roomViewController.showMissedDiscussionsBadge = NO;
[roomViewController displayRoom:roomDataSource];
[self pushViewController:roomViewController];
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mxSession];
[roomDataSourceManager roomDataSourceForRoom:room.roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) {
RoomViewController *roomViewController = [RoomViewController roomViewController];
roomViewController.showMissedDiscussionsBadge = NO;
[roomViewController displayRoom:roomDataSource];
[self pushViewController:roomViewController];
}];
}
else
{
@@ -55,21 +55,38 @@
}
date = [searchDataSource.eventFormatter dateStringFromEvent:event withTime:NO];
}
return self;
}
+ (void)cellDataWithSearchResult:(MXSearchResult *)searchResult andSearchDataSource:(MXKSearchDataSource *)searchDataSource onComplete:(void (^)(id<MXKSearchCellDataStoring>))onComplete
{
FilesSearchCellData *cellData = [[self alloc] initWithSearchResult:searchResult andSearchDataSource:searchDataSource];
if (cellData)
{
// Retrieve the sender display name from the current room state
MXRoom *room = [searchDataSource.mxSession roomWithRoomId:roomId];
MXRoom *room = [searchDataSource.mxSession roomWithRoomId:cellData.roomId];
if (room)
{
senderDisplayName = [room.state.members memberName:event.sender];
[room state:^(MXRoomState *roomState) {
cellData->senderDisplayName = [roomState.members memberName:searchResult.result.sender];
cellData->message = cellData->senderDisplayName;
onComplete(cellData);
}];
}
else
{
senderDisplayName = event.sender;
cellData->senderDisplayName = searchResult.result.sender;
cellData->message = cellData->senderDisplayName;
onComplete(cellData);
}
message = senderDisplayName;
}
return self;
else
{
onComplete(nil);
}
}
- (void)setShouldShowRoomDisplayName:(BOOL)shouldShowRoomDisplayName2
@@ -33,9 +33,11 @@
[super destroy];
}
- (void)convertHomeserverResultsIntoCells:(MXSearchRoomEventResults *)roomEventResults
- (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)
@@ -43,48 +45,57 @@
// 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;
UIFont *patternFont = nil;
MXKRoomDataSource *roomDataSource;
if (roomId)
{
dispatch_group_enter(group);
// Check whether the user knows this room to create the room data source if it doesn't exist.
roomDataSource = [roomDataSourceManager roomDataSourceForRoom:roomId create:([self.mxSession roomWithRoomId:roomId])];
if (roomDataSource)
{
// Prepare text font used to highlight the search pattern.
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.room.state andRoomDataSource:roomDataSource];
if (cellData)
[roomDataSourceManager roomDataSourceForRoom:roomId create:[self.mxSession roomWithRoomId:roomId] onComplete:^(MXKRoomDataSource *roomDataSource) {
if (roomDataSource)
{
// Highlight the search pattern
[cellData highlightPatternInTextMessage:self.searchText withForegroundColor:kRiotColorGreen andFont:patternFont];
[cellDataArray insertObject:cellData atIndex:0];
// 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 withForegroundColor:kRiotColorGreen andFont:patternFont];
[self->cellDataArray insertObject:cellData atIndex:0];
}
}
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;
}
}
}
// 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 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
@@ -69,34 +69,24 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
if (!self.URL && !operation)
{
__weak __typeof__(self) weakSelf = self;
[self startActivityIndicator];
// Make sure we have a scalar token
MXWeakify(self);
operation = [[WidgetManager sharedManager] getScalarTokenForMXSession:mxSession success:^(NSString *theScalarToken) {
MXStrongifyAndReturnIfNil(self);
typeof(self) self = weakSelf;
self->operation = nil;
self->scalarToken = theScalarToken;
if (self)
{
self->operation = nil;
scalarToken = theScalarToken;
// Launch the webview on the right modular webapp page
self.URL = [self interfaceUrl];
}
// Launch the webview on the right modular webapp page
self.URL = [self interfaceUrl];
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
typeof(self) self = weakSelf;
if (self)
{
self->operation = nil;
[self stopActivityIndicator];
}
self->operation = nil;
[self stopActivityIndicator];
}];
}
}
@@ -253,26 +243,28 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
#pragma mark - Private methods
- (MXRoom *)roomCheckForRequest:(NSString*)requestId data:(NSDictionary*)requestData
- (void)roomCheckForRequest:(NSString*)requestId data:(NSDictionary*)requestData onComplete:(void (^)(MXRoom *room, MXRoomState *roomState))onComplete
{
MXRoom *room = [mxSession roomWithRoomId:roomId];
if (!room)
if (room)
{
[room state:^(MXRoomState *roomState) {
onComplete(room, roomState);
}];
}
else
{
[self sendLocalisedError:@"widget_integration_room_not_recognised" toRequest:requestId];
}
return room;
}
- (void)inviteUser:(NSString*)userId request:(NSString*)requestId data:(NSDictionary*)requestData
{
NSLog(@"[IntegrationManagerVC] Received request to invite %@ into room %@.", userId, roomId);
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
if (room)
{
MXRoomMember *member = [room.state.members memberWithUserId:userId];
[self roomCheckForRequest:requestId data:requestData onComplete:^(MXRoom *room, MXRoomState *roomState) {
MXRoomMember *member = [roomState.members memberWithUserId:userId];
if (member && member.membership == MXMembershipJoin)
{
[self sendNSObjectResponse:@{
@@ -282,29 +274,22 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
}
else
{
__weak __typeof__(self) weakSelf = self;
MXWeakify(self);
[room inviteUser:userId success:^{
MXStrongifyAndReturnIfNil(self);
typeof(self) self = weakSelf;
if (self)
{
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toRequest:requestId];
}
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toRequest:requestId];
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
typeof(self) self = weakSelf;
if (self)
{
[self sendLocalisedError:@"widget_integration_need_to_be_able_to_invite" toRequest:requestId];
}
[self sendLocalisedError:@"widget_integration_need_to_be_able_to_invite" toRequest:requestId];
}];
}
}
}];
}
- (void)setWidget:(NSString*)requestId data:(NSDictionary*)requestData
@@ -396,9 +381,8 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
else
{
// Room widget
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
if (room)
{
[self roomCheckForRequest:requestId data:requestData onComplete:^(MXRoom *room, MXRoomState *roomState) {
// TODO: Move to kWidgetMatrixEventTypeString ("m.widget") type but when?
[room sendStateEventOfType:kWidgetModularEventTypeString
content:widgetEventContent
@@ -422,51 +406,50 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
[self sendLocalisedError:@"widget_integration_failed_to_send_request" toRequest:requestId];
}
}];
}
}];
}
}
- (void)getWidgets:(NSString*)requestId data:(NSDictionary*)requestData
{
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
NSMutableArray<NSDictionary*> *widgetStateEvents = [NSMutableArray array];
MXWeakify(self);
[self roomCheckForRequest:requestId data:requestData onComplete:^(MXRoom *room, MXRoomState *roomState) {
MXStrongifyAndReturnIfNil(self);
if (room)
{
NSArray<Widget*> *widgets = [[WidgetManager sharedManager] widgetsInRoom:room];
NSMutableArray<NSDictionary*> *widgetStateEvents = [NSMutableArray array];
NSArray<Widget*> *widgets = [[WidgetManager sharedManager] widgetsInRoom:room withRoomState:roomState];
for (Widget *widget in widgets)
{
[widgetStateEvents addObject:widget.widgetEvent.JSONDictionary];
}
}
// Add user widgets (not linked to a specific room)
for (Widget *widget in [[WidgetManager sharedManager] userWidgets:mxSession])
{
[widgetStateEvents addObject:widget.widgetEvent.JSONDictionary];
}
// Add user widgets (not linked to a specific room)
for (Widget *widget in [[WidgetManager sharedManager] userWidgets:self->mxSession])
{
[widgetStateEvents addObject:widget.widgetEvent.JSONDictionary];
}
[self sendNSObjectResponse:widgetStateEvents toRequest:requestId];
[self sendNSObjectResponse:widgetStateEvents toRequest:requestId];
}];
}
- (void)getRoomEncState:(NSString*)requestId data:(NSDictionary*)requestData
{
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
if (room)
{
[self roomCheckForRequest:requestId data:requestData onComplete:^(MXRoom *room, MXRoomState *roomState) {
[self sendBoolResponse:room.summary.isEncrypted toRequest:requestId];
}
}];
}
- (void)canSendEvent:(NSString*)requestId data:(NSDictionary*)requestData
{
NSString *eventType;
BOOL isState = NO;
MXWeakify(self);
[self roomCheckForRequest:requestId data:requestData onComplete:^(MXRoom *room, MXRoomState *roomState) {
MXStrongifyAndReturnIfNil(self);
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
NSString *eventType;
BOOL isState = NO;
if (room)
{
if (room.summary.membership != MXMembershipJoin)
{
[self sendLocalisedError:@"widget_integration_must_be_in_room" toRequest:requestId];
@@ -476,8 +459,8 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
MXJSONModelSetString(eventType, requestData[@"event_type"]);
MXJSONModelSetBoolean(isState, requestData[@"is_state"]);
MXRoomPowerLevels *powerLevels = room.state.powerLevels;
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:mxSession.myUser.userId];
MXRoomPowerLevels *powerLevels = roomState.powerLevels;
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self->mxSession.myUser.userId];
BOOL canSend = NO;
@@ -498,47 +481,42 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
{
[self sendLocalisedError:@"widget_integration_no_permission_in_room" toRequest:requestId];
}
}
}];
}
- (void)getMembershipState:(NSString*)userId request:(NSString*)requestId data:(NSDictionary*)requestData
{
NSLog(@"[IntegrationManagerVC] membership_state of %@ in room %@ requested.", userId, roomId);
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
if (room)
{
MXRoomMember *member = [room.state.members memberWithUserId:userId];
[self roomCheckForRequest:requestId data:requestData onComplete:^(MXRoom *room, MXRoomState *roomState) {
MXRoomMember *member = [roomState.members memberWithUserId:userId];
[self sendNSObjectResponse:member.originalEvent.content toRequest:requestId];
}
}];
}
- (void)getJoinRules:(NSString*)requestId data:(NSDictionary*)requestData
{
NSLog(@"[IntegrationManagerVC] join_rules of %@ requested.", roomId);
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
if (room)
{
MXEvent *event = [room.state stateEventsWithType:kMXEventTypeStringRoomJoinRules].lastObject;
[self roomCheckForRequest:requestId data:requestData onComplete:^(MXRoom *room, MXRoomState *roomState) {
MXEvent *event = [roomState stateEventsWithType:kMXEventTypeStringRoomJoinRules].lastObject;
[self sendNSObjectResponse:event.JSONDictionary toRequest:requestId];
}
}];
}
- (void)setPlumbingState:(NSString*)requestId data:(NSDictionary*)requestData
{
NSLog(@"[IntegrationManagerVC] Received request to set plumbing state to status %@ in room %@.", requestData[@"status"], roomId);
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
if (room)
{
[self roomCheckForRequest:requestId data:requestData onComplete:^(MXRoom *room, MXRoomState *roomState) {
NSString *status;
MXJSONModelSetString(status, requestData[@"status"]);
if (status)
{
__weak __typeof__(self) weakSelf = self;
__weak __typeof__(self) weakSelf = self;
[room sendStateEventOfType:kMXEventTypeStringRoomPlumbing
content:@{
@"status": status
@@ -552,7 +530,7 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toRequest:requestId];
toRequest:requestId];
}
}
failure:^(NSError *error) {
@@ -568,19 +546,18 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
{
NSLog(@"[IntegrationManagerVC] setPlumbingState. Error: Plumbing state status should be a string.");
}
}
}];
}
- (void)getBotOptions:(NSString*)userId request:(NSString*)requestId data:(NSDictionary*)requestData
{
NSLog(@"[IntegrationManagerVC] Received request to get options for bot %@ in room %@", userId, roomId);
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
if (room)
{
[self roomCheckForRequest:requestId data:requestData onComplete:^(MXRoom *room, MXRoomState *roomState) {
NSString *stateKey = [NSString stringWithFormat:@"_%@", userId];
NSArray<MXEvent*> *stateEvents = [room.state stateEventsWithType:kMXEventTypeStringRoomBotOptions];
NSArray<MXEvent*> *stateEvents = [roomState stateEventsWithType:kMXEventTypeStringRoomBotOptions];
MXEvent *botOptionsEvent;
@@ -590,22 +567,21 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
{
if (!botOptionsEvent || stateEvent.ageLocalTs > botOptionsEvent.ageLocalTs)
{
botOptionsEvent = stateEvent;
botOptionsEvent = stateEvent;
}
}
}
[self sendNSObjectResponse:botOptionsEvent.JSONDictionary toRequest:requestId];
}
}];
}
- (void)setBotOptions:(NSString*)userId request:(NSString*)requestId data:(NSDictionary*)requestData
{
NSLog(@"[IntegrationManagerVC] Received request to set options for bot %@ in room %@", userId, roomId);
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
if (room)
{
[self roomCheckForRequest:requestId data:requestData onComplete:^(MXRoom *room, MXRoomState *roomState) {
NSDictionary *content;
MXJSONModelSetDictionary(content, requestData[@"content"]);
@@ -626,7 +602,7 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toRequest:requestId];
toRequest:requestId];
}
}
failure:^(NSError *error) {
@@ -642,16 +618,14 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
{
NSLog(@"[IntegrationManagerVC] setBotOptions. Error: options should be a dict.");
}
}
}];
}
- (void)setBotPower:(NSString*)userId request:(NSString*)requestId data:(NSDictionary*)requestData
{
NSLog(@"[IntegrationManagerVC] Received request to set power level to %@ for bot %@ in room %@.", requestData[@"level"], userId, roomId);
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
if (room)
{
[self roomCheckForRequest:requestId data:requestData onComplete:^(MXRoom *room, MXRoomState *roomState) {
NSInteger level = -1;
MXJSONModelSetInteger(level, requestData[@"level"]);
@@ -667,7 +641,7 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
[self sendNSObjectResponse:@{
@"success": @(YES)
}
toRequest:requestId];
toRequest:requestId];
}
} failure:^(NSError *error) {
@@ -684,17 +658,15 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ";
NSLog(@"[IntegrationManagerVC] setBotPower. Power level must be positive integer.");
[self sendLocalisedError:@"widget_integration_positive_power_level" toRequest:requestId];
}
}
}];
}
- (void)getMembershipCount:(NSString*)requestId data:(NSDictionary*)requestData
{
MXRoom *room = [self roomCheckForRequest:requestId data:requestData];
if (room)
{
[self roomCheckForRequest:requestId data:requestData onComplete:^(MXRoom *room, MXRoomState *roomState) {
NSUInteger membershipCount = room.summary.membersCount.joined;
[self sendIntegerResponse:membershipCount toRequest:requestId];
}
}];
}
@end
@@ -49,64 +49,66 @@
- (void)showInViewController:(MXKViewController *)mxkViewController
{
UIAlertAction *alertAction;
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:mxSession];
[roomDataSourceManager roomDataSourceForRoom:roomId create:NO onComplete:^(MXKRoomDataSource *roomDataSource) {
MXRoom *room = [mxSession roomWithRoomId:roomId];
UIAlertAction *alertAction;
NSArray<Widget*> *widgets = [[WidgetManager sharedManager] widgetsNotOfTypes:@[kWidgetTypeJitsi]
inRoom:room];
NSArray<Widget*> *widgets = [[WidgetManager sharedManager] widgetsNotOfTypes:@[kWidgetTypeJitsi]
inRoom:roomDataSource.room
withRoomState:roomDataSource.roomState];
// List widgets
for (Widget *widget in widgets)
{
alertAction = [UIAlertAction actionWithTitle:widget.name ? widget.name : widget.type
// List widgets
for (Widget *widget in widgets)
{
alertAction = [UIAlertAction actionWithTitle:widget.name ? widget.name : widget.type
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action)
{
// Hide back button title
mxkViewController.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
// Display the widget
[widget widgetUrl:^(NSString * _Nonnull widgetUrl) {
WidgetViewController *widgetVC = [[WidgetViewController alloc] initWithUrl:widgetUrl forWidget:widget];
widgetVC.roomDataSource = roomDataSource;
[mxkViewController.navigationController pushViewController:widgetVC animated:YES];
} failure:^(NSError * _Nonnull error) {
NSLog(@"[WidgetPickerVC] Cannot display widget %@", widget);
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}];
[self.alertController addAction:alertAction];
}
// Link to the integration manager
alertAction = [UIAlertAction actionWithTitle:@"Manage integrations..."
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action)
{
// Hide back button title
mxkViewController.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc] initForMXSession:self->mxSession
inRoom:self->roomId
screen:kIntegrationManagerMainScreen
widgetId:nil];
// Display the widget
[widget widgetUrl:^(NSString * _Nonnull widgetUrl) {
WidgetViewController *widgetVC = [[WidgetViewController alloc] initWithUrl:widgetUrl forWidget:widget];
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:mxSession];
widgetVC.roomDataSource = [roomDataSourceManager roomDataSourceForRoom:roomId create:NO];
[mxkViewController.navigationController pushViewController:widgetVC animated:YES];
} failure:^(NSError * _Nonnull error) {
NSLog(@"[WidgetPickerVC] Cannot display widget %@", widget);
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
[mxkViewController presentViewController:modularVC animated:NO completion:nil];
}];
[_alertController addAction:alertAction];
}
[self.alertController addAction:alertAction];
// Link to the integration manager
alertAction = [UIAlertAction actionWithTitle:@"Manage integrations..."
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action)
{
IntegrationManagerViewController *modularVC = [[IntegrationManagerViewController alloc] initForMXSession:self->mxSession
inRoom:self->roomId
screen:kIntegrationManagerMainScreen
widgetId:nil];
// Cancel
alertAction = [UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleCancel
handler:nil];
[self.alertController addAction:alertAction];
[mxkViewController presentViewController:modularVC animated:NO completion:nil];
}];
[_alertController addAction:alertAction];
// Cancel
alertAction = [UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
style:UIAlertActionStyleCancel
handler:nil];
[_alertController addAction:alertAction];
// And show it
[mxkViewController presentViewController:_alertController animated:YES completion:nil];
}
// And show it
[mxkViewController presentViewController:_alertController animated:YES completion:nil];
}];
}
@end
@@ -271,7 +271,7 @@
for (MXReceiptData* data in receipts)
{
MXRoomMember * roomMember = [self.room.state.members memberWithUserId:data.userId];
MXRoomMember * roomMember = [self.roomState.members memberWithUserId:data.userId];
if (roomMember)
{
[roomMembers addObject:roomMember];
@@ -495,7 +495,7 @@
Widget *jitsiWidget;
// Note: Manage only one jitsi widget at a time for the moment
jitsiWidget = [[WidgetManager sharedManager] widgetsOfTypes:@[kWidgetTypeJitsi] inRoom:self.room].firstObject;
jitsiWidget = [[WidgetManager sharedManager] widgetsOfTypes:@[kWidgetTypeJitsi] inRoom:self.room withRoomState:self.roomState].firstObject;
return jitsiWidget;
}
@@ -367,22 +367,27 @@
self.roomMemberNameLabel.text = self.mxRoomMember.displayname ? self.mxRoomMember.displayname : self.mxRoomMember.userId;
// Update member badge
MXRoomPowerLevels *powerLevels = [self.mxRoom.state powerLevels];
NSInteger powerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxRoomMember.userId];
if (powerLevel >= kRiotRoomAdminLevel)
{
memberTitleView.memberBadge.image = [UIImage imageNamed:@"admin_icon"];
memberTitleView.memberBadge.hidden = NO;
}
else if (powerLevel >= kRiotRoomModeratorLevel)
{
memberTitleView.memberBadge.image = [UIImage imageNamed:@"mod_icon"];
memberTitleView.memberBadge.hidden = NO;
}
else
{
memberTitleView.memberBadge.hidden = YES;
}
MXWeakify(self);
[self.mxRoom state:^(MXRoomState *roomState) {
MXStrongifyAndReturnIfNil(self);
MXRoomPowerLevels *powerLevels = [roomState powerLevels];
NSInteger powerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxRoomMember.userId];
if (powerLevel >= kRiotRoomAdminLevel)
{
self->memberTitleView.memberBadge.image = [UIImage imageNamed:@"admin_icon"];
self->memberTitleView.memberBadge.hidden = NO;
}
else if (powerLevel >= kRiotRoomModeratorLevel)
{
self->memberTitleView.memberBadge.image = [UIImage imageNamed:@"mod_icon"];
self->memberTitleView.memberBadge.hidden = NO;
}
else
{
self->memberTitleView.memberBadge.hidden = YES;
}
}];
NSString* presenceText;
@@ -477,7 +482,7 @@
BOOL isOneself = NO;
// Check user's power level before allowing an action (kick, ban, ...)
MXRoomPowerLevels *powerLevels = [self.mxRoom.state powerLevels];
MXRoomPowerLevels *powerLevels = [self.mxRoom.dangerousSyncState powerLevels];
NSInteger memberPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxRoomMember.userId];
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
@@ -930,7 +935,9 @@
{
case MXKRoomMemberDetailsActionSetDefaultPowerLevel:
{
[self setPowerLevel:self.mxRoom.state.powerLevels.usersDefault promptUser:YES];
[self.mxRoom state:^(MXRoomState *roomState) {
[self setPowerLevel:roomState.powerLevels.usersDefault promptUser:YES];
}];
break;
}
case MXKRoomMemberDetailsActionSetModerator:
@@ -216,8 +216,13 @@
if (membersListener)
{
[self.mxRoom.liveTimeline removeListener:membersListener];
membersListener = nil;
MXWeakify(self);
[self.mxRoom liveTimeline:^(MXEventTimeline *liveTimeline) {
MXStrongifyAndReturnIfNil(self);
[liveTimeline removeListener:self->membersListener];
self->membersListener = nil;
}];
}
if (currentAlert)
@@ -330,142 +335,159 @@
{
// Cancel any pending search
[self searchBarCancelButtonClicked:_searchBarView];
// Remove previous room registration (if any).
if (_mxRoom)
{
// Remove the previous listener
if (leaveRoomNotificationObserver)
// Make sure we can access synchronously to self.mxRoom and mxRoom data
// to avoid race conditions
MXWeakify(self);
[_mxRoom.mxSession preloadRoomsData:@[_mxRoom.roomId, mxRoom.roomId] onComplete:^{
MXStrongifyAndReturnIfNil(self);
// Remove previous room registration (if any).
if (self.mxRoom)
{
[[NSNotificationCenter defaultCenter] removeObserver:leaveRoomNotificationObserver];
leaveRoomNotificationObserver = nil;
}
if (roomDidFlushDataNotificationObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:roomDidFlushDataNotificationObserver];
roomDidFlushDataNotificationObserver = nil;
}
if (membersListener)
{
[_mxRoom.liveTimeline removeListener:membersListener];
membersListener = nil;
}
[self removeMatrixSession:_mxRoom.mxSession];
}
_mxRoom = mxRoom;
if (_mxRoom)
{
_searchBarHeader.hidden = NO;
// Update the current matrix session.
[self addMatrixSession:_mxRoom.mxSession];
// Observe kMXSessionWillLeaveRoomNotification to be notified if the user leaves the current room.
leaveRoomNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionWillLeaveRoomNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
// Check whether the user will leave the room related to the displayed participants
if (notif.object == _mxRoom.mxSession)
// Remove the previous listener
if (self->leaveRoomNotificationObserver)
{
NSString *roomId = notif.userInfo[kMXSessionNotificationRoomIdKey];
if (roomId && [roomId isEqualToString:_mxRoom.roomId])
{
// We remove the current view controller.
[self withdrawViewControllerAnimated:YES completion:nil];
}
[[NSNotificationCenter defaultCenter] removeObserver:self->leaveRoomNotificationObserver];
self->leaveRoomNotificationObserver = nil;
}
}];
// Observe room history flush (sync with limited timeline, or state event redaction)
roomDidFlushDataNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXRoomDidFlushDataNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXRoom *room = notif.object;
if (_mxRoom.mxSession == room.mxSession && [_mxRoom.roomId isEqualToString:room.roomId])
if (self->roomDidFlushDataNotificationObserver)
{
// The existing room history has been flushed during server sync. Take into account the updated room members list.
[self refreshParticipantsFromRoomMembers];
[self refreshTableView];
[[NSNotificationCenter defaultCenter] removeObserver:self->roomDidFlushDataNotificationObserver];
self->roomDidFlushDataNotificationObserver = nil;
}
}];
// Register a listener for events that concern room members
NSArray *mxMembersEvents = @[kMXEventTypeStringRoomMember, kMXEventTypeStringRoomThirdPartyInvite, kMXEventTypeStringRoomPowerLevels];
membersListener = [_mxRoom.liveTimeline listenToEventsOfTypes:mxMembersEvents onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) {
// Consider only live event
if (direction == MXTimelineDirectionForwards)
if (self->membersListener)
{
switch (event.eventType)
MXWeakify(self);
[self.mxRoom liveTimeline:^(MXEventTimeline *liveTimeline) {
MXStrongifyAndReturnIfNil(self);
[liveTimeline removeListener:self->membersListener];
self->membersListener = nil;
}];
}
[self removeMatrixSession:self.mxRoom.mxSession];
}
self->_mxRoom = mxRoom;
if (self.mxRoom)
{
self.searchBarHeader.hidden = NO;
// Update the current matrix session.
[self addMatrixSession:self.mxRoom.mxSession];
// Observe kMXSessionWillLeaveRoomNotification to be notified if the user leaves the current room.
self->leaveRoomNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionWillLeaveRoomNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
// Check whether the user will leave the room related to the displayed participants
if (notif.object == self.mxRoom.mxSession)
{
case MXEventTypeRoomMember:
NSString *roomId = notif.userInfo[kMXSessionNotificationRoomIdKey];
if (roomId && [roomId isEqualToString:self.mxRoom.roomId])
{
// Take into account updated member
// Ignore here change related to the current user (this change is handled by leaveRoomNotificationObserver)
if ([event.stateKey isEqualToString:self.mxRoom.mxSession.myUser.userId] == NO)
// We remove the current view controller.
[self withdrawViewControllerAnimated:YES completion:nil];
}
}
}];
// Observe room history flush (sync with limited timeline, or state event redaction)
self->roomDidFlushDataNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXRoomDidFlushDataNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXRoom *room = notif.object;
if (self.mxRoom.mxSession == room.mxSession && [self.mxRoom.roomId isEqualToString:room.roomId])
{
// The existing room history has been flushed during server sync. Take into account the updated room members list.
[self refreshParticipantsFromRoomMembers];
[self refreshTableView];
}
}];
// Register a listener for events that concern room members
NSArray *mxMembersEvents = @[kMXEventTypeStringRoomMember, kMXEventTypeStringRoomThirdPartyInvite, kMXEventTypeStringRoomPowerLevels];
MXWeakify(self);
[self.mxRoom liveTimeline:^(MXEventTimeline *liveTimeline) {
MXStrongifyAndReturnIfNil(self);
self->membersListener = [liveTimeline listenToEventsOfTypes:mxMembersEvents onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) {
// Consider only live event
if (direction == MXTimelineDirectionForwards)
{
switch (event.eventType)
{
MXRoomMember *mxMember = [self.mxRoom.state.members memberWithUserId:event.stateKey];
if (mxMember)
case MXEventTypeRoomMember:
{
// Remove previous occurrence of this member (if any)
[self removeParticipantByKey:mxMember.userId];
// If any, remove 3pid invite corresponding to this room member
if (mxMember.thirdPartyInviteToken)
// Take into account updated member
// Ignore here change related to the current user (this change is handled by leaveRoomNotificationObserver)
if ([event.stateKey isEqualToString:self.mxRoom.mxSession.myUser.userId] == NO)
{
[self removeParticipantByKey:mxMember.thirdPartyInviteToken];
MXRoomMember *mxMember = [liveTimeline.state.members memberWithUserId:event.stateKey];
if (mxMember)
{
// Remove previous occurrence of this member (if any)
[self removeParticipantByKey:mxMember.userId];
// If any, remove 3pid invite corresponding to this room member
if (mxMember.thirdPartyInviteToken)
{
[self removeParticipantByKey:mxMember.thirdPartyInviteToken];
}
[self handleRoomMember:mxMember];
[self finalizeParticipantsList:liveTimeline.state];
[self refreshTableView];
}
}
[self handleRoomMember:mxMember];
[self finalizeParticipantsList];
[self refreshTableView];
break;
}
case MXEventTypeRoomThirdPartyInvite:
{
MXRoomThirdPartyInvite *thirdPartyInvite = [liveTimeline.state thirdPartyInviteWithToken:event.stateKey];
if (thirdPartyInvite)
{
[self addRoomThirdPartyInviteToParticipants:thirdPartyInvite roomState:liveTimeline.state];
[self finalizeParticipantsList:liveTimeline.state];
[self refreshTableView];
}
break;
}
case MXEventTypeRoomPowerLevels:
{
[self refreshParticipantsFromRoomMembers];
[self refreshTableView];
break;
}
default:
break;
}
break;
}
case MXEventTypeRoomThirdPartyInvite:
{
MXRoomThirdPartyInvite *thirdPartyInvite = [self.mxRoom.state thirdPartyInviteWithToken:event.stateKey];
if (thirdPartyInvite)
{
[self addRoomThirdPartyInviteToParticipants:thirdPartyInvite];
[self finalizeParticipantsList];
[self refreshTableView];
}
break;
}
case MXEventTypeRoomPowerLevels:
{
[self refreshParticipantsFromRoomMembers];
[self refreshTableView];
break;
}
default:
break;
}
}
}];
}
else
{
// Search bar header is hidden when no room is provided
_searchBarHeader.hidden = YES;
}
// Refresh the members list.
[self refreshParticipantsFromRoomMembers];
[self refreshTableView];
}];
}];
}
else
{
// Search bar header is hidden when no room is provided
self.searchBarHeader.hidden = YES;
}
// Refresh the members list.
[self refreshParticipantsFromRoomMembers];
[self refreshTableView];
}];
}
- (void)setEnableMention:(BOOL)enableMention
@@ -686,36 +708,41 @@
if (self.mxRoom)
{
// Retrieve the current members from the room state
NSArray *members = [self.mxRoom.state.members membersWithoutConferenceUser];
NSString *userId = self.mxRoom.mxSession.myUser.userId;
NSArray *roomThirdPartyInvites = self.mxRoom.state.thirdPartyInvites;
for (MXRoomMember *mxMember in members)
{
// Update the current participants list
if ([mxMember.userId isEqualToString:userId])
MXWeakify(self);
[self.mxRoom state:^(MXRoomState *roomState) {
MXStrongifyAndReturnIfNil(self);
NSArray *members = [roomState.members membersWithoutConferenceUser];
NSString *userId = self.mxRoom.mxSession.myUser.userId;
NSArray *roomThirdPartyInvites = roomState.thirdPartyInvites;
for (MXRoomMember *mxMember in members)
{
if (mxMember.membership == MXMembershipJoin || mxMember.membership == MXMembershipInvite)
// Update the current participants list
if ([mxMember.userId isEqualToString:userId])
{
// The user is in this room
NSString *displayName = NSLocalizedStringFromTable(@"you", @"Vector", nil);
userParticipant = [[Contact alloc] initMatrixContactWithDisplayName:displayName andMatrixID:userId];
userParticipant.mxMember = [self.mxRoom.state.members memberWithUserId:userId];
if (mxMember.membership == MXMembershipJoin || mxMember.membership == MXMembershipInvite)
{
// The user is in this room
NSString *displayName = NSLocalizedStringFromTable(@"you", @"Vector", nil);
self->userParticipant = [[Contact alloc] initMatrixContactWithDisplayName:displayName andMatrixID:userId];
self->userParticipant.mxMember = [roomState.members memberWithUserId:userId];
}
}
else
{
[self handleRoomMember:mxMember];
}
}
else
for (MXRoomThirdPartyInvite *roomThirdPartyInvite in roomThirdPartyInvites)
{
[self handleRoomMember:mxMember];
[self addRoomThirdPartyInviteToParticipants:roomThirdPartyInvite roomState:roomState];
}
}
for (MXRoomThirdPartyInvite *roomThirdPartyInvite in roomThirdPartyInvites)
{
[self addRoomThirdPartyInviteToParticipants:roomThirdPartyInvite];
}
[self finalizeParticipantsList];
[self finalizeParticipantsList:roomState];
}];
}
}
@@ -766,10 +793,10 @@
}
}
- (void)addRoomThirdPartyInviteToParticipants:(MXRoomThirdPartyInvite*)roomThirdPartyInvite
- (void)addRoomThirdPartyInviteToParticipants:(MXRoomThirdPartyInvite*)roomThirdPartyInvite roomState:(MXRoomState*)roomState
{
// If the homeserver has converted the 3pid invite into a room member, do no show it
if (![self.mxRoom.state memberWithThirdPartyInviteToken:roomThirdPartyInvite.token])
if (![roomState memberWithThirdPartyInviteToken:roomThirdPartyInvite.token])
{
Contact *contact = [[Contact alloc] initMatrixContactWithDisplayName:roomThirdPartyInvite.displayname andMatrixID:nil];
contact.isThirdPartyInvite = YES;
@@ -819,7 +846,7 @@
}
}
- (void)finalizeParticipantsList
- (void)finalizeParticipantsList:(MXRoomState*)roomState
{
// Sort contacts by last active, with "active now" first.
// ...and then by power
@@ -846,7 +873,7 @@
if (userA.currentlyActive && userB.currentlyActive)
{
// Order first by power levels (admins then moderators then others)
MXRoomPowerLevels *powerLevels = [self.mxRoom.state powerLevels];
MXRoomPowerLevels *powerLevels = [roomState powerLevels];
NSInteger powerLevelA = [powerLevels powerLevelOfUserWithUserID:contactA.mxMember.userId];
NSInteger powerLevelB = [powerLevels powerLevelOfUserWithUserID:contactB.mxMember.userId];
@@ -1094,8 +1121,10 @@
if (contact.mxMember)
{
MXRoomState *roomState = self.mxRoom.dangerousSyncState;
// Update member badge
MXRoomPowerLevels *powerLevels = [self.mxRoom.state powerLevels];
MXRoomPowerLevels *powerLevels = [roomState powerLevels];
NSInteger powerLevel = [powerLevels powerLevelOfUserWithUserID:contact.mxMember.userId];
if (powerLevel >= kRiotRoomAdminLevel)
{
@@ -1111,7 +1140,7 @@
// Update the contact display name by considering the current room state.
if (contact.mxMember.userId)
{
participantCell.contactDisplayNameLabel.text = [self.mxRoom.state.members memberName:contact.mxMember.userId];
participantCell.contactDisplayNameLabel.text = [roomState.members memberName:contact.mxMember.userId];
}
}
}
+92 -73
View File
@@ -545,9 +545,13 @@
// Observe missed notifications
mxRoomSummaryDidChangeObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXRoomSummaryDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
[self refreshMissedDiscussionsCount:NO];
MXRoomSummary *roomSummary = notif.object;
if ([roomSummary.roomId isEqualToString:self.roomDataSource.roomId])
{
[self refreshMissedDiscussionsCount:NO];
}
}];
[self refreshMissedDiscussionsCount:YES];
@@ -931,9 +935,9 @@
Class roomInputToolbarViewClass = RoomInputToolbarView.class;
// Check the user has enough power to post message
if (self.roomDataSource.room.state)
if (self.roomDataSource.roomState)
{
MXRoomPowerLevels *powerLevels = self.roomDataSource.room.state.powerLevels;
MXRoomPowerLevels *powerLevels = self.roomDataSource.roomState.powerLevels;
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
BOOL canSend = (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsMessage:kMXEventTypeStringRoomMessage]);
@@ -1894,7 +1898,7 @@
{
if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnAvatarView])
{
selectedRoomMember = [self.roomDataSource.room.state.members memberWithUserId:userInfo[kMXKRoomBubbleCellUserIdKey]];
selectedRoomMember = [self.roomDataSource.roomState.members memberWithUserId:userInfo[kMXKRoomBubbleCellUserIdKey]];
if (selectedRoomMember)
{
[self performSegueWithIdentifier:@"showMemberDetails" sender:self];
@@ -1903,7 +1907,7 @@
else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellLongPressOnAvatarView])
{
// Add the member display name in text input
MXRoomMember *roomMember = [self.roomDataSource.room.state.members memberWithUserId:userInfo[kMXKRoomBubbleCellUserIdKey]];
MXRoomMember *roomMember = [self.roomDataSource.roomState.members memberWithUserId:userInfo[kMXKRoomBubbleCellUserIdKey]];
if (roomMember)
{
[self mention:roomMember];
@@ -2656,7 +2660,7 @@
NSString *userId = absoluteURLString;
MXRoomMember* member = [self.roomDataSource.room.state.members memberWithUserId:userId];
MXRoomMember* member = [self.roomDataSource.roomState.members memberWithUserId:userId];
if (member)
{
// Use the room member detail VC for room members
@@ -2765,7 +2769,11 @@
// Files tab
[titles addObject: NSLocalizedStringFromTable(@"room_details_files", @"Vector", nil)];
RoomFilesViewController *roomFilesViewController = [RoomFilesViewController roomViewController];
MXKRoomDataSource *roomFilesDataSource = [[MXKRoomDataSource alloc] initWithRoomId:roomId andMatrixSession:session];
// @TODO (async-state): This call should be synchronous. Every thing will be fine
__block MXKRoomDataSource *roomFilesDataSource;
[MXKRoomDataSource loadRoomDataSourceWithRoomId:roomId andMatrixSession:session onComplete:^(id roomDataSource) {
roomFilesDataSource = roomDataSource;
}];
roomFilesDataSource.filterMessagesWithURL = YES;
[roomFilesDataSource finalizeInitialization];
// Give the data source ownership to the room files view controller.
@@ -2858,25 +2866,18 @@
contactsDataSource.contactCellAccessoryImage = [UIImage imageNamed:@"plus_icon"];
// List all the participants matrix user id to ignore them during the contacts search.
MXSession* session = self.roomDataSource.mxSession;
NSString* roomId = self.roomDataSource.roomId;
MXRoom *room = [session roomWithRoomId:roomId];
if (room)
NSArray *members = [self.roomDataSource.roomState.members membersWithoutConferenceUser];
for (MXRoomMember *mxMember in members)
{
NSArray *members = [room.state.members membersWithoutConferenceUser];
for (MXRoomMember *mxMember in members)
// Check his status
if (mxMember.membership == MXMembershipJoin || mxMember.membership == MXMembershipInvite)
{
// Check his status
if (mxMember.membership == MXMembershipJoin || mxMember.membership == MXMembershipInvite)
{
// Create the contact related to this member
MXKContact *contact = [[MXKContact alloc] initMatrixContactWithDisplayName:mxMember.displayname andMatrixID:mxMember.userId];
[contactsDataSource.ignoredContactsByMatrixId setObject:contact forKey:mxMember.userId];
}
// Create the contact related to this member
MXKContact *contact = [[MXKContact alloc] initMatrixContactWithDisplayName:mxMember.displayname andMatrixID:mxMember.userId];
[contactsDataSource.ignoredContactsByMatrixId setObject:contact forKey:mxMember.userId];
}
}
[contactsPickerViewController showSearch:YES];
contactsPickerViewController.searchBar.placeholder = NSLocalizedStringFromTable(@"room_participants_invite_another_user", @"Vector", nil);
@@ -3067,7 +3068,7 @@
// In case of conference call, check that the user has enough power level
else if (self.roomDataSource.room.summary.membersCount.joined > 2 &&
![MXCallManager canPlaceConferenceCallInRoom:self.roomDataSource.room])
![MXCallManager canPlaceConferenceCallInRoom:self.roomDataSource.room roomState:self.roomDataSource.roomState])
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
@@ -3195,18 +3196,21 @@
[self showExpandedHeader:NO];
// Dismiss potential keyboard.
[self dismissKeyboard];
MXKRoomDataSource *roomDataSource;
// Jump to the last unread event by using a temporary room data source initialized with the last unread event id.
roomDataSource = [[RoomDataSource alloc] initWithRoomId:self.roomDataSource.roomId initialEventId:self.roomDataSource.room.accountData.readMarkerEventId andMatrixSession:self.mainSession];
[roomDataSource finalizeInitialization];
// Center the bubbles table content on the bottom of the read marker event in order to display correctly the read marker view.
self.centerBubblesTableViewContentOnTheInitialEventBottom = YES;
[self displayRoom:roomDataSource];
// Give the data source ownership to the room view controller.
self.hasRoomDataSourceOwnership = YES;
MXWeakify(self);
[RoomDataSource loadRoomDataSourceWithRoomId:self.roomDataSource.roomId initialEventId:self.roomDataSource.room.accountData.readMarkerEventId andMatrixSession:self.mainSession onComplete:^(id roomDataSource) {
MXStrongifyAndReturnIfNil(self);
[roomDataSource finalizeInitialization];
// Center the bubbles table content on the bottom of the read marker event in order to display correctly the read marker view.
self.centerBubblesTableViewContentOnTheInitialEventBottom = YES;
[self displayRoom:roomDataSource];
// Give the data source ownership to the room view controller.
self.hasRoomDataSourceOwnership = YES;
}];
}
else if (sender == self.resetReadMarkerButton)
{
@@ -3474,13 +3478,17 @@
// If an event was specified, replace the datasource by a non live datasource showing the event
if (eventId)
{
RoomDataSource *roomDataSource = [[RoomDataSource alloc] initWithRoomId:self.roomDataSource.roomId initialEventId:eventId andMatrixSession:self.mainSession];
[roomDataSource finalizeInitialization];
roomDataSource.markTimelineInitialEvent = YES;
[self displayRoom:roomDataSource];
self.hasRoomDataSourceOwnership = YES;
MXWeakify(self);
[RoomDataSource loadRoomDataSourceWithRoomId:self.roomDataSource.roomId initialEventId:eventId andMatrixSession:self.mainSession onComplete:^(id roomDataSource) {
MXStrongifyAndReturnIfNil(self);
[roomDataSource finalizeInitialization];
((RoomDataSource*)roomDataSource).markTimelineInitialEvent = YES;
[self displayRoom:roomDataSource];
self.hasRoomDataSourceOwnership = YES;
}];
}
else
{
@@ -3555,8 +3563,13 @@
// Remove the previous live listener
if (typingNotifListener)
{
[self.roomDataSource.room.liveTimeline removeListener:typingNotifListener];
typingNotifListener = nil;
MXWeakify(self);
[self.roomDataSource.room liveTimeline:^(MXEventTimeline *liveTimeline) {
MXStrongifyAndReturnIfNil(self);
[liveTimeline removeListener:self->typingNotifListener];
self->typingNotifListener = nil;
}];
}
}
@@ -3568,8 +3581,10 @@
if (self.roomDataSource)
{
// Add typing notification listener
typingNotifListener = [self.roomDataSource.room.liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringTypingNotification] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) {
MXWeakify(self);
self->typingNotifListener = [self.roomDataSource.room listenToEventsOfTypes:@[kMXEventTypeStringTypingNotification] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) {
MXStrongifyAndReturnIfNil(self);
// Handle only live events
if (direction == MXTimelineDirectionForwards)
{
@@ -3581,17 +3596,16 @@
{
[typingUsers removeObjectAtIndex:index];
}
// Ignore this notification if both arrays are empty
if (currentTypingUsers.count || typingUsers.count)
if (self->currentTypingUsers.count || typingUsers.count)
{
currentTypingUsers = typingUsers;
self->currentTypingUsers = typingUsers;
[self refreshActivitiesViewDisplay];
}
}
}];
// Retrieve the current typing users list
NSMutableArray *typingUsers = [NSMutableArray arrayWithArray:self.roomDataSource.room.typingUsers];
// Remove typing info for the current user
@@ -3621,7 +3635,7 @@
{
NSString* name = [currentTypingUsers objectAtIndex:i];
MXRoomMember* member = [self.roomDataSource.room.state.members memberWithUserId:name];
MXRoomMember* member = [self.roomDataSource.roomState.members memberWithUserId:name];
if (member && member.displayname.length)
{
@@ -3754,7 +3768,8 @@
- (NSUInteger)widgetsCount:(BOOL)includeUserWidgets
{
NSUInteger widgetsCount = [[WidgetManager sharedManager] widgetsNotOfTypes:@[kWidgetTypeJitsi]
inRoom:self.roomDataSource.room].count;
inRoom:self.roomDataSource.room
withRoomState:self.roomDataSource.roomState].count;
if (includeUserWidgets)
{
widgetsCount += [[WidgetManager sharedManager] userWidgets:self.roomDataSource.room.mxSession].count;
@@ -3783,7 +3798,7 @@
{
[roomActivitiesView displayNetworkErrorNotification:NSLocalizedStringFromTable(@"room_offline_notification", @"Vector", nil)];
}
else if (customizedRoomDataSource.room.state.isOngoingConferenceCall)
else if (customizedRoomDataSource.roomState.isOngoingConferenceCall)
{
// Show the "Ongoing conference call" banner only if the user is not in the conference
MXCall *callInRoom = [self.roomDataSource.mxSession.callManager callInRoom:self.roomDataSource.roomId];
@@ -3928,24 +3943,28 @@
{
// Switch back to the room live timeline managed by MXKRoomDataSourceManager
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mainSession];
MXKRoomDataSource *roomDataSource = [roomDataSourceManager roomDataSourceForRoom:self.roomDataSource.roomId create:YES];
// Scroll to bottom the bubble history on the display refresh.
shouldScrollToBottomOnTableRefresh = YES;
[self displayRoom:roomDataSource];
// The room view controller do not have here the data source ownership.
self.hasRoomDataSourceOwnership = NO;
[self refreshActivitiesViewDisplay];
[self refreshJumpToLastUnreadBannerDisplay];
if (self.saveProgressTextInput)
{
// Restore the potential message partially typed before jump to last unread messages.
self.inputToolbarView.textMessage = roomDataSource.partialTextMessage;
}
MXWeakify(self);
[roomDataSourceManager roomDataSourceForRoom:self.roomDataSource.roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) {
MXStrongifyAndReturnIfNil(self);
// Scroll to bottom the bubble history on the display refresh.
self->shouldScrollToBottomOnTableRefresh = YES;
[self displayRoom:roomDataSource];
// The room view controller do not have here the data source ownership.
self.hasRoomDataSourceOwnership = NO;
[self refreshActivitiesViewDisplay];
[self refreshJumpToLastUnreadBannerDisplay];
if (self.saveProgressTextInput)
{
// Restore the potential message partially typed before jump to last unread messages.
self.inputToolbarView.textMessage = roomDataSource.partialTextMessage;
}
}];
}
}
@@ -52,7 +52,7 @@
[super destroy];
}
- (void)convertHomeserverResultsIntoCells:(MXSearchRoomEventResults *)roomEventResults
- (void)convertHomeserverResultsIntoCells:(MXSearchRoomEventResults *)roomEventResults onComplete:(dispatch_block_t)onComplete
{
// Prepare text font used to highlight the search pattern.
UIFont *patternFont = [roomDataSource.eventFormatter bingTextFont];
@@ -64,7 +64,7 @@
// 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.room.state andRoomDataSource:roomDataSource];
RoomBubbleCellData *cellData = [[RoomBubbleCellData alloc] initWithEvent:result.result andRoomState:roomDataSource.roomState andRoomDataSource:roomDataSource];
if (cellData)
{
// Highlight the search pattern
@@ -73,6 +73,8 @@
[cellDataArray insertObject:cellData atIndex:0];
}
}
onComplete();
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
@@ -290,14 +290,19 @@
if (selectedSearchEvent)
{
RoomViewController *roomViewController = segue.destinationViewController;
RoomDataSource *roomDataSource = [[RoomDataSource alloc] initWithRoomId:selectedSearchEvent.roomId initialEventId:selectedSearchEvent.eventId andMatrixSession:selectedSearchEventSession];
[roomDataSource finalizeInitialization];
roomDataSource.markTimelineInitialEvent = YES;
[roomViewController displayRoom:roomDataSource];
roomViewController.hasRoomDataSourceOwnership = YES;
roomViewController.navigationItem.leftItemsSupplementBackButton = YES;
[RoomDataSource loadRoomDataSourceWithRoomId:selectedSearchEvent.roomId
initialEventId:selectedSearchEvent.eventId
andMatrixSession:selectedSearchEventSession onComplete:^(RoomDataSource *roomDataSource) {
[roomDataSource finalizeInitialization];
roomDataSource.markTimelineInitialEvent = YES;
[roomViewController displayRoom:roomDataSource];
roomViewController.hasRoomDataSourceOwnership = YES;
roomViewController.navigationItem.leftItemsSupplementBackButton = YES;
}];
}
// Hide back button title
@@ -190,8 +190,8 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
[super initWithSession:session andRoomId:roomId];
// Add an additional listener to update banned users
extraEventsListener = [mxRoom.liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMember] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) {
self->extraEventsListener = [mxRoom listenToEventsOfTypes:@[kMXEventTypeStringRoomMember] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) {
if (direction == MXTimelineDirectionForwards)
{
[self updateRoomState:roomState];
@@ -399,8 +399,13 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
if (extraEventsListener)
{
[mxRoom.liveTimeline removeListener:extraEventsListener];
extraEventsListener = nil;
MXWeakify(self);
[mxRoom liveTimeline:^(MXEventTimeline *liveTimeline) {
MXStrongifyAndReturnIfNil(self);
[liveTimeline removeListener:self->extraEventsListener];
self->extraEventsListener = nil;
}];
}
[super destroy];
@@ -467,7 +472,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
if (!eventTypeForSelectedField)
return;
MXRoomPowerLevels *powerLevels = [mxRoom.state powerLevels];
MXRoomPowerLevels *powerLevels = [mxRoomState powerLevels];
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
if (oneSelfPowerLevel < [powerLevels minimumPowerLevelForSendingEventAsStateEvent:eventTypeForSelectedField])
@@ -1963,7 +1968,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
if (self.mainSession)
{
// Check user's power level to know whether the user is allowed to add room alias
MXRoomPowerLevels *powerLevels = [mxRoom.state powerLevels];
MXRoomPowerLevels *powerLevels = [mxRoomState powerLevels];
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
if (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomAliases])
@@ -1981,7 +1986,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
if (self.mainSession)
{
// Check user's power level to know whether the user is allowed to add communities to this room
MXRoomPowerLevels *powerLevels = [mxRoom.state powerLevels];
MXRoomPowerLevels *powerLevels = [mxRoomState powerLevels];
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
if (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomRelatedGroups])
@@ -2118,7 +2123,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
UITableViewCell* cell;
// Check user's power level to know which settings are editable.
MXRoomPowerLevels *powerLevels = [mxRoom.state powerLevels];
MXRoomPowerLevels *powerLevels = [mxRoomState powerLevels];
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
// general settings
@@ -2732,7 +2737,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
else
{
// Check user's power level to know whether the user is allowed to turn on the encryption mode
MXRoomPowerLevels *powerLevels = [mxRoom.state powerLevels];
MXRoomPowerLevels *powerLevels = [mxRoomState powerLevels];
NSInteger oneSelfPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
if (oneSelfPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomEncryption])
@@ -65,46 +65,51 @@
self.roomTopic.text = [MXTools stripNewlineCharacters:self.mxRoom.summary.topic];
// Compute active members count
NSArray *members = [self.mxRoom.state.members membersWithMembership:MXMembershipJoin includeConferenceUser:NO];
NSUInteger activeCount = 0;
NSUInteger memberCount = 0;
for (MXRoomMember *mxMember in members)
{
memberCount ++;
MXWeakify(self);
[self.mxRoom members:^(MXRoomMembers *roomMembers) {
MXStrongifyAndReturnIfNil(self);
// Get the user that corresponds to this member
MXUser *user = [self.mxRoom.mxSession userWithUserId:mxMember.userId];
// existing user ?
if (user && user.presence == MXPresenceOnline)
NSArray *members = [roomMembers membersWithMembership:MXMembershipJoin includeConferenceUser:NO];
NSUInteger activeCount = 0;
NSUInteger memberCount = 0;
for (MXRoomMember *mxMember in members)
{
activeCount ++;
}
}
memberCount ++;
if (memberCount)
{
// Check whether the logged in user is alone in this room
if (memberCount == 1 && self.mxRoom.summary.membership == MXMembershipJoin)
{
self.roomMembers.text = NSLocalizedStringFromTable(@"room_title_invite_members", @"Vector", nil);
}
else
{
if (activeCount > 1)
// Get the user that corresponds to this member
MXUser *user = [self.mxRoom.mxSession userWithUserId:mxMember.userId];
// existing user ?
if (user && user.presence == MXPresenceOnline)
{
self.roomMembers.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_title_multiple_active_members", @"Vector", nil), @(activeCount), @(memberCount)];
activeCount ++;
}
}
if (memberCount)
{
// Check whether the logged in user is alone in this room
if (memberCount == 1 && self.mxRoom.summary.membership == MXMembershipJoin)
{
self.roomMembers.text = NSLocalizedStringFromTable(@"room_title_invite_members", @"Vector", nil);
}
else
{
self.roomMembers.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_title_one_active_member", @"Vector", nil), @(activeCount), @(memberCount)];
if (activeCount > 1)
{
self.roomMembers.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_title_multiple_active_members", @"Vector", nil), @(activeCount), @(memberCount)];
}
else
{
self.roomMembers.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_title_one_active_member", @"Vector", nil), @(activeCount), @(memberCount)];
}
}
}
}
else
{
// Should not happen
self.roomMembers.text = nil;
}
else
{
// Should not happen
self.roomMembers.text = nil;
}
}];
}
else
{
@@ -182,51 +182,56 @@
self.roomTopic.text = [MXTools stripNewlineCharacters:self.mxRoom.summary.topic];
// Compute active members count, and look for the inviter
NSArray *members = self.mxRoom.state.members.members;
NSUInteger activeCount = 0;
NSUInteger memberCount = 0;
NSString *inviter = nil;
for (MXRoomMember *mxMember in members)
{
if (mxMember.membership == MXMembershipJoin)
MXWeakify(self);
[self.mxRoom members:^(MXRoomMembers *roomMembers) {
MXStrongifyAndReturnIfNil(self);
NSArray *members = roomMembers.members;
NSUInteger activeCount = 0;
NSUInteger memberCount = 0;
NSString *inviter = nil;
for (MXRoomMember *mxMember in members)
{
memberCount ++;
// Get the user that corresponds to this member
MXUser *user = [self.mxRoom.mxSession userWithUserId:mxMember.userId];
// existing user ?
if (user && user.presence == MXPresenceOnline)
if (mxMember.membership == MXMembershipJoin)
{
activeCount ++;
memberCount ++;
// Get the user that corresponds to this member
MXUser *user = [self.mxRoom.mxSession userWithUserId:mxMember.userId];
// existing user ?
if (user && user.presence == MXPresenceOnline)
{
activeCount ++;
}
// Presently only one member is available from invited room data
// This is the inviter
inviter = mxMember.displayname.length ? mxMember.displayname : mxMember.userId;
}
// Presently only one member is available from invited room data
// This is the inviter
inviter = mxMember.displayname.length ? mxMember.displayname : mxMember.userId;
}
}
// FIXME: Display members status when it will be available
self.roomMembers.text = nil;
// if (memberCount)
// {
// if (activeCount > 1)
// {
// self.roomMembers.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_title_multiple_active_members", @"Vector", nil), @(activeCount), @(memberCount)];
// }
// else
// {
// self.roomMembers.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_title_one_active_member", @"Vector", nil), @(activeCount), @(memberCount)];
// }
// }
// else
// {
// // Should not happen
// self.roomMembers.text = nil;
// }
self.previewLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_preview_invitation_format", @"Vector", nil), inviter];
// FIXME: Display members status when it will be available
self.roomMembers.text = nil;
// if (memberCount)
// {
// if (activeCount > 1)
// {
// self.roomMembers.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_title_multiple_active_members", @"Vector", nil), @(activeCount), @(memberCount)];
// }
// else
// {
// self.roomMembers.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_title_one_active_member", @"Vector", nil), @(activeCount), @(memberCount)];
// }
// }
// else
// {
// // Should not happen
// self.roomMembers.text = nil;
// }
self.previewLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_preview_invitation_format", @"Vector", nil), inviter];
}];
}
else
{
@@ -2975,8 +2975,9 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)();
{
if (room.summary.isEncrypted)
{
MXKRoomDataSource *roomDataSource = [roomDataSourceManager roomDataSourceForRoom:room.roomId create:NO];
[roomDataSource reload];
[roomDataSourceManager roomDataSourceForRoom:room.roomId create:NO onComplete:^(MXKRoomDataSource *roomDataSource) {
[roomDataSource reload];
}];
}
}
+108 -69
View File
@@ -1,6 +1,7 @@
/*
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector 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
@@ -510,59 +511,37 @@
if ([[segue identifier] isEqualToString:@"showRoomDetails"])
{
// Replace the rootviewcontroller with a room view controller
// Get the RoomViewController from the storyboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
_currentRoomViewController = [storyboard instantiateViewControllerWithIdentifier:@"RoomViewControllerStoryboardId"];
navigationController.viewControllers = @[_currentRoomViewController];
if (!_selectedRoomPreviewData)
{
MXKRoomDataSource *roomDataSource;
// Check whether an event has been selected from messages or files search tab.
MXEvent *selectedSearchEvent = unifiedSearchViewController.selectedSearchEvent;
MXSession *selectedSearchEventSession = unifiedSearchViewController.selectedSearchEventSession;
if (!selectedSearchEvent)
{
if (!_selectedEventId)
{
// LIVE: Show the room live timeline managed by MXKRoomDataSourceManager
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:_selectedRoomSession];
roomDataSource = [roomDataSourceManager roomDataSourceForRoom:_selectedRoomId create:YES];
}
else
{
// Open the room on the requested event
roomDataSource = [[RoomDataSource alloc] initWithRoomId:_selectedRoomId initialEventId:_selectedEventId andMatrixSession:_selectedRoomSession];
[roomDataSource finalizeInitialization];
((RoomDataSource*)roomDataSource).markTimelineInitialEvent = YES;
// Give the data source ownership to the room view controller.
_currentRoomViewController.hasRoomDataSourceOwnership = YES;
}
}
else
{
// Search result: Create a temp timeline from the selected event
roomDataSource = [[RoomDataSource alloc] initWithRoomId:selectedSearchEvent.roomId initialEventId:selectedSearchEvent.eventId andMatrixSession:selectedSearchEventSession];
[roomDataSource finalizeInitialization];
((RoomDataSource*)roomDataSource).markTimelineInitialEvent = YES;
// Give the data source ownership to the room view controller.
_currentRoomViewController.hasRoomDataSourceOwnership = YES;
}
[_currentRoomViewController displayRoom:roomDataSource];
MXWeakify(self);
[self dataSourceOfRoomToDisplay:^(MXKRoomDataSource *roomDataSource) {
MXStrongifyAndReturnIfNil(self);
// Replace the rootviewcontroller with a room view controller
// Get the RoomViewController from the storyboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
self->_currentRoomViewController = [storyboard instantiateViewControllerWithIdentifier:@"RoomViewControllerStoryboardId"];
navigationController.viewControllers = @[self.currentRoomViewController];
[self.currentRoomViewController displayRoom:roomDataSource];
[self setupLeftBarButtonItem];
}];
}
else
{
// Replace the rootviewcontroller with a room view controller
// Get the RoomViewController from the storyboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
_currentRoomViewController = [storyboard instantiateViewControllerWithIdentifier:@"RoomViewControllerStoryboardId"];
navigationController.viewControllers = @[_currentRoomViewController];
[_currentRoomViewController displayRoomPreview:_selectedRoomPreviewData];
_selectedRoomPreviewData = nil;
[self setupLeftBarButtonItem];
}
}
else if ([[segue identifier] isEqualToString:@"showContactDetails"])
@@ -573,6 +552,8 @@
_currentContactDetailViewController.contact = _selectedContact;
navigationController.viewControllers = @[_currentContactDetailViewController];
[self setupLeftBarButtonItem];
}
else
{
@@ -581,28 +562,8 @@
[_currentGroupDetailViewController setGroup:_selectedGroup withMatrixSession:_selectedGroupSession];
navigationController.viewControllers = @[_currentGroupDetailViewController];
}
if (self.splitViewController)
{
// Refresh selected cell without scrolling the selected cell (We suppose it's visible here)
[self refreshCurrentSelectedCell:NO];
if (_currentRoomViewController)
{
_currentRoomViewController.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
_currentRoomViewController.navigationItem.leftItemsSupplementBackButton = YES;
}
else if (_currentContactDetailViewController)
{
_currentContactDetailViewController.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
_currentContactDetailViewController.navigationItem.leftItemsSupplementBackButton = YES;
}
else if (_currentGroupDetailViewController)
{
_currentGroupDetailViewController.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
_currentGroupDetailViewController.navigationItem.leftItemsSupplementBackButton = YES;
}
[self setupLeftBarButtonItem];
}
}
else
@@ -647,6 +608,84 @@
self.navigationController.topViewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
}
/**
Load the data source of the room to open.
@param onComplete a block providing the loaded room data source.
*/
- (void)dataSourceOfRoomToDisplay:(void (^)(MXKRoomDataSource *roomDataSource))onComplete
{
// Check whether an event has been selected from messages or files search tab.
MXEvent *selectedSearchEvent = unifiedSearchViewController.selectedSearchEvent;
MXSession *selectedSearchEventSession = unifiedSearchViewController.selectedSearchEventSession;
if (!selectedSearchEvent)
{
if (!_selectedEventId)
{
// LIVE: Show the room live timeline managed by MXKRoomDataSourceManager
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:_selectedRoomSession];
[roomDataSourceManager roomDataSourceForRoom:_selectedRoomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) {
onComplete(roomDataSource);
}];
}
else
{
// Open the room on the requested event
[RoomDataSource loadRoomDataSourceWithRoomId:_selectedRoomId initialEventId:_selectedEventId andMatrixSession:_selectedRoomSession onComplete:^(id roomDataSource) {
((RoomDataSource*)roomDataSource).markTimelineInitialEvent = YES;
// Give the data source ownership to the room view controller.
self.currentRoomViewController.hasRoomDataSourceOwnership = YES;
onComplete(roomDataSource);
}];
}
}
else
{
// Search result: Create a temp timeline from the selected event
[RoomDataSource loadRoomDataSourceWithRoomId:selectedSearchEvent.roomId initialEventId:selectedSearchEvent.eventId andMatrixSession:selectedSearchEventSession onComplete:^(id roomDataSource) {
[roomDataSource finalizeInitialization];
((RoomDataSource*)roomDataSource).markTimelineInitialEvent = YES;
// Give the data source ownership to the room view controller.
self.currentRoomViewController.hasRoomDataSourceOwnership = YES;
onComplete(roomDataSource);
}];
}
}
- (void)setupLeftBarButtonItem
{
if (self.splitViewController)
{
// Refresh selected cell without scrolling the selected cell (We suppose it's visible here)
[self refreshCurrentSelectedCell:NO];
if (_currentRoomViewController)
{
_currentRoomViewController.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
_currentRoomViewController.navigationItem.leftItemsSupplementBackButton = YES;
}
else if (_currentContactDetailViewController)
{
_currentContactDetailViewController.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
_currentContactDetailViewController.navigationItem.leftItemsSupplementBackButton = YES;
}
else if (_currentGroupDetailViewController)
{
_currentGroupDetailViewController.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
_currentGroupDetailViewController.navigationItem.leftItemsSupplementBackButton = YES;
}
}
}
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion
{
// Keep ref on presented view controller
+4 -4
View File
@@ -262,9 +262,9 @@ NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator = @"/";
#pragma mark - MXRoomSummaryUpdating
- (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary withStateEvents:(NSArray<MXEvent *> *)stateEvents
- (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary withStateEvents:(NSArray<MXEvent *> *)stateEvents roomState:(MXRoomState *)roomState
{
BOOL ret = [super session:session updateRoomSummary:summary withStateEvents:stateEvents];
BOOL ret = [super session:session updateRoomSummary:summary withStateEvents:stateEvents roomState:roomState];
// Check whether the room display name and/or the room avatar url should be updated at Riot level.
BOOL refreshRiotRoomDisplayName = NO;
@@ -303,7 +303,7 @@ NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator = @"/";
if (refreshRiotRoomDisplayName)
{
NSString *riotRoomDisplayName = [self riotRoomDisplayNameFromRoomState:summary.room.state];
NSString *riotRoomDisplayName = [self riotRoomDisplayNameFromRoomState:roomState];
if (riotRoomDisplayName.length && ![summary.displayname isEqualToString:riotRoomDisplayName])
{
@@ -313,7 +313,7 @@ NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator = @"/";
}
if (refreshRiotRoomAvatarURL)
{
NSString *riotRoomAvatarURL = [self riotRoomAvatarURLFromRoomState:summary.room.state];
NSString *riotRoomAvatarURL = [self riotRoomAvatarURLFromRoomState:roomState];
if (riotRoomAvatarURL.length && ![summary.avatar isEqualToString:riotRoomAvatarURL])
{