From 13c76d47eaddc112c06669858902add1e1b1e95e Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 4 Dec 2014 10:59:14 +0100 Subject: [PATCH 01/18] Updated MatrixConsole to use the new MXRoomPowerLevels class --- matrixConsole/matrixConsole/MatrixHandler.m | 24 ---------- .../matrixConsole/View/RoomMemberTableCell.m | 45 +++++++------------ 2 files changed, 15 insertions(+), 54 deletions(-) diff --git a/matrixConsole/matrixConsole/MatrixHandler.m b/matrixConsole/matrixConsole/MatrixHandler.m index bb5f5d60b..3f2966b9c 100644 --- a/matrixConsole/matrixConsole/MatrixHandler.m +++ b/matrixConsole/matrixConsole/MatrixHandler.m @@ -117,9 +117,6 @@ static MatrixHandler *sharedHandler = nil; kMXEventTypeStringRoomCreate, kMXEventTypeStringRoomJoinRules, kMXEventTypeStringRoomPowerLevels, - kMXEventTypeStringRoomAddStateLevel, - kMXEventTypeStringRoomSendEventLevel, - kMXEventTypeStringRoomOpsLevel, kMXEventTypeStringRoomAliases, kMXEventTypeStringRoomMessage, kMXEventTypeStringRoomMessageFeedback, @@ -573,27 +570,6 @@ static MatrixHandler *sharedHandler = nil; } break; } -// case MXEventTypeRoomAddStateLevel: { -// NSString *minLevel = event.content[@"level"]; -// if (minLevel) { -// displayText = [NSString stringWithFormat:@"The minimum power level a user needs to add state is: %@", minLevel]; -// } -// break; -// } -// case MXEventTypeRoomSendEventLevel: { -// NSString *minLevel = event.content[@"level"]; -// if (minLevel) { -// displayText = [NSString stringWithFormat:@"The minimum power level a user needs to send an event is: %@", minLevel]; -// } -// break; -// } -// case MXEventTypeRoomOpsLevel: { -// displayText = @"The minimum power levels that a user must have before acting are:"; -// for (NSString *key in event.content.allKeys) { -// displayText = [NSString stringWithFormat:@"%@\r\n%@:%@", displayText, key, [event.content objectForKey:key]]; -// } -// break; -// } case MXEventTypeRoomAliases: { NSArray *aliases = event.content[@"aliases"]; if (aliases) { diff --git a/matrixConsole/matrixConsole/View/RoomMemberTableCell.m b/matrixConsole/matrixConsole/View/RoomMemberTableCell.m index c7f06adc5..6b8231421 100644 --- a/matrixConsole/matrixConsole/View/RoomMemberTableCell.m +++ b/matrixConsole/matrixConsole/View/RoomMemberTableCell.m @@ -51,38 +51,23 @@ self.backgroundColor = [UIColor whiteColor]; // Handle power level display - self.userPowerLevel.hidden = NO; - NSDictionary *powerLevels; - if (room.state.powerLevels[@"users"]){ - // In Matrix 0.5, users power levels are listed under the `users` dictionnary - powerLevels = room.state.powerLevels[@"users"]; - } - else { - // @TODO: Remove this backward compatibility - powerLevels = room.state.powerLevels; - } - - if (powerLevels) { - int maxLevel = 0; - for (NSString *powerLevel in powerLevels.allValues) { - int level = [powerLevel intValue]; - if (level > maxLevel) { - maxLevel = level; - } + self.userPowerLevel.hidden = NO; + MXRoomPowerLevels *roomPowerLevels = room.state.powerLevels; + + int maxLevel = 0; + for (NSString *powerLevel in roomPowerLevels.users.allValues) { + int level = [powerLevel intValue]; + if (level > maxLevel) { + maxLevel = level; } - NSString *userPowerLevel = [powerLevels objectForKey:roomMember.userId]; // CAUTION: we invoke objectForKey here because user_id starts with an '@' character - if (userPowerLevel == nil) { - userPowerLevel = [powerLevels valueForKey:@"default"]; - } - float userPowerLevelFloat = 0.0; - if (userPowerLevel) { - userPowerLevelFloat = [userPowerLevel floatValue]; - } - self.userPowerLevel.progress = maxLevel ? userPowerLevelFloat / maxLevel : 1; - } else { - self.userPowerLevel.progress = 0; } - + NSUInteger userPowerLevel = [roomPowerLevels powerLevelOfUserWithUserID:roomMember.userId]; + float userPowerLevelFloat = 0.0; + if (userPowerLevel) { + userPowerLevelFloat = userPowerLevel; + } + self.userPowerLevel.progress = maxLevel ? userPowerLevelFloat / maxLevel : 1; + if (roomMember.membership == MXMembershipInvite) { self.lastActiveAgoLabel.backgroundColor = [UIColor lightGrayColor]; self.lastActiveAgoLabel.text = @"invited"; From 984d2c1d1f7dab7c1624d52ae1424a3d1a70b1c2 Mon Sep 17 00:00:00 2001 From: giomfo Date: Thu, 4 Dec 2014 11:43:43 +0100 Subject: [PATCH 02/18] Room details: fix flickering on outgoing messages + improve table refresh at the end of back pagination. --- .../ViewController/RoomViewController.m | 164 ++++++------------ 1 file changed, 51 insertions(+), 113 deletions(-) diff --git a/matrixConsole/matrixConsole/ViewController/RoomViewController.m b/matrixConsole/matrixConsole/ViewController/RoomViewController.m index 9d3539202..44a0166ea 100644 --- a/matrixConsole/matrixConsole/ViewController/RoomViewController.m +++ b/matrixConsole/matrixConsole/ViewController/RoomViewController.m @@ -96,6 +96,8 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. forceScrollToBottomOnViewDidAppear = YES; + // Hide messages table by default in order to hide initial scrolling to the bottom + self.messagesTableView.hidden = YES; UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoLight]; [button addTarget:self action:@selector(showHideRoomMembers:) forControlEvents:UIControlEventTouchUpInside]; @@ -197,6 +199,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; // Scroll to the bottom [self scrollToBottomAnimated:animated]; forceScrollToBottomOnViewDidAppear = NO; + self.messagesTableView.hidden = NO; } } @@ -288,28 +291,22 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; // We will scroll to bottom after updating tableView only if the most recent message is entirely visible. CGFloat maxPositionY = self.messagesTableView.contentOffset.y + (self.messagesTableView.frame.size.height - self.messagesTableView.contentInset.bottom); shouldScrollToBottom = (maxPositionY >= self.messagesTableView.contentSize.height); + // Update Table - NSIndexPath *indexPathForInsertedRow = nil; - NSIndexPath *indexPathForDeletedRow = nil; - NSMutableArray *indexPathsForUpdatedRows = [NSMutableArray array]; - BOOL isComplete = NO; + BOOL isHandled = NO; // For outgoing message, remove the temporary event if ([event.userId isEqualToString:[MatrixHandler sharedHandler].userId] && messages.count) { // Consider first the last message RoomMessage *message = [messages lastObject]; NSUInteger index = messages.count - 1; - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0]; if ([message containsEventId:event.eventId]) { if (message.messageType == RoomMessageTypeText) { // Removing temporary event (local echo) [message removeEvent:event.eventId]; // Update message with the received event - isComplete = [message addEvent:event withRoomState:roomState]; - if (message.attributedTextMessage.length) { - [indexPathsForUpdatedRows addObject:indexPath]; - } else { + isHandled = [message addEvent:event withRoomState:roomState]; + if (! message.attributedTextMessage.length) { [messages removeObjectAtIndex:index]; - indexPathForDeletedRow = indexPath; } } else { // Create a new message to handle attachment @@ -317,31 +314,24 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; if (!message) { // Ignore unsupported/unexpected events [messages removeObjectAtIndex:index]; - indexPathForDeletedRow = indexPath; } else { [messages replaceObjectAtIndex:index withObject:message]; - [indexPathsForUpdatedRows addObject:indexPath]; } - isComplete = YES; + isHandled = YES; } } else { while (index--) { message = [messages objectAtIndex:index]; - indexPath = [NSIndexPath indexPathForRow:index inSection:0]; if ([message containsEventId:event.eventId]) { if (message.messageType == RoomMessageTypeText) { // Removing temporary event (local echo) [message removeEvent:event.eventId]; - if (message.attributedTextMessage.length) { - [indexPathsForUpdatedRows addObject:indexPath]; - } else { + if (!message.attributedTextMessage.length) { [messages removeObjectAtIndex:index]; - indexPathForDeletedRow = indexPath; } } else { // Remove the local event (a new one will be added to messages) [messages removeObjectAtIndex:index]; - indexPathForDeletedRow = indexPath; } break; } @@ -349,51 +339,29 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; } } - if (isComplete == NO) { + if (isHandled == NO) { // Check whether the event may be grouped with last message RoomMessage *lastMessage = [messages lastObject]; if (lastMessage && [lastMessage addEvent:event withRoomState:roomState]) { - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:(messages.count - 1) inSection:0]; - [indexPathsForUpdatedRows addObject:indexPath]; + isHandled = YES; } else { + // Create a new item lastMessage = [[RoomMessage alloc] initWithEvent:event andRoomState:roomState]; if (lastMessage) { - indexPathForInsertedRow = [NSIndexPath indexPathForRow:messages.count inSection:0]; [messages addObject:lastMessage]; + isHandled = YES; } // else ignore unsupported/unexpected events } } - // Refresh table display - BOOL isModified = NO; - [UIView setAnimationsEnabled:NO]; - [self.messagesTableView beginUpdates]; - if (indexPathForDeletedRow) { - if (indexPathForInsertedRow) { - [indexPathsForUpdatedRows removeAllObjects]; - NSUInteger index = indexPathForDeletedRow.row; - for (; index < messages.count; index++) { - [indexPathsForUpdatedRows addObject:[NSIndexPath indexPathForRow:index inSection:0]]; + // Refresh table display except if a back pagination is in progress + if (!isBackPaginationInProgress) { + [self.messagesTableView reloadData]; + if (isHandled) { + if ([[AppDelegate theDelegate].masterTabBarController.visibleRoomId isEqualToString:self.roomId] == NO) { + // Some new events are received for this room while it is not visible, scroll to bottom on viewDidAppear to focus on them + forceScrollToBottomOnViewDidAppear = YES; } - } else { - [self.messagesTableView deleteRowsAtIndexPaths:@[indexPathForDeletedRow] withRowAnimation:UITableViewRowAnimationNone]; - isModified = YES; - } - } else if (indexPathForInsertedRow) { - [self.messagesTableView insertRowsAtIndexPaths:@[indexPathForInsertedRow] withRowAnimation:UITableViewRowAnimationNone]; - isModified = YES; - } - if (indexPathsForUpdatedRows.count) { - [self.messagesTableView reloadRowsAtIndexPaths:indexPathsForUpdatedRows withRowAnimation:UITableViewRowAnimationNone]; - isModified = YES; - } - [self.messagesTableView endUpdates]; - [UIView setAnimationsEnabled:YES]; - - if (isModified) { - if ([[AppDelegate theDelegate].masterTabBarController.visibleRoomId isEqualToString:self.roomId] == NO) { - // Some new events are received for this room while it is not visible, scroll to bottom on viewDidAppear to focus on them - forceScrollToBottomOnViewDidAppear = YES; } } } else if (isBackPaginationInProgress && direction == MXEventDirectionBackwards) { @@ -453,42 +421,35 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; return; } if (backPaginationAddedItemsNb) { - // Prepare insertion of new rows at the top of the table (compute cumulative height of added cells) - NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:backPaginationAddedItemsNb]; - NSIndexPath *indexPath; + // We will scroll to bottom when table is loaded for the first time + BOOL shouldScrollToBottom = (self.messagesTableView.contentSize.height == 0); CGFloat verticalOffset = 0; - for (NSUInteger index = 0; index < backPaginationAddedItemsNb; index++) { - indexPath = [NSIndexPath indexPathForRow:index inSection:0]; - [indexPaths addObject:indexPath]; - verticalOffset += [self tableView:self.messagesTableView heightForRowAtIndexPath:indexPath]; + if (shouldScrollToBottom == NO) { + // In this case, we will adjust the vertical offset in order to make visible only a few part of added messages (at the top of the table) + NSIndexPath *indexPath; + // Compute the cumulative height of the added messages + for (NSUInteger index = 0; index < backPaginationAddedItemsNb; index++) { + indexPath = [NSIndexPath indexPathForRow:index inSection:0]; + verticalOffset += [self tableView:self.messagesTableView heightForRowAtIndexPath:indexPath]; + } + // Deduce the vertical offset from this height + verticalOffset -= 100; } - // Here indexPath corresponds to the first added message (We will reuse it at the end of table update to make it visible) // Reset count to enable tableView update backPaginationAddedItemsNb = 0; - - // Disable animation during cells insertion to prevent flickering - [UIView setAnimationsEnabled:NO]; // Store the current content offset CGPoint contentOffset = self.messagesTableView.contentOffset; - [self.messagesTableView beginUpdates]; - [self.messagesTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone]; - [self.messagesTableView endUpdates]; - // Enable animation again - [UIView setAnimationsEnabled:YES]; - // Fix vertical offset in order to prevent scrolling down - contentOffset.y += verticalOffset; - [self.messagesTableView setContentOffset:contentOffset animated:NO]; + // Reload + [self.messagesTableView reloadData]; + if (shouldScrollToBottom) { + [self scrollToBottomAnimated:NO]; + } else if (verticalOffset > 0) { + // Adjust vertical offset in order to limit scrolling down + contentOffset.y += verticalOffset; + [self.messagesTableView setContentOffset:contentOffset animated:NO]; + } [_activityIndicator stopAnimating]; isBackPaginationInProgress = NO; - - // Scroll tableView in order to make visible the first added message (dispatch this action in order to let table end its refresh) - dispatch_async(dispatch_get_main_queue(), ^{ - if (indexPath.row == messages.count - 1) { - [self scrollToBottomAnimated:NO]; - } else { - [self.messagesTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; - } - }); } else { // Here there was no event related to the listened types [_activityIndicator stopAnimating]; @@ -1409,17 +1370,12 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; while (index--) { message = [messages objectAtIndex:index]; if ([message containsEventId:localEvent.eventId]) { - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0]; localEvent.content = msgContent; if (message.messageType == RoomMessageTypeText) { [message removeEvent:localEvent.eventId]; [message addEvent:localEvent withRoomState:mxRoom.state]; - if (message.attributedTextMessage.length) { - // Refresh table display - [self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; - } else { + if (!message.attributedTextMessage.length) { [messages removeObjectAtIndex:index]; - [self.messagesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } } else { // Create a new message @@ -1427,15 +1383,14 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; if (message) { // Refresh table display [messages replaceObjectAtIndex:index withObject:message]; - [self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } else { [messages removeObjectAtIndex:index]; - [self.messagesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } } break; } } + [self.messagesTableView reloadData]; } else { // Create a temporary event to displayed outgoing message (local echo) NSString* localEventId = [NSString stringWithFormat:@"%@%@", kLocalEchoEventIdPrefix, [[NSProcessInfo processInfo] globallyUniqueString]]; @@ -1449,19 +1404,16 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; localEvent.originServerTs = kMXUndefinedTimestamp; // Check whether this new event may be grouped with last message RoomMessage *lastMessage = [messages lastObject]; - if (lastMessage && [lastMessage addEvent:localEvent withRoomState:mxRoom.state]) { - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:(messages.count - 1) inSection:0]; - [self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; - } else { + if (lastMessage == nil || [lastMessage addEvent:localEvent withRoomState:mxRoom.state] == NO) { + // Create a new item lastMessage = [[RoomMessage alloc] initWithEvent:localEvent andRoomState:mxRoom.state]; if (lastMessage) { - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:messages.count inSection:0]; [messages addObject:lastMessage]; - [self.messagesTableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } else { NSLog(@"ERROR: Unable to add local event: %@", localEvent.description); } } + [self.messagesTableView reloadData]; [self scrollToBottomAnimated:NO]; } @@ -1482,7 +1434,6 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; while (index--) { RoomMessage *message = [messages objectAtIndex:index]; if ([message containsEventId:localEvent.eventId]) { - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0]; if (message.messageType == RoomMessageTypeText) { [message removeEvent:localEvent.eventId]; if (isEventAlreadyAddedToRoom == NO) { @@ -1490,12 +1441,8 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; localEvent.eventId = event_id; [message addEvent:localEvent withRoomState:mxRoom.state]; } - if (message.attributedTextMessage.length) { - // Refresh table display - [self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; - } else { + if (! message.attributedTextMessage.length) { [messages removeObjectAtIndex:index]; - [self.messagesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } } else { message = nil; @@ -1507,15 +1454,14 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; if (message) { // Refresh table display [messages replaceObjectAtIndex:index withObject:message]; - [self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } else { [messages removeObjectAtIndex:index]; - [self.messagesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } } break; } } + [self.messagesTableView reloadData]; } failure:^(NSError *error) { [self handleError:error forLocalEvent:localEvent]; }]; @@ -1564,13 +1510,11 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; // Update table sources RoomMessage *message = [[RoomMessage alloc] initWithEvent:mxEvent andRoomState:mxRoom.state]; if (message) { - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:messages.count inSection:0]; [messages addObject:message]; - [self.messagesTableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } else { NSLog(@"ERROR: Unable to add local event for attachment: %@", mxEvent.description); } - + [self.messagesTableView reloadData]; [self scrollToBottomAnimated:NO]; return mxEvent; } @@ -1588,17 +1532,12 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; RoomMessage *message = [messages objectAtIndex:index]; if ([message containsEventId:localEvent.eventId]) { NSLog(@"Posted event: %@", localEvent.description); - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0]; if (message.messageType == RoomMessageTypeText) { [message removeEvent:localEvent.eventId]; localEvent.eventId = kFailedEventId; [message addEvent:localEvent withRoomState:mxRoom.state]; - if (message.attributedTextMessage.length) { - // Refresh table display - [self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; - } else { + if (!message.attributedTextMessage.length) { [messages removeObjectAtIndex:index]; - [self.messagesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } } else { // Create a new message @@ -1607,15 +1546,14 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; if (message) { // Refresh table display [messages replaceObjectAtIndex:index withObject:message]; - [self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } else { [messages removeObjectAtIndex:index]; - [self.messagesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } } break; } } + [self.messagesTableView reloadData]; } - (BOOL)isIRCStyleCommand:(NSString*)text{ From 6bce0a11ef5d824021d17afb38f36cc8044604d6 Mon Sep 17 00:00:00 2001 From: giomfo Date: Thu, 4 Dec 2014 13:23:38 +0100 Subject: [PATCH 03/18] Update user's change listener --- matrixConsole/matrixConsole/MatrixHandler.m | 28 ++++++++------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/matrixConsole/matrixConsole/MatrixHandler.m b/matrixConsole/matrixConsole/MatrixHandler.m index 3f2966b9c..2ffa9a143 100644 --- a/matrixConsole/matrixConsole/MatrixHandler.m +++ b/matrixConsole/matrixConsole/MatrixHandler.m @@ -28,7 +28,7 @@ static MatrixHandler *sharedHandler = nil; BOOL notifyOpenSessionFailure; // Handle user's settings change - id roomMembersListener; + id userUpdateListener; // Handle events notification id eventsListener; } @@ -138,19 +138,13 @@ static MatrixHandler *sharedHandler = nil; self.isInitialSyncDone = YES; // Register listener to update user's information - roomMembersListener = [self.mxSession listenToEventsOfTypes:@[kMXEventTypeStringPresence] onEvent:^(MXEvent *event, MXEventDirection direction, id customObject) { - // Consider only live events - if (direction == MXEventDirectionForwards) { - // Consider only events from app user - if ([event.userId isEqualToString:self.userId]) { - // Update local storage - if (![self.userDisplayName isEqualToString:event.content[@"displayname"]]) { - self.userDisplayName = event.content[@"displayname"]; - } - if (![self.userPictureURL isEqualToString:event.content[@"avatar_url"]]) { - self.userPictureURL = event.content[@"avatar_url"]; - } - } + userUpdateListener = [self.mxSession.myUser listenToUserUpdate:^(MXEvent *event) { + // Update local storage + if (![self.userDisplayName isEqualToString:event.content[@"displayname"]]) { + self.userDisplayName = event.content[@"displayname"]; + } + if (![self.userPictureURL isEqualToString:event.content[@"avatar_url"]]) { + self.userPictureURL = event.content[@"avatar_url"]; } }]; @@ -179,9 +173,9 @@ static MatrixHandler *sharedHandler = nil; [self.mxSession removeListener:eventsListener]; eventsListener = nil; } - if (roomMembersListener) { - [self.mxSession removeListener:roomMembersListener]; - roomMembersListener = nil; + if (userUpdateListener) { + [self.mxSession.myUser removeListener:userUpdateListener]; + userUpdateListener = nil; } [self.mxSession close]; self.mxSession = nil; From e728d27a033636486c8ceff23bb8351bd38086cc Mon Sep 17 00:00:00 2001 From: giomfo Date: Thu, 4 Dec 2014 14:19:59 +0100 Subject: [PATCH 04/18] Prepare support of "invite" level to power level events (related to SYN-190) --- matrixConsole/matrixConsole/MatrixHandler.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/matrixConsole/matrixConsole/MatrixHandler.m b/matrixConsole/matrixConsole/MatrixHandler.m index 2ffa9a143..0d73f4b95 100644 --- a/matrixConsole/matrixConsole/MatrixHandler.m +++ b/matrixConsole/matrixConsole/MatrixHandler.m @@ -550,6 +550,9 @@ static MatrixHandler *sharedHandler = nil; if (event.content[@"redact"]) { displayText = [NSString stringWithFormat:@"%@\r\n\u2022 redact: %@", displayText, event.content[@"redact"]]; } + if (event.content[@"invite"]) { + displayText = [NSString stringWithFormat:@"%@\r\n\u2022 invite: %@", displayText, event.content[@"invite"]]; + } displayText = [NSString stringWithFormat:@"%@\r\nThe minimum power levels related to events are:", displayText]; NSDictionary *events = event.content[@"events"]; From 3757f8a371daff9814531b5b4c91bb3fa3b3e442 Mon Sep 17 00:00:00 2001 From: giomfo Date: Thu, 4 Dec 2014 16:33:31 +0100 Subject: [PATCH 05/18] Room details: fix back pagination size (paginate back until the defined size is reached) --- .../ViewController/RoomViewController.m | 130 ++++++++++-------- 1 file changed, 73 insertions(+), 57 deletions(-) diff --git a/matrixConsole/matrixConsole/ViewController/RoomViewController.m b/matrixConsole/matrixConsole/ViewController/RoomViewController.m index 44a0166ea..c14fe480e 100644 --- a/matrixConsole/matrixConsole/ViewController/RoomViewController.m +++ b/matrixConsole/matrixConsole/ViewController/RoomViewController.m @@ -28,7 +28,8 @@ #import "MediaManager.h" -#define UPLOAD_FILE_SIZE 5000000 +#define ROOMVIEWCONTROLLER_UPLOAD_FILE_SIZE 5000000 +#define ROOMVIEWCONTROLLER_BACK_PAGINATION_SIZE 20 #define ROOM_MESSAGE_CELL_DEFAULT_HEIGHT 50 #define ROOM_MESSAGE_CELL_DEFAULT_TEXTVIEW_TOP_CONST 10 @@ -57,7 +58,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; // Back pagination BOOL isBackPaginationInProgress; - NSUInteger backPaginationAddedItemsNb; + NSUInteger backPaginationAddedMsgNb; // Members list NSArray *members; @@ -371,7 +372,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; firstMessage = [[RoomMessage alloc] initWithEvent:event andRoomState:roomState]; if (firstMessage) { [messages insertObject:firstMessage atIndex:0]; - backPaginationAddedItemsNb++; + backPaginationAddedMsgNb++; } // Ignore unsupported/unexpected events } @@ -413,61 +414,74 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; if (mxRoom.canPaginate) { [_activityIndicator startAnimating]; isBackPaginationInProgress = YES; - backPaginationAddedItemsNb = 0; + backPaginationAddedMsgNb = 0; - [mxRoom paginateBackMessages:20 complete:^{ - // Sanity check: check whether the view controller has not been released while back pagination was running - if (self.roomId == nil) { - return; - } - if (backPaginationAddedItemsNb) { - // We will scroll to bottom when table is loaded for the first time - BOOL shouldScrollToBottom = (self.messagesTableView.contentSize.height == 0); - CGFloat verticalOffset = 0; - if (shouldScrollToBottom == NO) { - // In this case, we will adjust the vertical offset in order to make visible only a few part of added messages (at the top of the table) - NSIndexPath *indexPath; - // Compute the cumulative height of the added messages - for (NSUInteger index = 0; index < backPaginationAddedItemsNb; index++) { - indexPath = [NSIndexPath indexPathForRow:index inSection:0]; - verticalOffset += [self tableView:self.messagesTableView heightForRowAtIndexPath:indexPath]; - } - // Deduce the vertical offset from this height - verticalOffset -= 100; - } - // Reset count to enable tableView update - backPaginationAddedItemsNb = 0; - // Store the current content offset - CGPoint contentOffset = self.messagesTableView.contentOffset; - // Reload - [self.messagesTableView reloadData]; - if (shouldScrollToBottom) { - [self scrollToBottomAnimated:NO]; - } else if (verticalOffset > 0) { - // Adjust vertical offset in order to limit scrolling down - contentOffset.y += verticalOffset; - [self.messagesTableView setContentOffset:contentOffset animated:NO]; - } - [_activityIndicator stopAnimating]; - isBackPaginationInProgress = NO; - } else { - // Here there was no event related to the listened types - [_activityIndicator stopAnimating]; - isBackPaginationInProgress = NO; - // Trigger a new back pagination (if possible) - [self triggerBackPagination]; - } - } failure:^(NSError *error) { - [_activityIndicator stopAnimating]; - isBackPaginationInProgress = NO; - backPaginationAddedItemsNb = 0; - NSLog(@"Failed to paginate back: %@", error); - //Alert user - [[AppDelegate theDelegate] showErrorAsAlert:error]; - }]; + [self paginateBackMessages:ROOMVIEWCONTROLLER_BACK_PAGINATION_SIZE]; } } +- (void)paginateBackMessages:(NSUInteger)requestedItemsNb { + [mxRoom paginateBackMessages:requestedItemsNb complete:^{ + // Sanity check: check whether the view controller has not been released while back pagination was running + if (self.roomId == nil) { + return; + } + // Compute number of received items + NSUInteger itemsCount = 0; + for (NSUInteger index = 0; index < backPaginationAddedMsgNb; index++) { + RoomMessage *message = [messages objectAtIndex:index]; + itemsCount += message.components.count; + } + // Check whether we got enough items + if (itemsCount < requestedItemsNb && mxRoom.canPaginate) { + // Ask more items + [self paginateBackMessages:(requestedItemsNb - itemsCount)]; + } else { + [self onBackPaginationComplete]; + } + } failure:^(NSError *error) { + [self onBackPaginationComplete]; + NSLog(@"Failed to paginate back: %@", error); + //Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + }]; +} + +- (void)onBackPaginationComplete { + if (backPaginationAddedMsgNb) { + // We scroll to bottom when table is loaded for the first time + BOOL shouldScrollToBottom = (self.messagesTableView.contentSize.height == 0); + + CGFloat verticalOffset = 0; + if (shouldScrollToBottom == NO) { + // In this case, we will adjust the vertical offset in order to make visible only a few part of added messages (at the top of the table) + NSIndexPath *indexPath; + // Compute the cumulative height of the added messages + for (NSUInteger index = 0; index < backPaginationAddedMsgNb; index++) { + indexPath = [NSIndexPath indexPathForRow:index inSection:0]; + verticalOffset += [self tableView:self.messagesTableView heightForRowAtIndexPath:indexPath]; + } + // Deduce the vertical offset from this height + verticalOffset -= 100; + } + // Reset count to enable tableView update + backPaginationAddedMsgNb = 0; + // Reload + [self.messagesTableView reloadData]; + // Adjust vertical content offset + if (shouldScrollToBottom) { + [self scrollToBottomAnimated:NO]; + } else if (verticalOffset > 0) { + // Adjust vertical offset in order to limit scrolling down + CGPoint contentOffset = self.messagesTableView.contentOffset; + contentOffset.y = verticalOffset - self.messagesTableView.contentInset.top; + [self.messagesTableView setContentOffset:contentOffset animated:NO]; + } + } + [_activityIndicator stopAnimating]; + isBackPaginationInProgress = NO; +} + #pragma mark - KVO - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { @@ -761,7 +775,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; return members.count; } - if (backPaginationAddedItemsNb) { + if (backPaginationAddedMsgNb) { // Here some old messages have been added to messages during back pagination. // Stop table refreshing, the table will be refreshed at the end of pagination return 0; @@ -1196,7 +1210,9 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; // paginate ? if (scrollView.contentOffset.y < -64) { - [self triggerBackPagination]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self triggerBackPagination]; + }); } } } @@ -1816,7 +1832,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; NSData *videoData = [NSData dataWithContentsOfURL:tmpVideoLocation]; [[NSFileManager defaultManager] removeItemAtPath:[tmpVideoLocation path] error:nil]; if (videoData) { - if (videoData.length < UPLOAD_FILE_SIZE) { + if (videoData.length < ROOMVIEWCONTROLLER_UPLOAD_FILE_SIZE) { [videoInfo setValue:[NSNumber numberWithUnsignedInteger:videoData.length] forKey:@"size"]; [mxHandler.mxRestClient uploadContent:videoData mimeType:videoInfo[@"mimetype"] timeout:30 success:^(NSString *url) { [videoContent setValue:url forKey:@"url"]; From 9284fda2252a6788248e57b5953eb464d10e7ee1 Mon Sep 17 00:00:00 2001 From: giomfo Date: Fri, 5 Dec 2014 11:55:01 +0100 Subject: [PATCH 06/18] Recents: Add sync feedback on App resume --- matrixConsole/matrixConsole/AppDelegate.m | 6 ++++++ matrixConsole/matrixConsole/MatrixHandler.h | 3 +++ matrixConsole/matrixConsole/MatrixHandler.m | 19 +++++++++++++++++++ .../ViewController/RecentsViewController.m | 17 ++++++++++++++--- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/matrixConsole/matrixConsole/AppDelegate.m b/matrixConsole/matrixConsole/AppDelegate.m index 0814a4322..7bd9cd731 100644 --- a/matrixConsole/matrixConsole/AppDelegate.m +++ b/matrixConsole/matrixConsole/AppDelegate.m @@ -70,10 +70,16 @@ [self.errorNotification dismiss:NO]; self.errorNotification = nil; } + + // Suspend Matrix handler + [[MatrixHandler sharedHandler] pause]; } - (void)applicationWillEnterForeground:(UIApplication *)application { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + + // Resume Matrix handler + [[MatrixHandler sharedHandler] resume]; } - (void)applicationDidBecomeActive:(UIApplication *)application { diff --git a/matrixConsole/matrixConsole/MatrixHandler.h b/matrixConsole/matrixConsole/MatrixHandler.h index 164f90393..edb2da105 100644 --- a/matrixConsole/matrixConsole/MatrixHandler.h +++ b/matrixConsole/matrixConsole/MatrixHandler.h @@ -38,9 +38,12 @@ extern NSString *const kMatrixHandlerUnsupportedMessagePrefix; @property (nonatomic,readonly) BOOL isLogged; @property (nonatomic,readonly) BOOL isInitialSyncDone; +@property (nonatomic,readonly) BOOL isResumeDone; + (MatrixHandler *)sharedHandler; +- (void)pause; +- (void)resume; - (void)logout; // Flush and restore Matrix data diff --git a/matrixConsole/matrixConsole/MatrixHandler.m b/matrixConsole/matrixConsole/MatrixHandler.m index 0d73f4b95..6c40f244c 100644 --- a/matrixConsole/matrixConsole/MatrixHandler.m +++ b/matrixConsole/matrixConsole/MatrixHandler.m @@ -34,6 +34,7 @@ static MatrixHandler *sharedHandler = nil; } @property (nonatomic,readwrite) BOOL isInitialSyncDone; +@property (nonatomic,readwrite) BOOL isResumeDone; @property (strong, nonatomic) CustomAlert *mxNotification; @end @@ -58,6 +59,7 @@ static MatrixHandler *sharedHandler = nil; -(MatrixHandler *)init { if (self = [super init]) { _isInitialSyncDone = NO; + _isResumeDone = NO; notifyOpenSessionFailure = YES; // Read potential homeserver url in shared defaults object @@ -136,6 +138,7 @@ static MatrixHandler *sharedHandler = nil; // Launch mxSession [self.mxSession start:^{ self.isInitialSyncDone = YES; + _isResumeDone = YES; // Register listener to update user's information userUpdateListener = [self.mxSession.myUser listenToUserUpdate:^(MXEvent *event) { @@ -188,6 +191,7 @@ static MatrixHandler *sharedHandler = nil; } self.isInitialSyncDone = NO; + _isResumeDone = NO; notifyOpenSessionFailure = YES; } @@ -217,6 +221,21 @@ static MatrixHandler *sharedHandler = nil; return (self.accessToken != nil); } +- (void)pause { + if (self.mxSession) { + [self.mxSession pause]; + self.isResumeDone = NO; + } +} + +- (void)resume { + if (self.mxSession) { + [self.mxSession resume:^{ + self.isResumeDone = YES; + }]; + } +} + - (void)logout { // Reset access token (mxSession is closed by setter) self.accessToken = nil; diff --git a/matrixConsole/matrixConsole/ViewController/RecentsViewController.m b/matrixConsole/matrixConsole/ViewController/RecentsViewController.m index ac98ce84c..9daf02f32 100644 --- a/matrixConsole/matrixConsole/ViewController/RecentsViewController.m +++ b/matrixConsole/matrixConsole/ViewController/RecentsViewController.m @@ -105,6 +105,7 @@ // Refresh recents table [self configureView]; [[MatrixHandler sharedHandler] addObserver:self forKeyPath:@"isInitialSyncDone" options:0 context:nil]; + [[MatrixHandler sharedHandler] addObserver:self forKeyPath:@"isResumeDone" options:0 context:nil]; } - (void)viewWillDisappear:(BOOL)animated { @@ -124,6 +125,7 @@ _preSelectedRoomId = nil; [[MatrixHandler sharedHandler] removeObserver:self forKeyPath:@"isInitialSyncDone"]; + [[MatrixHandler sharedHandler] removeObserver:self forKeyPath:@"isResumeDone"]; } #pragma mark - @@ -189,8 +191,9 @@ recents = [NSMutableArray arrayWithArray:[mxHandler.mxSession recentsWithTypeIn:mxHandler.eventsFilterForMessages]]; // Reload table [self.tableView reloadData]; - [_activityIndicator stopAnimating]; - + if ([mxHandler isResumeDone]) { + [_activityIndicator stopAnimating]; + } // Check whether a room is preselected if (_preSelectedRoomId) { self.preSelectedRoomId = _preSelectedRoomId; @@ -203,7 +206,9 @@ // Reload table [self.tableView reloadData]; - [_activityIndicator stopAnimating]; + if ([mxHandler isResumeDone]) { + [_activityIndicator stopAnimating]; + } // Check whether a room is preselected if (_preSelectedRoomId) { @@ -245,6 +250,12 @@ dispatch_async(dispatch_get_main_queue(), ^{ [self configureView]; }); + } else if ([@"isResumeDone" isEqualToString:keyPath]) { + if ([[MatrixHandler sharedHandler] isResumeDone]) { + [_activityIndicator stopAnimating]; + } else { + [_activityIndicator startAnimating]; + } } } From 9bbfb2efdda6503fe449d104d0da23c406f97410 Mon Sep 17 00:00:00 2001 From: giomfo Date: Fri, 5 Dec 2014 13:50:14 +0100 Subject: [PATCH 07/18] Recents: improve activity indicator displayed during data sync --- .../matrixConsole/Base.lproj/Main.storyboard | 2 +- .../ViewController/RecentsViewController.m | 37 ++++++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/matrixConsole/matrixConsole/Base.lproj/Main.storyboard b/matrixConsole/matrixConsole/Base.lproj/Main.storyboard index 2068b497e..34f6a5f97 100644 --- a/matrixConsole/matrixConsole/Base.lproj/Main.storyboard +++ b/matrixConsole/matrixConsole/Base.lproj/Main.storyboard @@ -1024,7 +1024,7 @@ -