Merge remote-tracking branch 'origin/develop'

This commit is contained in:
manuroe 2014-12-09 14:24:47 +01:00
commit e4de36cfa1
17 changed files with 816 additions and 429 deletions

View file

@ -39,7 +39,10 @@
F0CEA5B119E6898800E47915 /* tab_home.ico in Resources */ = {isa = PBXBuildFile; fileRef = F0CEA5B019E6898800E47915 /* tab_home.ico */; }; F0CEA5B119E6898800E47915 /* tab_home.ico in Resources */ = {isa = PBXBuildFile; fileRef = F0CEA5B019E6898800E47915 /* tab_home.ico */; };
F0D3C30C1A011EF10000D49E /* AppSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = F0D3C30B1A011EF10000D49E /* AppSettings.m */; }; F0D3C30C1A011EF10000D49E /* AppSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = F0D3C30B1A011EF10000D49E /* AppSettings.m */; };
F0D3C30F1A01330F0000D49E /* SettingsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F0D3C30E1A01330F0000D49E /* SettingsTableViewCell.m */; }; F0D3C30F1A01330F0000D49E /* SettingsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F0D3C30E1A01330F0000D49E /* SettingsTableViewCell.m */; };
F0D942F61A31F3A300826CC1 /* RecentRoom.m in Sources */ = {isa = PBXBuildFile; fileRef = F0D942F51A31F3A300826CC1 /* RecentRoom.m */; };
F0E84D401A1F9AEC005F2E42 /* RecentsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F0E84D3F1A1F9AEC005F2E42 /* RecentsTableViewCell.m */; }; F0E84D401A1F9AEC005F2E42 /* RecentsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F0E84D3F1A1F9AEC005F2E42 /* RecentsTableViewCell.m */; };
F0F90C691A32596700455977 /* icon_search.png in Resources */ = {isa = PBXBuildFile; fileRef = F0F90C681A32596700455977 /* icon_search.png */; };
F0F90C6B1A325ABF00455977 /* icon_search@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F0F90C6A1A325ABF00455977 /* icon_search@2x.png */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -108,8 +111,12 @@
F0D3C30B1A011EF10000D49E /* AppSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppSettings.m; sourceTree = "<group>"; }; F0D3C30B1A011EF10000D49E /* AppSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppSettings.m; sourceTree = "<group>"; };
F0D3C30D1A01330F0000D49E /* SettingsTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsTableViewCell.h; sourceTree = "<group>"; }; F0D3C30D1A01330F0000D49E /* SettingsTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsTableViewCell.h; sourceTree = "<group>"; };
F0D3C30E1A01330F0000D49E /* SettingsTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsTableViewCell.m; sourceTree = "<group>"; }; F0D3C30E1A01330F0000D49E /* SettingsTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsTableViewCell.m; sourceTree = "<group>"; };
F0D942F41A31F3A300826CC1 /* RecentRoom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecentRoom.h; sourceTree = "<group>"; };
F0D942F51A31F3A300826CC1 /* RecentRoom.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RecentRoom.m; sourceTree = "<group>"; };
F0E84D3E1A1F9AEC005F2E42 /* RecentsTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecentsTableViewCell.h; sourceTree = "<group>"; }; F0E84D3E1A1F9AEC005F2E42 /* RecentsTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecentsTableViewCell.h; sourceTree = "<group>"; };
F0E84D3F1A1F9AEC005F2E42 /* RecentsTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RecentsTableViewCell.m; sourceTree = "<group>"; }; F0E84D3F1A1F9AEC005F2E42 /* RecentsTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RecentsTableViewCell.m; sourceTree = "<group>"; };
F0F90C681A32596700455977 /* icon_search.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_search.png; sourceTree = "<group>"; };
F0F90C6A1A325ABF00455977 /* icon_search@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_search@2x.png"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -153,6 +160,8 @@
F01628B519E298710071C473 /* Assets */ = { F01628B519E298710071C473 /* Assets */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
F0F90C6A1A325ABF00455977 /* icon_search@2x.png */,
F0F90C681A32596700455977 /* icon_search.png */,
F08B6FCB1A1DE7F80094A35B /* matrixConsole.jpg */, F08B6FCB1A1DE7F80094A35B /* matrixConsole.jpg */,
F02BCE221A1A5A2B00543B47 /* play.png */, F02BCE221A1A5A2B00543B47 /* play.png */,
F024098119E7D177006E741B /* tab_recents@2x.png */, F024098119E7D177006E741B /* tab_recents@2x.png */,
@ -204,6 +213,8 @@
F0465AF71A251F85003639F9 /* Model */ = { F0465AF71A251F85003639F9 /* Model */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
F0D942F41A31F3A300826CC1 /* RecentRoom.h */,
F0D942F51A31F3A300826CC1 /* RecentRoom.m */,
F0465AF81A251F85003639F9 /* RoomMessage.h */, F0465AF81A251F85003639F9 /* RoomMessage.h */,
F0465AF91A251F85003639F9 /* RoomMessage.m */, F0465AF91A251F85003639F9 /* RoomMessage.m */,
F01A0FF11A27314B009FAE2F /* RoomMessageComponent.h */, F01A0FF11A27314B009FAE2F /* RoomMessageComponent.h */,
@ -372,7 +383,9 @@
F0CEA5AF19E6895E00E47915 /* tab_recents.png in Resources */, F0CEA5AF19E6895E00E47915 /* tab_recents.png in Resources */,
F01628C319E29C660071C473 /* logo.png in Resources */, F01628C319E29C660071C473 /* logo.png in Resources */,
F01628C119E29C660071C473 /* default-profile.png in Resources */, F01628C119E29C660071C473 /* default-profile.png in Resources */,
F0F90C6B1A325ABF00455977 /* icon_search@2x.png in Resources */,
F0CEA5AE19E6895E00E47915 /* logoHighRes.png in Resources */, F0CEA5AE19E6895E00E47915 /* logoHighRes.png in Resources */,
F0F90C691A32596700455977 /* icon_search.png in Resources */,
F0CEA5B119E6898800E47915 /* tab_home.ico in Resources */, F0CEA5B119E6898800E47915 /* tab_home.ico in Resources */,
F07A80E619DD9DE700B621A1 /* Images.xcassets in Resources */, F07A80E619DD9DE700B621A1 /* Images.xcassets in Resources */,
F08B6FCC1A1DE7F80094A35B /* matrixConsole.jpg in Resources */, F08B6FCC1A1DE7F80094A35B /* matrixConsole.jpg in Resources */,
@ -435,6 +448,7 @@
F03EF5FA19F171EB00A0EE52 /* RoomViewController.m in Sources */, F03EF5FA19F171EB00A0EE52 /* RoomViewController.m in Sources */,
F03EF5F819F171EB00A0EE52 /* MasterTabBarController.m in Sources */, F03EF5F819F171EB00A0EE52 /* MasterTabBarController.m in Sources */,
F03EF5F619F171EB00A0EE52 /* HomeViewController.m in Sources */, F03EF5F619F171EB00A0EE52 /* HomeViewController.m in Sources */,
F0D942F61A31F3A300826CC1 /* RecentRoom.m in Sources */,
F03EF60219F19E7C00A0EE52 /* MediaManager.m in Sources */, F03EF60219F19E7C00A0EE52 /* MediaManager.m in Sources */,
F03EF5F919F171EB00A0EE52 /* RecentsViewController.m in Sources */, F03EF5F919F171EB00A0EE52 /* RecentsViewController.m in Sources */,
F0465AFA1A251F85003639F9 /* RoomMessage.m in Sources */, F0465AFA1A251F85003639F9 /* RoomMessage.m in Sources */,

View file

@ -70,10 +70,16 @@
[self.errorNotification dismiss:NO]; [self.errorNotification dismiss:NO];
self.errorNotification = nil; self.errorNotification = nil;
} }
// Suspend Matrix handler
[[MatrixHandler sharedHandler] pause];
} }
- (void)applicationWillEnterForeground:(UIApplication *)application { - (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. // 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 { - (void)applicationDidBecomeActive:(UIApplication *)application {

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

View file

@ -1024,7 +1024,7 @@
</connections> </connections>
</tableViewController> </tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Rux-fX-hf1" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="Rux-fX-hf1" sceneMemberID="firstResponder"/>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" id="4dn-O0-IJ0"> <activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="white" id="4dn-O0-IJ0">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/> <rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</activityIndicatorView> </activityIndicatorView>

View file

@ -27,6 +27,7 @@ extern NSString *const kMatrixHandlerUnsupportedMessagePrefix;
@property (strong, nonatomic) NSString *homeServer; @property (strong, nonatomic) NSString *homeServer;
@property (strong, nonatomic) NSString *userLogin; @property (strong, nonatomic) NSString *userLogin;
@property (strong, nonatomic) NSString *userId; @property (strong, nonatomic) NSString *userId;
@property (strong, nonatomic, readonly) NSString *localPartFromUserId;
@property (strong, nonatomic) NSString *accessToken; @property (strong, nonatomic) NSString *accessToken;
// The type of events to display // The type of events to display
@ -38,9 +39,12 @@ extern NSString *const kMatrixHandlerUnsupportedMessagePrefix;
@property (nonatomic,readonly) BOOL isLogged; @property (nonatomic,readonly) BOOL isLogged;
@property (nonatomic,readonly) BOOL isInitialSyncDone; @property (nonatomic,readonly) BOOL isInitialSyncDone;
@property (nonatomic,readonly) BOOL isResumeDone;
+ (MatrixHandler *)sharedHandler; + (MatrixHandler *)sharedHandler;
- (void)pause;
- (void)resume;
- (void)logout; - (void)logout;
// Flush and restore Matrix data // Flush and restore Matrix data
@ -50,6 +54,10 @@ extern NSString *const kMatrixHandlerUnsupportedMessagePrefix;
- (BOOL)isSupportedAttachment:(MXEvent*)event; - (BOOL)isSupportedAttachment:(MXEvent*)event;
- (BOOL)isEmote:(MXEvent*)event; - (BOOL)isEmote:(MXEvent*)event;
// Note: the room state expected by the 3 following methods is the room state right before handling the event
- (NSString*)senderDisplayNameForEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState;
- (NSString*)senderAvatarUrlForEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState;
- (NSString*)displayTextForEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState inSubtitleMode:(BOOL)isSubtitle; - (NSString*)displayTextForEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState inSubtitleMode:(BOOL)isSubtitle;
@end @end

View file

@ -28,12 +28,13 @@ static MatrixHandler *sharedHandler = nil;
BOOL notifyOpenSessionFailure; BOOL notifyOpenSessionFailure;
// Handle user's settings change // Handle user's settings change
id roomMembersListener; id userUpdateListener;
// Handle events notification // Handle events notification
id eventsListener; id eventsListener;
} }
@property (nonatomic,readwrite) BOOL isInitialSyncDone; @property (nonatomic,readwrite) BOOL isInitialSyncDone;
@property (nonatomic,readwrite) BOOL isResumeDone;
@property (strong, nonatomic) CustomAlert *mxNotification; @property (strong, nonatomic) CustomAlert *mxNotification;
@end @end
@ -58,6 +59,7 @@ static MatrixHandler *sharedHandler = nil;
-(MatrixHandler *)init { -(MatrixHandler *)init {
if (self = [super init]) { if (self = [super init]) {
_isInitialSyncDone = NO; _isInitialSyncDone = NO;
_isResumeDone = NO;
notifyOpenSessionFailure = YES; notifyOpenSessionFailure = YES;
// Read potential homeserver url in shared defaults object // Read potential homeserver url in shared defaults object
@ -109,7 +111,7 @@ static MatrixHandler *sharedHandler = nil;
self.mxSession = [[MXSession alloc] initWithMatrixRestClient:self.mxRestClient andStore:store]; self.mxSession = [[MXSession alloc] initWithMatrixRestClient:self.mxRestClient andStore:store];
// Check here whether the app user wants to display all the events // Check here whether the app user wants to display all the events
if ([[AppSettings sharedSettings] displayAllEvents]) { if ([[AppSettings sharedSettings] displayAllEvents]) {
// Use a filter to retrieve all the events // Use a filter to retrieve all the events (except kMXEventTypeStringPresence which are not related to a specific room)
self.eventsFilterForMessages = @[ self.eventsFilterForMessages = @[
kMXEventTypeStringRoomName, kMXEventTypeStringRoomName,
kMXEventTypeStringRoomTopic, kMXEventTypeStringRoomTopic,
@ -117,13 +119,9 @@ static MatrixHandler *sharedHandler = nil;
kMXEventTypeStringRoomCreate, kMXEventTypeStringRoomCreate,
kMXEventTypeStringRoomJoinRules, kMXEventTypeStringRoomJoinRules,
kMXEventTypeStringRoomPowerLevels, kMXEventTypeStringRoomPowerLevels,
kMXEventTypeStringRoomAddStateLevel,
kMXEventTypeStringRoomSendEventLevel,
kMXEventTypeStringRoomOpsLevel,
kMXEventTypeStringRoomAliases, kMXEventTypeStringRoomAliases,
kMXEventTypeStringRoomMessage, kMXEventTypeStringRoomMessage,
kMXEventTypeStringRoomMessageFeedback, kMXEventTypeStringRoomMessageFeedback
kMXEventTypeStringPresence
]; ];
} }
else { else {
@ -139,21 +137,16 @@ static MatrixHandler *sharedHandler = nil;
// Launch mxSession // Launch mxSession
[self.mxSession start:^{ [self.mxSession start:^{
self.isInitialSyncDone = YES; self.isInitialSyncDone = YES;
_isResumeDone = YES;
// Register listener to update user's information // Register listener to update user's information
roomMembersListener = [self.mxSession listenToEventsOfTypes:@[kMXEventTypeStringPresence] onEvent:^(MXEvent *event, MXEventDirection direction, id customObject) { userUpdateListener = [self.mxSession.myUser listenToUserUpdate:^(MXEvent *event) {
// Consider only live events // Update local storage
if (direction == MXEventDirectionForwards) { if (![self.userDisplayName isEqualToString:event.content[@"displayname"]]) {
// Consider only events from app user self.userDisplayName = event.content[@"displayname"];
if ([event.userId isEqualToString:self.userId]) { }
// Update local storage if (![self.userPictureURL isEqualToString:event.content[@"avatar_url"]]) {
if (![self.userDisplayName isEqualToString:event.content[@"displayname"]]) { self.userPictureURL = event.content[@"avatar_url"];
self.userDisplayName = event.content[@"displayname"];
}
if (![self.userPictureURL isEqualToString:event.content[@"avatar_url"]]) {
self.userPictureURL = event.content[@"avatar_url"];
}
}
} }
}]; }];
@ -182,9 +175,9 @@ static MatrixHandler *sharedHandler = nil;
[self.mxSession removeListener:eventsListener]; [self.mxSession removeListener:eventsListener];
eventsListener = nil; eventsListener = nil;
} }
if (roomMembersListener) { if (userUpdateListener) {
[self.mxSession removeListener:roomMembersListener]; [self.mxSession.myUser removeListener:userUpdateListener];
roomMembersListener = nil; userUpdateListener = nil;
} }
[self.mxSession close]; [self.mxSession close];
self.mxSession = nil; self.mxSession = nil;
@ -197,6 +190,7 @@ static MatrixHandler *sharedHandler = nil;
} }
self.isInitialSyncDone = NO; self.isInitialSyncDone = NO;
_isResumeDone = NO;
notifyOpenSessionFailure = YES; notifyOpenSessionFailure = YES;
} }
@ -226,9 +220,25 @@ static MatrixHandler *sharedHandler = nil;
return (self.accessToken != 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 { - (void)logout {
// Reset access token (mxSession is closed by setter) // Reset access token (mxSession is closed by setter)
self.accessToken = nil; self.accessToken = nil;
self.userId = nil;
// Reset local storage of user's settings // Reset local storage of user's settings
self.userDisplayName = @""; self.userDisplayName = @"";
@ -247,8 +257,8 @@ static MatrixHandler *sharedHandler = nil;
if (isEnabled) { if (isEnabled) {
// Register events listener // Register events listener
eventsListener = [self.mxSession listenToEventsOfTypes:self.eventsFilterForMessages onEvent:^(MXEvent *event, MXEventDirection direction, id customObject) { eventsListener = [self.mxSession listenToEventsOfTypes:self.eventsFilterForMessages onEvent:^(MXEvent *event, MXEventDirection direction, id customObject) {
// Consider only live event (Ignore presence event) // Consider only live event
if (direction == MXEventDirectionForwards && (event.eventType != MXEventTypePresence)) { if (direction == MXEventDirectionForwards) {
MXRoomState* roomState = (MXRoomState*)customObject; MXRoomState* roomState = (MXRoomState*)customObject;
// If we are running on background, show a local notif // If we are running on background, show a local notif
if (UIApplicationStateBackground == [UIApplication sharedApplication].applicationState) if (UIApplicationStateBackground == [UIApplication sharedApplication].applicationState)
@ -258,8 +268,10 @@ static MatrixHandler *sharedHandler = nil;
localNotification.hasAction = YES; localNotification.hasAction = YES;
[localNotification setAlertBody:[self displayTextForEvent:event withRoomState:roomState inSubtitleMode:YES]]; [localNotification setAlertBody:[self displayTextForEvent:event withRoomState:roomState inSubtitleMode:YES]];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification]; [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
} else if ([[AppDelegate theDelegate].masterTabBarController.visibleRoomId isEqualToString:event.roomId] == NO) { } else if (![event.userId isEqualToString:self.userId]
// The concerned room is not presently visible, we display a notification by removing existing one (if any) && ![[AppDelegate theDelegate].masterTabBarController.visibleRoomId isEqualToString:event.roomId]) {
// The sender is not the user and the concerned room is not presently visible,
// we display a notification by removing existing one (if any)
if (self.mxNotification) { if (self.mxNotification) {
[self.mxNotification dismiss:NO]; [self.mxNotification dismiss:NO];
} }
@ -347,12 +359,28 @@ static MatrixHandler *sharedHandler = nil;
- (void)setUserId:(NSString *)inUserId { - (void)setUserId:(NSString *)inUserId {
if (inUserId.length) { if (inUserId.length) {
[[NSUserDefaults standardUserDefaults] setObject:inUserId forKey:@"userid"]; [[NSUserDefaults standardUserDefaults] setObject:inUserId forKey:@"userid"];
// Deduce local userid
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"localuserid"];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"@(.*):\\w+" options:NSRegularExpressionCaseInsensitive error:nil];
NSTextCheckingResult *match = [regex firstMatchInString:inUserId options:0 range:NSMakeRange(0, [inUserId length])];
if (match.numberOfRanges == 2) {
NSString* localId = [inUserId substringWithRange:[match rangeAtIndex:1]];
if (localId) {
[[NSUserDefaults standardUserDefaults] setObject:localId forKey:@"localuserid"];
}
}
} else { } else {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"userid"]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"userid"];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"localuserid"];
} }
[[NSUserDefaults standardUserDefaults] synchronize]; [[NSUserDefaults standardUserDefaults] synchronize];
} }
- (NSString *)localPartFromUserId {
return [[NSUserDefaults standardUserDefaults] objectForKey:@"localuserid"];
}
- (NSString *)accessToken { - (NSString *)accessToken {
return [[NSUserDefaults standardUserDefaults] objectForKey:@"accesstoken"]; return [[NSUserDefaults standardUserDefaults] objectForKey:@"accesstoken"];
} }
@ -438,10 +466,32 @@ static MatrixHandler *sharedHandler = nil;
} }
- (NSString*)senderDisplayNameForEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState {
// Consider first the current display name defined in provided room state (Note: this room state is supposed to not take the new event into account)
NSString *senderDisplayName = [roomState memberName:event.userId];
// Check whether this sender name is updated by the current event (This happens in case of new joined member)
if ([event.content[@"displayname"] length]) {
// Use the actual display name
senderDisplayName = event.content[@"displayname"];
}
return senderDisplayName;
}
- (NSString*)senderAvatarUrlForEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState {
// Consider first the avatar url defined in provided room state (Note: this room state is supposed to not take the new event into account)
NSString *senderAvatarUrl = [roomState memberWithUserId:event.userId].avatarUrl;
// Check whether this avatar url is updated by the current event (This happens in case of new joined member)
if ([event.content[@"avatar_url"] length]) {
// Use the actual display name
senderAvatarUrl = event.content[@"avatar_url"];
}
return senderAvatarUrl;
}
- (NSString*)displayTextForEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState inSubtitleMode:(BOOL)isSubtitle { - (NSString*)displayTextForEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState inSubtitleMode:(BOOL)isSubtitle {
NSString *displayText = nil; NSString *displayText = nil;
// Prepare display name for concerned users // Prepare display name for concerned users
NSString *memberDisplayName = [roomState memberName:event.userId]; NSString *senderDisplayName = [self senderDisplayNameForEvent:event withRoomState:roomState];
NSString *targetDisplayName = nil; NSString *targetDisplayName = nil;
if (event.stateKey) { if (event.stateKey) {
targetDisplayName = [roomState memberName:event.stateKey]; targetDisplayName = [roomState memberName:event.stateKey];
@ -449,11 +499,11 @@ static MatrixHandler *sharedHandler = nil;
switch (event.eventType) { switch (event.eventType) {
case MXEventTypeRoomName: { case MXEventTypeRoomName: {
displayText = [NSString stringWithFormat:@"%@ changed the room name to: %@", memberDisplayName, event.content[@"name"]]; displayText = [NSString stringWithFormat:@"%@ changed the room name to: %@", senderDisplayName, event.content[@"name"]];
break; break;
} }
case MXEventTypeRoomTopic: { case MXEventTypeRoomTopic: {
displayText = [NSString stringWithFormat:@"%@ changed the topic to: %@", memberDisplayName, event.content[@"topic"]]; displayText = [NSString stringWithFormat:@"%@ changed the topic to: %@", senderDisplayName, event.content[@"topic"]];
break; break;
} }
case MXEventTypeRoomMember: { case MXEventTypeRoomMember: {
@ -494,30 +544,30 @@ static MatrixHandler *sharedHandler = nil;
if (displayText) { if (displayText) {
displayText = [NSString stringWithFormat:@"%@ (picture profile was changed too)", displayText]; displayText = [NSString stringWithFormat:@"%@ (picture profile was changed too)", displayText];
} else { } else {
displayText = [NSString stringWithFormat:@"%@ changed their picture profile", memberDisplayName]; displayText = [NSString stringWithFormat:@"%@ changed their picture profile", senderDisplayName];
} }
} }
} else { } else {
// Consider here a membership change // Consider here a membership change
if ([membership isEqualToString:@"invite"]) { if ([membership isEqualToString:@"invite"]) {
displayText = [NSString stringWithFormat:@"%@ invited %@", memberDisplayName, targetDisplayName]; displayText = [NSString stringWithFormat:@"%@ invited %@", senderDisplayName, targetDisplayName];
} else if ([membership isEqualToString:@"join"]) { } else if ([membership isEqualToString:@"join"]) {
displayText = [NSString stringWithFormat:@"%@ joined", memberDisplayName]; displayText = [NSString stringWithFormat:@"%@ joined", senderDisplayName];
} else if ([membership isEqualToString:@"leave"]) { } else if ([membership isEqualToString:@"leave"]) {
if ([event.userId isEqualToString:event.stateKey]) { if ([event.userId isEqualToString:event.stateKey]) {
displayText = [NSString stringWithFormat:@"%@ left", memberDisplayName]; displayText = [NSString stringWithFormat:@"%@ left", senderDisplayName];
} else if (prevMembership) { } else if (prevMembership) {
if ([prevMembership isEqualToString:@"join"] || [prevMembership isEqualToString:@"invite"]) { if ([prevMembership isEqualToString:@"join"] || [prevMembership isEqualToString:@"invite"]) {
displayText = [NSString stringWithFormat:@"%@ kicked %@", memberDisplayName, targetDisplayName]; displayText = [NSString stringWithFormat:@"%@ kicked %@", senderDisplayName, targetDisplayName];
if (event.content[@"reason"]) { if (event.content[@"reason"]) {
displayText = [NSString stringWithFormat:@"%@: %@", displayText, event.content[@"reason"]]; displayText = [NSString stringWithFormat:@"%@: %@", displayText, event.content[@"reason"]];
} }
} else if ([prevMembership isEqualToString:@"ban"]) { } else if ([prevMembership isEqualToString:@"ban"]) {
displayText = [NSString stringWithFormat:@"%@ unbanned %@", memberDisplayName, targetDisplayName]; displayText = [NSString stringWithFormat:@"%@ unbanned %@", senderDisplayName, targetDisplayName];
} }
} }
} else if ([membership isEqualToString:@"ban"]) { } else if ([membership isEqualToString:@"ban"]) {
displayText = [NSString stringWithFormat:@"%@ banned %@", memberDisplayName, targetDisplayName]; displayText = [NSString stringWithFormat:@"%@ banned %@", senderDisplayName, targetDisplayName];
if (event.content[@"reason"]) { if (event.content[@"reason"]) {
displayText = [NSString stringWithFormat:@"%@: %@", displayText, event.content[@"reason"]]; displayText = [NSString stringWithFormat:@"%@: %@", displayText, event.content[@"reason"]];
} }
@ -559,6 +609,9 @@ static MatrixHandler *sharedHandler = nil;
if (event.content[@"redact"]) { if (event.content[@"redact"]) {
displayText = [NSString stringWithFormat:@"%@\r\n\u2022 redact: %@", displayText, 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]; displayText = [NSString stringWithFormat:@"%@\r\nThe minimum power levels related to events are:", displayText];
NSDictionary *events = event.content[@"events"]; NSDictionary *events = event.content[@"events"];
@ -573,27 +626,6 @@ static MatrixHandler *sharedHandler = nil;
} }
break; 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: { case MXEventTypeRoomAliases: {
NSArray *aliases = event.content[@"aliases"]; NSArray *aliases = event.content[@"aliases"];
if (aliases) { if (aliases) {
@ -606,7 +638,7 @@ static MatrixHandler *sharedHandler = nil;
displayText = [event.content[@"body"] isKindOfClass:[NSString class]] ? event.content[@"body"] : nil; displayText = [event.content[@"body"] isKindOfClass:[NSString class]] ? event.content[@"body"] : nil;
if ([msgtype isEqualToString:kMXMessageTypeEmote]) { if ([msgtype isEqualToString:kMXMessageTypeEmote]) {
displayText = [NSString stringWithFormat:@"* %@ %@", memberDisplayName, displayText]; displayText = [NSString stringWithFormat:@"* %@ %@", senderDisplayName, displayText];
} else if ([msgtype isEqualToString:kMXMessageTypeImage]) { } else if ([msgtype isEqualToString:kMXMessageTypeImage]) {
displayText = displayText? displayText : @"image attachment"; displayText = displayText? displayText : @"image attachment";
// Check attachment validity // Check attachment validity
@ -654,7 +686,7 @@ static MatrixHandler *sharedHandler = nil;
// Check whether the sender name has to be added // Check whether the sender name has to be added
if (isSubtitle && [msgtype isEqualToString:kMXMessageTypeEmote] == NO) { if (isSubtitle && [msgtype isEqualToString:kMXMessageTypeEmote] == NO) {
displayText = [NSString stringWithFormat:@"%@: %@", memberDisplayName, displayText]; displayText = [NSString stringWithFormat:@"%@: %@", senderDisplayName, displayText];
} }
break; break;

View file

@ -0,0 +1,28 @@
/*
Copyright 2014 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixSDK/MatrixSDK.h>
@interface RecentRoom : NSObject
@property (nonatomic, readonly) NSString *roomId;
@property (nonatomic, readonly) MXEvent *lastEvent;
@property (nonatomic, readonly) NSUInteger unreadCount;
- (id)initWithLastEvent:(MXEvent*)event andMarkAsUnread:(BOOL)isUnread;
- (void)updateWithLastEvent:(MXEvent*)event andMarkAsUnread:(BOOL)isUnread;
- (void)resetUnreadCount;
@end

View file

@ -0,0 +1,50 @@
/*
Copyright 2014 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "RecentRoom.h"
@implementation RecentRoom
- (id)initWithLastEvent:(MXEvent*)event andMarkAsUnread:(BOOL)isUnread {
if (self = [super init]) {
_lastEvent = event;
_unreadCount = isUnread ? 1 : 0;
}
return self;
}
- (void)updateWithLastEvent:(MXEvent*)event andMarkAsUnread:(BOOL)isUnread {
_lastEvent = event;
if (isUnread) {
_unreadCount ++;
}
}
- (void)resetUnreadCount {
_unreadCount = 0;
}
- (void)dealloc {
_lastEvent = nil;
}
#pragma mark -
- (NSString*)roomId {
return _lastEvent.roomId;
}
@end

View file

@ -36,15 +36,16 @@ static NSAttributedString *messageSeparator = nil;
- (id)initWithEvent:(MXEvent*)event andRoomState:(MXRoomState*)roomState { - (id)initWithEvent:(MXEvent*)event andRoomState:(MXRoomState*)roomState {
if (self = [super init]) { if (self = [super init]) {
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
_senderId = event.userId; _senderId = event.userId;
_senderName = [roomState memberName:event.userId]; _senderName = [mxHandler senderDisplayNameForEvent:event withRoomState:roomState];
_senderAvatarUrl = [roomState memberWithUserId:event.userId].avatarUrl; _senderAvatarUrl = [mxHandler senderAvatarUrlForEvent:event withRoomState:roomState];
_contentSize = CGSizeZero; _contentSize = CGSizeZero;
currentAttributedTextMsg = nil; currentAttributedTextMsg = nil;
// Set message type (consider text by default), and check attachment if any // Set message type (consider text by default), and check attachment if any
_messageType = RoomMessageTypeText; _messageType = RoomMessageTypeText;
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
if ([mxHandler isSupportedAttachment:event]) { if ([mxHandler isSupportedAttachment:event]) {
// Note: event.eventType is equal here to MXEventTypeRoomMessage // Note: event.eventType is equal here to MXEventTypeRoomMessage
NSString *msgtype = event.content[@"msgtype"]; NSString *msgtype = event.content[@"msgtype"];
@ -101,12 +102,14 @@ static NSAttributedString *messageSeparator = nil;
} }
// Check sender information // Check sender information
if ((_senderName || [roomState memberName:event.userId]) && NSString *eventSenderName = [mxHandler senderDisplayNameForEvent:event withRoomState:roomState];
([_senderName isEqualToString:[roomState memberName:event.userId]] == NO)) { NSString *eventSenderAvatar = [mxHandler senderAvatarUrlForEvent:event withRoomState:roomState];
if ((_senderName || eventSenderName) &&
([_senderName isEqualToString:eventSenderName] == NO)) {
return NO; return NO;
} }
if ((_senderAvatarUrl || [roomState memberWithUserId:event.userId].avatarUrl) && if ((_senderAvatarUrl || eventSenderAvatar) &&
([_senderAvatarUrl isEqualToString:[roomState memberWithUserId:event.userId].avatarUrl] == NO)) { ([_senderAvatarUrl isEqualToString:eventSenderAvatar] == NO)) {
return NO; return NO;
} }

View file

@ -21,7 +21,7 @@ extern NSString *const kFailedEventId;
typedef enum : NSUInteger { typedef enum : NSUInteger {
RoomMessageComponentStyleDefault, RoomMessageComponentStyleDefault,
RoomMessageComponentStyleHighlighted, RoomMessageComponentStyleBing,
RoomMessageComponentStyleInProgress, RoomMessageComponentStyleInProgress,
RoomMessageComponentStyleFailed, RoomMessageComponentStyleFailed,
RoomMessageComponentStyleUnsupported RoomMessageComponentStyleUnsupported

View file

@ -33,7 +33,7 @@ NSString *const kFailedEventId = @"failedEventId";
_eventId = event.eventId; _eventId = event.eventId;
_height = 0; _height = 0;
NSString *senderName = [roomState memberName:event.userId]; NSString *senderName = [mxHandler senderDisplayNameForEvent:event withRoomState:roomState];
_startsWithSenderName = ([textMessage hasPrefix:senderName] || [mxHandler isEmote:event]); _startsWithSenderName = ([textMessage hasPrefix:senderName] || [mxHandler isEmote:event]);
// Set date time text label // Set date time text label
@ -43,21 +43,22 @@ NSString *const kFailedEventId = @"failedEventId";
_date = nil; _date = nil;
} }
// Set state event flag
_isStateEvent = (event.eventType != MXEventTypeRoomMessage);
// Set style // Set style
BOOL isIncomingMsg = ([event.userId isEqualToString:mxHandler.userId] == NO); BOOL isIncomingMsg = ([event.userId isEqualToString:mxHandler.userId] == NO);
if ([textMessage hasPrefix:kMatrixHandlerUnsupportedMessagePrefix]) { if ([textMessage hasPrefix:kMatrixHandlerUnsupportedMessagePrefix]) {
_style = RoomMessageComponentStyleUnsupported; _style = RoomMessageComponentStyleUnsupported;
} else if ([_eventId hasPrefix:kFailedEventId]) { } else if ([_eventId hasPrefix:kFailedEventId]) {
_style = RoomMessageComponentStyleFailed; _style = RoomMessageComponentStyleFailed;
} else if (isIncomingMsg && ([textMessage rangeOfString:mxHandler.userDisplayName options:NSCaseInsensitiveSearch].location != NSNotFound || [textMessage rangeOfString:mxHandler.userId options:NSCaseInsensitiveSearch].location != NSNotFound)) { } else if (isIncomingMsg && !_isStateEvent && [self containsBingWord]) {
_style = RoomMessageComponentStyleHighlighted; _style = RoomMessageComponentStyleBing;
} else if (!isIncomingMsg && [_eventId hasPrefix:kLocalEchoEventIdPrefix]) { } else if (!isIncomingMsg && [_eventId hasPrefix:kLocalEchoEventIdPrefix]) {
_style = RoomMessageComponentStyleInProgress; _style = RoomMessageComponentStyleInProgress;
} else { } else {
_style = RoomMessageComponentStyleDefault; _style = RoomMessageComponentStyleDefault;
} }
_isStateEvent = (event.eventType != MXEventTypeRoomMessage);
} else { } else {
// Ignore this event // Ignore this event
self = nil; self = nil;
@ -66,6 +67,29 @@ NSString *const kFailedEventId = @"failedEventId";
return self; return self;
} }
- (BOOL)containsBingWord {
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
NSString *pattern = nil;
if (mxHandler.userDisplayName.length) {
pattern = [NSString stringWithFormat:@"\\b%@\\b", mxHandler.userDisplayName];
}
if (mxHandler.localPartFromUserId.length) {
if (pattern) {
pattern = [NSString stringWithFormat:@"(%@|\\b%@\\b)", pattern, mxHandler.localPartFromUserId];
} else {
pattern = [NSString stringWithFormat:@"\\b%@\\b", mxHandler.localPartFromUserId];
}
}
if (pattern) {
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil];
if ([regex numberOfMatchesInString:_textMessage options:0 range:NSMakeRange(0, [_textMessage length])]) {
return YES;
}
}
return NO;
}
- (NSDictionary*)stringAttributes { - (NSDictionary*)stringAttributes {
UIColor *textColor; UIColor *textColor;
UIFont *font; UIFont *font;
@ -74,7 +98,7 @@ NSString *const kFailedEventId = @"failedEventId";
case RoomMessageComponentStyleDefault: case RoomMessageComponentStyleDefault:
textColor = [UIColor blackColor]; textColor = [UIColor blackColor];
break; break;
case RoomMessageComponentStyleHighlighted: case RoomMessageComponentStyleBing:
textColor = [UIColor blueColor]; textColor = [UIColor blueColor];
break; break;
case RoomMessageComponentStyleInProgress: case RoomMessageComponentStyleInProgress:

View file

@ -51,38 +51,23 @@
self.backgroundColor = [UIColor whiteColor]; self.backgroundColor = [UIColor whiteColor];
// Handle power level display // Handle power level display
self.userPowerLevel.hidden = NO; self.userPowerLevel.hidden = NO;
NSDictionary *powerLevels; MXRoomPowerLevels *roomPowerLevels = room.state.powerLevels;
if (room.state.powerLevels[@"users"]){
// In Matrix 0.5, users power levels are listed under the `users` dictionnary int maxLevel = 0;
powerLevels = room.state.powerLevels[@"users"]; for (NSString *powerLevel in roomPowerLevels.users.allValues) {
} int level = [powerLevel intValue];
else { if (level > maxLevel) {
// @TODO: Remove this backward compatibility maxLevel = level;
powerLevels = room.state.powerLevels;
}
if (powerLevels) {
int maxLevel = 0;
for (NSString *powerLevel in powerLevels.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) { if (roomMember.membership == MXMembershipInvite) {
self.lastActiveAgoLabel.backgroundColor = [UIColor lightGrayColor]; self.lastActiveAgoLabel.backgroundColor = [UIColor lightGrayColor];
self.lastActiveAgoLabel.text = @"invited"; self.lastActiveAgoLabel.text = @"invited";

View file

@ -16,7 +16,7 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@interface HomeViewController : UITableViewController <UITextFieldDelegate> @interface HomeViewController : UITableViewController <UITextFieldDelegate, UISearchBarDelegate>
@end @end

View file

@ -19,12 +19,17 @@
#import "MatrixHandler.h" #import "MatrixHandler.h"
#import "AppDelegate.h" #import "AppDelegate.h"
@interface HomeViewController () { @interface HomeViewController () {
NSArray *publicRooms; NSArray *publicRooms;
// List of public room names to highlight in displayed list // List of public room names to highlight in displayed list
NSArray* highlightedPublicRooms; NSArray* highlightedPublicRooms;
// Search in public room
UISearchBar *recentsSearchBar;
NSMutableArray *filteredPublicRooms;
BOOL searchBarShouldEndEditing;
UIView *savedTableHeaderView;
} }
@property (weak, nonatomic) IBOutlet UITableView *publicRoomsTable; @property (weak, nonatomic) IBOutlet UITableView *publicRoomsTable;
@ -64,6 +69,10 @@
- (void)dealloc{ - (void)dealloc{
publicRooms = nil; publicRooms = nil;
highlightedPublicRooms = nil; highlightedPublicRooms = nil;
recentsSearchBar = nil;
filteredPublicRooms = nil;
savedTableHeaderView = nil;
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
@ -84,6 +93,10 @@
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated]; [super viewWillDisappear:animated];
// Leave potential search session
if (recentsSearchBar) {
[self searchBarCancelButtonClicked:recentsSearchBar];
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil];
} }
@ -110,6 +123,29 @@
} }
- (void)search:(id)sender {
if (!recentsSearchBar) {
// Check whether there are data in which search
if (publicRooms.count) {
// Create search bar
recentsSearchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
recentsSearchBar.showsCancelButton = YES;
recentsSearchBar.returnKeyType = UIReturnKeyDone;
recentsSearchBar.delegate = self;
[recentsSearchBar becomeFirstResponder];
// Hide table header during search session
savedTableHeaderView = self.tableView.tableHeaderView;
self.tableView.tableHeaderView = nil;
// Reload table in order to display search bar as section header
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[self.tableView reloadData];
}
} else {
[self searchBarCancelButtonClicked: recentsSearchBar];
}
}
- (void)dismissKeyboard { - (void)dismissKeyboard {
// Hide the keyboard // Hide the keyboard
[_roomNameTextField resignFirstResponder]; [_roomNameTextField resignFirstResponder];
@ -289,32 +325,64 @@
} }
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (filteredPublicRooms) {
return filteredPublicRooms.count;
}
return publicRooms.count; return publicRooms.count;
} }
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
if (recentsSearchBar) {
return (recentsSearchBar.frame.size.height + 40);
}
return 40;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UILabel *sectionHeader = [[UILabel alloc] initWithFrame:[tableView rectForHeaderInSection:section]]; UIView *sectionHeader = [[UIView alloc] initWithFrame:[tableView rectForHeaderInSection:section]];
sectionHeader.font = [UIFont boldSystemFontOfSize:16];
sectionHeader.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.0]; sectionHeader.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.0];
UILabel *sectionLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, sectionHeader.frame.size.width, 40)];
sectionLabel.font = [UIFont boldSystemFontOfSize:16];
sectionLabel.backgroundColor = [UIColor clearColor];
[sectionHeader addSubview:sectionLabel];
if (publicRooms) { if (publicRooms) {
NSString *homeserver = [MatrixHandler sharedHandler].homeServerURL; NSString *homeserver = [MatrixHandler sharedHandler].homeServerURL;
if (homeserver.length) { if (homeserver.length) {
sectionHeader.text = [NSString stringWithFormat:@" Public Rooms (at %@):", homeserver]; sectionLabel.text = [NSString stringWithFormat:@" Public Rooms (at %@):", homeserver];
} else { } else {
sectionHeader.text = @" Public Rooms:"; sectionLabel.text = @" Public Rooms:";
}
UIButton *searchButton = [UIButton buttonWithType:UIButtonTypeCustom];
[searchButton setImage:[UIImage imageNamed:@"icon_search"] forState:UIControlStateNormal];
[searchButton setImage:[UIImage imageNamed:@"icon_search"] forState:UIControlStateHighlighted];
[searchButton addTarget:self action:@selector(search:) forControlEvents:UIControlEventTouchUpInside];
searchButton.frame = CGRectMake(sectionLabel.frame.size.width - 45, 0, 40, 40);
[sectionHeader addSubview:searchButton];
sectionHeader.userInteractionEnabled = YES;
if (recentsSearchBar) {
CGRect frame = recentsSearchBar.frame;
frame.origin.y = 40;
recentsSearchBar.frame = frame;
[sectionHeader addSubview:recentsSearchBar];
} }
} else { } else {
sectionHeader.text = @" No Public Rooms"; sectionLabel.text = @" No Public Rooms";
} }
return sectionHeader; return sectionHeader;
} }
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
{
// Cell is larger for public room with topic // Cell is larger for public room with topic
MXPublicRoom *publicRoom = [publicRooms objectAtIndex:indexPath.row]; MXPublicRoom *publicRoom;
if (filteredPublicRooms) {
publicRoom = [filteredPublicRooms objectAtIndex:indexPath.row];
} else {
publicRoom = [publicRooms objectAtIndex:indexPath.row];
}
if (publicRoom.topic) { if (publicRoom.topic) {
return 60; return 60;
} }
@ -322,8 +390,13 @@
} }
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MXPublicRoom *publicRoom = [publicRooms objectAtIndex:indexPath.row];
UITableViewCell *cell; UITableViewCell *cell;
MXPublicRoom *publicRoom;
if (filteredPublicRooms) {
publicRoom = [filteredPublicRooms objectAtIndex:indexPath.row];
} else {
publicRoom = [publicRooms objectAtIndex:indexPath.row];
}
// Check whether this public room has topic // Check whether this public room has topic
if (publicRoom.topic) { if (publicRoom.topic) {
@ -351,10 +424,15 @@
#pragma mark - Table view delegate #pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
MatrixHandler *mxHandler = [MatrixHandler sharedHandler]; MXPublicRoom *publicRoom;
if (filteredPublicRooms) {
publicRoom = [filteredPublicRooms objectAtIndex:indexPath.row];
} else {
publicRoom = [publicRooms objectAtIndex:indexPath.row];
}
// Check whether the user has already joined the selected public room // Check whether the user has already joined the selected public room
MXPublicRoom *publicRoom = [publicRooms objectAtIndex:indexPath.row]; MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
if ([mxHandler.mxSession roomWithRoomId:publicRoom.roomId]) { if ([mxHandler.mxSession roomWithRoomId:publicRoom.roomId]) {
// Open selected room // Open selected room
[[AppDelegate theDelegate].masterTabBarController showRoom:publicRoom.roomId]; [[AppDelegate theDelegate].masterTabBarController showRoom:publicRoom.roomId];
@ -385,4 +463,55 @@
[tableView deselectRowAtIndexPath:indexPath animated:YES]; [tableView deselectRowAtIndexPath:indexPath animated:YES];
} }
#pragma mark - UISearchBarDelegate
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
searchBarShouldEndEditing = NO;
return YES;
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar {
return searchBarShouldEndEditing;
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
// Update filtered list
if (searchText.length) {
if (filteredPublicRooms) {
[filteredPublicRooms removeAllObjects];
} else {
filteredPublicRooms = [NSMutableArray arrayWithCapacity:publicRooms.count];
}
for (MXPublicRoom *publicRoom in publicRooms) {
if ([[publicRoom displayname] rangeOfString:searchText options:NSCaseInsensitiveSearch].location != NSNotFound) {
[filteredPublicRooms addObject:publicRoom];
}
}
} else {
filteredPublicRooms = nil;
}
// Refresh display
[self.tableView reloadData];
if (filteredPublicRooms.count) {
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
// "Done" key has been pressed
searchBarShouldEndEditing = YES;
[searchBar resignFirstResponder];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
// Leave search
searchBarShouldEndEditing = YES;
[searchBar resignFirstResponder];
recentsSearchBar = nil;
filteredPublicRooms = nil;
// Restore table header and refresh table display
self.tableView.tableHeaderView = savedTableHeaderView;
[self.tableView reloadData];
}
@end @end

View file

@ -17,12 +17,14 @@
#import "RecentsViewController.h" #import "RecentsViewController.h"
#import "RoomViewController.h" #import "RoomViewController.h"
#import "RecentRoom.h"
#import "RecentsTableViewCell.h" #import "RecentsTableViewCell.h"
#import "AppDelegate.h" #import "AppDelegate.h"
#import "MatrixHandler.h" #import "MatrixHandler.h"
@interface RecentsViewController () { @interface RecentsViewController () {
// Array of RecentRooms
NSMutableArray *recents; NSMutableArray *recents;
id recentsListener; id recentsListener;
@ -59,16 +61,19 @@
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(createNewRoom:)]; UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(createNewRoom:)];
self.navigationItem.rightBarButtonItems = @[searchButton, addButton]; self.navigationItem.rightBarButtonItems = @[searchButton, addButton];
// Add activity indicator // Add background to activity indicator
[self.view addSubview:_activityIndicator]; CGRect frame = _activityIndicator.frame;
_activityIndicator.center = CGPointMake(self.view.center.x, 100); frame.size.width += 30;
[self.view bringSubviewToFront:_activityIndicator]; frame.size.height += 30;
_activityIndicator.bounds = frame;
_activityIndicator.backgroundColor = [UIColor darkGrayColor];
[_activityIndicator.layer setCornerRadius:5];
// Initialisation // Initialisation
recents = nil; recents = nil;
filteredRecents = nil; filteredRecents = nil;
NSString *dateFormat = @"MMM dd HH:mm"; NSString *dateFormat = @"MMM dd HH:mm";
dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]]]; [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]]];
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
@ -79,6 +84,7 @@
- (void)dealloc { - (void)dealloc {
if (currentRoomViewController) { if (currentRoomViewController) {
currentRoomViewController.roomId = nil; currentRoomViewController.roomId = nil;
currentRoomViewController = nil;
} }
if (recentsListener) { if (recentsListener) {
[[MatrixHandler sharedHandler].mxSession removeListener:recentsListener]; [[MatrixHandler sharedHandler].mxSession removeListener:recentsListener];
@ -102,9 +108,10 @@
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
// Refresh recents table // Refresh display
[self configureView]; [self configureView];
[[MatrixHandler sharedHandler] addObserver:self forKeyPath:@"isInitialSyncDone" options:0 context:nil]; [[MatrixHandler sharedHandler] addObserver:self forKeyPath:@"isInitialSyncDone" options:0 context:nil];
[[MatrixHandler sharedHandler] addObserver:self forKeyPath:@"isResumeDone" options:0 context:nil];
} }
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
@ -116,14 +123,22 @@
if (recentsSearchBar) { if (recentsSearchBar) {
[self searchBarCancelButtonClicked:recentsSearchBar]; [self searchBarCancelButtonClicked:recentsSearchBar];
} }
// Hide activity indicator
if (recentsListener) { [self stopActivityIndicator];
[[MatrixHandler sharedHandler].mxSession removeListener:recentsListener];
recentsListener = nil;
}
_preSelectedRoomId = nil; _preSelectedRoomId = nil;
[[MatrixHandler sharedHandler] removeObserver:self forKeyPath:@"isInitialSyncDone"]; [[MatrixHandler sharedHandler] removeObserver:self forKeyPath:@"isInitialSyncDone"];
[[MatrixHandler sharedHandler] removeObserver:self forKeyPath:@"isResumeDone"];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Release potential Room ViewController if none is visible (Note: check on room visibility is required to handle correctly splitViewController)
if ([AppDelegate theDelegate].masterTabBarController.visibleRoomId == nil && currentRoomViewController) {
currentRoomViewController.roomId = nil;
currentRoomViewController = nil;
}
} }
#pragma mark - #pragma mark -
@ -142,8 +157,8 @@
// Look for the room index in recents list // Look for the room index in recents list
NSIndexPath *indexPath = nil; NSIndexPath *indexPath = nil;
for (NSUInteger index = 0; index < recents.count; index++) { for (NSUInteger index = 0; index < recents.count; index++) {
MXEvent *mxEvent = [recents objectAtIndex:index]; RecentRoom *recentRoom = [recents objectAtIndex:index];
if ([roomId isEqualToString:mxEvent.roomId]) { if ([roomId isEqualToString:recentRoom.roomId]) {
indexPath = [NSIndexPath indexPathForRow:index inSection:0]; indexPath = [NSIndexPath indexPathForRow:index inSection:0];
break; break;
} }
@ -159,7 +174,7 @@
// Postpone room details display. We run activity indicator until recents are updated // Postpone room details display. We run activity indicator until recents are updated
_preSelectedRoomId = roomId; _preSelectedRoomId = roomId;
// Start activity indicator // Start activity indicator
[_activityIndicator startAnimating]; [self startActivityIndicator];
} }
} }
} }
@ -169,41 +184,74 @@
- (void)configureView { - (void)configureView {
MatrixHandler *mxHandler = [MatrixHandler sharedHandler]; MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
// Remove potential listener [self startActivityIndicator];
if (recentsListener && mxHandler.mxSession) {
[mxHandler.mxSession removeListener:recentsListener];
recentsListener = nil;
}
[_activityIndicator startAnimating];
if ([mxHandler isInitialSyncDone] || [mxHandler isLogged] == NO) { if ([mxHandler isInitialSyncDone] || [mxHandler isLogged] == NO) {
// Update recents // Create/Update recents
if (mxHandler.mxSession) { if (mxHandler.mxSession) {
recents = [NSMutableArray arrayWithArray:[mxHandler.mxSession recentsWithTypeIn:mxHandler.eventsFilterForMessages]]; if (!recents) {
// Register recent listener NSArray *recentEvents = [NSMutableArray arrayWithArray:[mxHandler.mxSession recentsWithTypeIn:mxHandler.eventsFilterForMessages]];
recentsListener = [mxHandler.mxSession listenToEventsOfTypes:mxHandler.eventsFilterForMessages onEvent:^(MXEvent *event, MXEventDirection direction, id customObject) { recents = [NSMutableArray arrayWithCapacity:recentEvents.count];
// consider only live event for (MXEvent *mxEvent in recentEvents) {
if (direction == MXEventDirectionForwards) { RecentRoom *recentRoom = [[RecentRoom alloc] initWithLastEvent:mxEvent andMarkAsUnread:NO];
// Refresh the whole recents list if (recentRoom) {
recents = [NSMutableArray arrayWithArray:[mxHandler.mxSession recentsWithTypeIn:mxHandler.eventsFilterForMessages]]; [recents addObject:recentRoom];
// Reload table
[self.tableView reloadData];
[_activityIndicator stopAnimating];
// Check whether a room is preselected
if (_preSelectedRoomId) {
self.preSelectedRoomId = _preSelectedRoomId;
} }
} }
}];
// Register recent listener
recentsListener = [mxHandler.mxSession listenToEventsOfTypes:mxHandler.eventsFilterForMessages onEvent:^(MXEvent *event, MXEventDirection direction, id customObject) {
// Consider only live event
if (direction == MXEventDirectionForwards) {
// Check user's membership in live room state (We will remove left rooms from recents)
MXRoom *mxRoom = [mxHandler.mxSession roomWithRoomId:event.roomId];
BOOL isLeft = (mxRoom == nil || mxRoom.state.membership == MXMembershipLeave || mxRoom.state.membership == MXMembershipBan);
// Consider this new event as unread only if the sender is not the user and if the room is not visible
BOOL isUnread = (![event.userId isEqualToString:mxHandler.userId]
&& ![[AppDelegate theDelegate].masterTabBarController.visibleRoomId isEqualToString:event.roomId]);
// Look for the room
BOOL isFound = NO;
for (NSUInteger index = 0; index < recents.count; index++) {
RecentRoom *recentRoom = [recents objectAtIndex:index];
if ([event.roomId isEqualToString:recentRoom.roomId]) {
isFound = YES;
if (isLeft) {
// Remove left room
[recents removeObjectAtIndex:index];
} else {
[recentRoom updateWithLastEvent:event andMarkAsUnread:isUnread];
// Move this room at first position
[recents removeObjectAtIndex:index];
[recents insertObject:recentRoom atIndex:0];
}
break;
}
}
if (!isFound && !isLeft) {
// Insert in first position this new room
RecentRoom *recentRoom = [[RecentRoom alloc] initWithLastEvent:event andMarkAsUnread:isUnread];
if (recentRoom) {
[recents insertObject:recentRoom atIndex:0];
}
}
// Reload table
[self.tableView reloadData];
}
}];
}
// else nothing to do
} else { } else {
recents = nil; recents = nil;
} }
// Reload table // Reload table
[self.tableView reloadData]; [self.tableView reloadData];
[_activityIndicator stopAnimating]; if ([mxHandler isResumeDone]) {
[self stopActivityIndicator];
}
// Check whether a room is preselected // Check whether a room is preselected
if (_preSelectedRoomId) { if (_preSelectedRoomId) {
@ -213,6 +261,14 @@
recents = nil; recents = nil;
[self.tableView reloadData]; [self.tableView reloadData];
} }
if (!recents) {
// Remove potential listener
if (recentsListener && mxHandler.mxSession) {
[mxHandler.mxSession removeListener:recentsListener];
recentsListener = nil;
}
}
} }
- (void)createNewRoom:(id)sender { - (void)createNewRoom:(id)sender {
@ -238,6 +294,18 @@
} }
} }
- (void)startActivityIndicator {
// Add the spinner on main screen in order to ignore potential table scrolling
_activityIndicator.center = CGPointMake(self.view.center.x, self.view.center.x);
[[AppDelegate theDelegate].window addSubview:_activityIndicator];
[_activityIndicator startAnimating];
}
- (void)stopActivityIndicator {
[_activityIndicator stopAnimating];
[_activityIndicator removeFromSuperview];
}
#pragma mark - KVO #pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
@ -245,6 +313,12 @@
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[self configureView]; [self configureView];
}); });
} else if ([@"isResumeDone" isEqualToString:keyPath]) {
if ([[MatrixHandler sharedHandler] isResumeDone]) {
[self stopActivityIndicator];
} else {
[self startActivityIndicator];
}
} }
} }
@ -253,11 +327,11 @@
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"showDetail"]) { if ([[segue identifier] isEqualToString:@"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
MXEvent *mxEvent; RecentRoom *recentRoom;
if (filteredRecents) { if (filteredRecents) {
mxEvent = filteredRecents[indexPath.row]; recentRoom = filteredRecents[indexPath.row];
} else { } else {
mxEvent = recents[indexPath.row]; recentRoom = recents[indexPath.row];
} }
UIViewController *controller; UIViewController *controller;
@ -268,18 +342,24 @@
} }
if ([controller isKindOfClass:[RoomViewController class]]) { if ([controller isKindOfClass:[RoomViewController class]]) {
// Release potential Room ViewController
if (currentRoomViewController) { if (currentRoomViewController) {
if ((currentRoomViewController != controller) || (![currentRoomViewController.roomId isEqualToString:mxEvent.roomId])) { currentRoomViewController.roomId = nil;
// Release the current one currentRoomViewController = nil;
currentRoomViewController.roomId = nil;
}
} }
currentRoomViewController = (RoomViewController *)controller; currentRoomViewController = (RoomViewController *)controller;
currentRoomViewController.roomId = mxEvent.roomId; currentRoomViewController.roomId = recentRoom.roomId;
} }
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem; // Reset unread count for this room
controller.navigationItem.leftItemsSupplementBackButton = YES; [recentRoom resetUnreadCount];
if (self.splitViewController) {
// Refresh display (required in case of splitViewController)
[self.tableView reloadData];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
}
} }
} }
@ -314,18 +394,18 @@
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
RecentsTableViewCell *cell = (RecentsTableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"RecentsCell" forIndexPath:indexPath]; RecentsTableViewCell *cell = (RecentsTableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"RecentsCell" forIndexPath:indexPath];
MXEvent *mxEvent; RecentRoom *recentRoom;
if (filteredRecents) { if (filteredRecents) {
mxEvent = filteredRecents[indexPath.row]; recentRoom = filteredRecents[indexPath.row];
} else { } else {
mxEvent = recents[indexPath.row]; recentRoom = recents[indexPath.row];
} }
MatrixHandler *mxHandler = [MatrixHandler sharedHandler]; MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
MXRoom *mxRoom = [mxHandler.mxSession roomWithRoomId:mxEvent.roomId]; MXRoom *mxRoom = [mxHandler.mxSession roomWithRoomId:recentRoom.roomId];
cell.roomTitle.text = [mxRoom.state displayname]; cell.roomTitle.text = [mxRoom.state displayname];
cell.lastEventDescription.text = [mxHandler displayTextForEvent:mxEvent withRoomState:mxRoom.state inSubtitleMode:YES]; cell.lastEventDescription.text = [mxHandler displayTextForEvent:recentRoom.lastEvent withRoomState:mxRoom.state inSubtitleMode:YES];
// Set in bold public room name // Set in bold public room name
if (mxRoom.state.isPublic) { if (mxRoom.state.isPublic) {
@ -334,12 +414,20 @@
cell.roomTitle.font = [UIFont systemFontOfSize:19]; cell.roomTitle.font = [UIFont systemFontOfSize:19];
} }
if (mxEvent.originServerTs != kMXUndefinedTimestamp) { if (recentRoom.lastEvent.originServerTs != kMXUndefinedTimestamp) {
NSDate *date = [NSDate dateWithTimeIntervalSince1970:mxEvent.originServerTs/1000]; NSDate *date = [NSDate dateWithTimeIntervalSince1970:recentRoom.lastEvent.originServerTs/1000];
cell.recentDate.text = [dateFormatter stringFromDate:date]; cell.recentDate.text = [dateFormatter stringFromDate:date];
} else { } else {
cell.recentDate.text = nil; cell.recentDate.text = nil;
} }
// set background color
if (recentRoom.unreadCount) {
cell.backgroundColor = [UIColor colorWithRed:1 green:0.9 blue:0.9 alpha:1.0];
cell.roomTitle.text = [NSString stringWithFormat:@"%@ (%lu)", cell.roomTitle.text, (unsigned long)recentRoom.unreadCount];
} else {
cell.backgroundColor = [UIColor clearColor];
}
return cell; return cell;
} }
@ -351,13 +439,13 @@
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) { if (editingStyle == UITableViewCellEditingStyleDelete) {
// Leave the selected room // Leave the selected room
MXEvent *mxEvent; RecentRoom *recentRoom;
if (filteredRecents) { if (filteredRecents) {
mxEvent = filteredRecents[indexPath.row]; recentRoom = filteredRecents[indexPath.row];
} else { } else {
mxEvent = recents[indexPath.row]; recentRoom = recents[indexPath.row];
} }
MXRoom *mxRoom = [[MatrixHandler sharedHandler].mxSession roomWithRoomId:mxEvent.roomId]; MXRoom *mxRoom = [[MatrixHandler sharedHandler].mxSession roomWithRoomId:recentRoom.roomId];
[mxRoom leave:^{ [mxRoom leave:^{
// Refresh table display // Refresh table display
if (filteredRecents) { if (filteredRecents) {
@ -367,7 +455,7 @@
} }
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} failure:^(NSError *error) { } failure:^(NSError *error) {
NSLog(@"Failed to leave room (%@) failed: %@", mxEvent.roomId, error); NSLog(@"Failed to leave room (%@) failed: %@", recentRoom.roomId, error);
//Alert user //Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error]; [[AppDelegate theDelegate] showErrorAsAlert:error];
}]; }];
@ -376,6 +464,11 @@
#pragma mark - UISearchBarDelegate #pragma mark - UISearchBarDelegate
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
searchBarShouldEndEditing = NO;
return YES;
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar { - (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar {
return searchBarShouldEndEditing; return searchBarShouldEndEditing;
} }
@ -389,10 +482,10 @@
filteredRecents = [NSMutableArray arrayWithCapacity:recents.count]; filteredRecents = [NSMutableArray arrayWithCapacity:recents.count];
} }
MatrixHandler *mxHandler = [MatrixHandler sharedHandler]; MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
for (MXEvent *mxEvent in recents) { for (RecentRoom *recentRoom in recents) {
MXRoom *mxRoom = [mxHandler.mxSession roomWithRoomId:mxEvent.roomId]; MXRoom *mxRoom = [mxHandler.mxSession roomWithRoomId:recentRoom.roomId];
if ([[mxRoom.state displayname] rangeOfString:searchText options:NSCaseInsensitiveSearch].location != NSNotFound) { if ([[mxRoom.state displayname] rangeOfString:searchText options:NSCaseInsensitiveSearch].location != NSNotFound) {
[filteredRecents addObject:mxEvent]; [filteredRecents addObject:recentRoom];
} }
} }
} else { } else {

View file

@ -28,7 +28,8 @@
#import "MediaManager.h" #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_HEIGHT 50
#define ROOM_MESSAGE_CELL_DEFAULT_TEXTVIEW_TOP_CONST 10 #define ROOM_MESSAGE_CELL_DEFAULT_TEXTVIEW_TOP_CONST 10
@ -57,7 +58,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
// Back pagination // Back pagination
BOOL isBackPaginationInProgress; BOOL isBackPaginationInProgress;
NSUInteger backPaginationAddedItemsNb; NSUInteger backPaginationAddedMsgNb;
// Members list // Members list
NSArray *members; NSArray *members;
@ -96,6 +97,8 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
[super viewDidLoad]; [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib. // Do any additional setup after loading the view, typically from a nib.
forceScrollToBottomOnViewDidAppear = YES; 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]; UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoLight];
[button addTarget:self action:@selector(showHideRoomMembers:) forControlEvents:UIControlEventTouchUpInside]; [button addTarget:self action:@selector(showHideRoomMembers:) forControlEvents:UIControlEventTouchUpInside];
@ -130,6 +133,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
[mxRoom removeListener:messagesListener]; [mxRoom removeListener:messagesListener];
messagesListener = nil; messagesListener = nil;
[[AppSettings sharedSettings] removeObserver:self forKeyPath:@"hideUnsupportedMessages"]; [[AppSettings sharedSettings] removeObserver:self forKeyPath:@"hideUnsupportedMessages"];
[[AppSettings sharedSettings] removeObserver:self forKeyPath:@"displayAllEvents"];
} }
mxRoom = nil; mxRoom = nil;
@ -165,9 +169,6 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onKeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onKeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onTextFieldChange:) name:UITextFieldTextDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onTextFieldChange:) name:UITextFieldTextDidChangeNotification object:nil];
// Set visible room id
[AppDelegate theDelegate].masterTabBarController.visibleRoomId = self.roomId;
} }
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
@ -185,21 +186,29 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil];
// Reset visible room id
[AppDelegate theDelegate].masterTabBarController.visibleRoomId = nil;
} }
- (void)viewDidAppear:(BOOL)animated { - (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated]; [super viewDidAppear:animated];
// Set visible room id
[AppDelegate theDelegate].masterTabBarController.visibleRoomId = self.roomId;
if (forceScrollToBottomOnViewDidAppear) { if (forceScrollToBottomOnViewDidAppear) {
// Scroll to the bottom // Scroll to the bottom
[self scrollToBottomAnimated:animated]; [self scrollToBottomAnimated:animated];
forceScrollToBottomOnViewDidAppear = NO; forceScrollToBottomOnViewDidAppear = NO;
self.messagesTableView.hidden = NO;
} }
} }
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
// Reset visible room id
[AppDelegate theDelegate].masterTabBarController.visibleRoomId = nil;
}
#pragma mark - room ID #pragma mark - room ID
- (void)setRoomId:(NSString *)roomId { - (void)setRoomId:(NSString *)roomId {
@ -240,6 +249,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
[mxRoom removeListener:messagesListener]; [mxRoom removeListener:messagesListener];
messagesListener = nil; messagesListener = nil;
[[AppSettings sharedSettings] removeObserver:self forKeyPath:@"hideUnsupportedMessages"]; [[AppSettings sharedSettings] removeObserver:self forKeyPath:@"hideUnsupportedMessages"];
[[AppSettings sharedSettings] removeObserver:self forKeyPath:@"displayAllEvents"];
} }
// The whole room history is flushed here to rebuild it from the current instant (live) // The whole room history is flushed here to rebuild it from the current instant (live)
messages = nil; messages = nil;
@ -247,10 +257,12 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
self.roomNameTextField.enabled = NO; self.roomNameTextField.enabled = NO;
// Update room data // Update room data
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
mxRoom = nil;
if (self.roomId) { if (self.roomId) {
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
mxRoom = [mxHandler.mxSession roomWithRoomId:self.roomId]; mxRoom = [mxHandler.mxSession roomWithRoomId:self.roomId];
}
if (mxRoom) {
// Update room title // Update room title
self.roomNameTextField.text = mxRoom.state.displayname; self.roomNameTextField.text = mxRoom.state.displayname;
@ -279,37 +291,38 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
messages = [NSMutableArray array]; messages = [NSMutableArray array];
[[AppSettings sharedSettings] addObserver:self forKeyPath:@"hideUnsupportedMessages" options:0 context:nil]; [[AppSettings sharedSettings] addObserver:self forKeyPath:@"hideUnsupportedMessages" options:0 context:nil];
[[AppSettings sharedSettings] addObserver:self forKeyPath:@"displayAllEvents" options:0 context:nil];
// Register a listener to handle messages // Register a listener to handle messages
messagesListener = [mxRoom listenToEventsOfTypes:mxHandler.eventsFilterForMessages onEvent:^(MXEvent *event, MXEventDirection direction, MXRoomState *roomState) { messagesListener = [mxRoom listenToEventsOfTypes:mxHandler.eventsFilterForMessages onEvent:^(MXEvent *event, MXEventDirection direction, MXRoomState *roomState) {
BOOL shouldScrollToBottom = NO; BOOL shouldScrollToBottom = NO;
// Handle first live events // Handle first live events
if (direction == MXEventDirectionForwards) { if (direction == MXEventDirectionForwards) {
// Check user's membership in live room state (Indeed we have to go back on recents when user leaves, or is kicked/banned)
if (mxRoom.state.membership == MXMembershipLeave || mxRoom.state.membership == MXMembershipBan) {
[self.navigationController popViewControllerAnimated:NO];
return;
}
// We will scroll to bottom after updating tableView only if the most recent message is entirely visible. // 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); CGFloat maxPositionY = self.messagesTableView.contentOffset.y + (self.messagesTableView.frame.size.height - self.messagesTableView.contentInset.bottom);
shouldScrollToBottom = (maxPositionY >= self.messagesTableView.contentSize.height); shouldScrollToBottom = (maxPositionY >= self.messagesTableView.contentSize.height);
// Update Table // Update Table
NSIndexPath *indexPathForInsertedRow = nil; BOOL isHandled = NO;
NSIndexPath *indexPathForDeletedRow = nil;
NSMutableArray *indexPathsForUpdatedRows = [NSMutableArray array];
BOOL isComplete = NO;
// For outgoing message, remove the temporary event // For outgoing message, remove the temporary event
if ([event.userId isEqualToString:[MatrixHandler sharedHandler].userId] && messages.count) { if ([event.userId isEqualToString:[MatrixHandler sharedHandler].userId] && messages.count) {
// Consider first the last message // Consider first the last message
RoomMessage *message = [messages lastObject]; RoomMessage *message = [messages lastObject];
NSUInteger index = messages.count - 1; NSUInteger index = messages.count - 1;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
if ([message containsEventId:event.eventId]) { if ([message containsEventId:event.eventId]) {
if (message.messageType == RoomMessageTypeText) { if (message.messageType == RoomMessageTypeText) {
// Removing temporary event (local echo) // Removing temporary event (local echo)
[message removeEvent:event.eventId]; [message removeEvent:event.eventId];
// Update message with the received event // Update message with the received event
isComplete = [message addEvent:event withRoomState:roomState]; isHandled = [message addEvent:event withRoomState:roomState];
if (message.attributedTextMessage.length) { if (! message.attributedTextMessage.length) {
[indexPathsForUpdatedRows addObject:indexPath];
} else {
[messages removeObjectAtIndex:index]; [messages removeObjectAtIndex:index];
indexPathForDeletedRow = indexPath;
} }
} else { } else {
// Create a new message to handle attachment // Create a new message to handle attachment
@ -317,31 +330,24 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
if (!message) { if (!message) {
// Ignore unsupported/unexpected events // Ignore unsupported/unexpected events
[messages removeObjectAtIndex:index]; [messages removeObjectAtIndex:index];
indexPathForDeletedRow = indexPath;
} else { } else {
[messages replaceObjectAtIndex:index withObject:message]; [messages replaceObjectAtIndex:index withObject:message];
[indexPathsForUpdatedRows addObject:indexPath];
} }
isComplete = YES; isHandled = YES;
} }
} else { } else {
while (index--) { while (index--) {
message = [messages objectAtIndex:index]; message = [messages objectAtIndex:index];
indexPath = [NSIndexPath indexPathForRow:index inSection:0];
if ([message containsEventId:event.eventId]) { if ([message containsEventId:event.eventId]) {
if (message.messageType == RoomMessageTypeText) { if (message.messageType == RoomMessageTypeText) {
// Removing temporary event (local echo) // Removing temporary event (local echo)
[message removeEvent:event.eventId]; [message removeEvent:event.eventId];
if (message.attributedTextMessage.length) { if (!message.attributedTextMessage.length) {
[indexPathsForUpdatedRows addObject:indexPath];
} else {
[messages removeObjectAtIndex:index]; [messages removeObjectAtIndex:index];
indexPathForDeletedRow = indexPath;
} }
} else { } else {
// Remove the local event (a new one will be added to messages) // Remove the local event (a new one will be added to messages)
[messages removeObjectAtIndex:index]; [messages removeObjectAtIndex:index];
indexPathForDeletedRow = indexPath;
} }
break; break;
} }
@ -349,51 +355,29 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
} }
} }
if (isComplete == NO) { if (isHandled == NO) {
// Check whether the event may be grouped with last message // Check whether the event may be grouped with last message
RoomMessage *lastMessage = [messages lastObject]; RoomMessage *lastMessage = [messages lastObject];
if (lastMessage && [lastMessage addEvent:event withRoomState:roomState]) { if (lastMessage && [lastMessage addEvent:event withRoomState:roomState]) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:(messages.count - 1) inSection:0]; isHandled = YES;
[indexPathsForUpdatedRows addObject:indexPath];
} else { } else {
// Create a new item
lastMessage = [[RoomMessage alloc] initWithEvent:event andRoomState:roomState]; lastMessage = [[RoomMessage alloc] initWithEvent:event andRoomState:roomState];
if (lastMessage) { if (lastMessage) {
indexPathForInsertedRow = [NSIndexPath indexPathForRow:messages.count inSection:0];
[messages addObject:lastMessage]; [messages addObject:lastMessage];
isHandled = YES;
} // else ignore unsupported/unexpected events } // else ignore unsupported/unexpected events
} }
} }
// Refresh table display // Refresh table display except if a back pagination is in progress
BOOL isModified = NO; if (!isBackPaginationInProgress) {
[UIView setAnimationsEnabled:NO]; [self.messagesTableView reloadData];
[self.messagesTableView beginUpdates]; if (isHandled) {
if (indexPathForDeletedRow) { if ([[AppDelegate theDelegate].masterTabBarController.visibleRoomId isEqualToString:self.roomId] == NO) {
if (indexPathForInsertedRow) { // Some new events are received for this room while it is not visible, scroll to bottom on viewDidAppear to focus on them
[indexPathsForUpdatedRows removeAllObjects]; forceScrollToBottomOnViewDidAppear = YES;
NSUInteger index = indexPathForDeletedRow.row;
for (; index < messages.count; index++) {
[indexPathsForUpdatedRows addObject:[NSIndexPath indexPathForRow:index inSection:0]];
} }
} 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) { } else if (isBackPaginationInProgress && direction == MXEventDirectionBackwards) {
@ -403,7 +387,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
firstMessage = [[RoomMessage alloc] initWithEvent:event andRoomState:roomState]; firstMessage = [[RoomMessage alloc] initWithEvent:event andRoomState:roomState];
if (firstMessage) { if (firstMessage) {
[messages insertObject:firstMessage atIndex:0]; [messages insertObject:firstMessage atIndex:0];
backPaginationAddedItemsNb++; backPaginationAddedMsgNb++;
} }
// Ignore unsupported/unexpected events // Ignore unsupported/unexpected events
} }
@ -419,7 +403,6 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
[mxRoom resetBackState]; [mxRoom resetBackState];
[self triggerBackPagination]; [self triggerBackPagination];
} else { } else {
mxRoom = nil;
// Update room title // Update room title
self.roomNameTextField.text = nil; self.roomNameTextField.text = nil;
} }
@ -445,72 +428,81 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
if (mxRoom.canPaginate) { if (mxRoom.canPaginate) {
[_activityIndicator startAnimating]; [_activityIndicator startAnimating];
isBackPaginationInProgress = YES; isBackPaginationInProgress = YES;
backPaginationAddedItemsNb = 0; backPaginationAddedMsgNb = 0;
[mxRoom paginateBackMessages:20 complete:^{ [self paginateBackMessages:ROOMVIEWCONTROLLER_BACK_PAGINATION_SIZE];
// Sanity check: check whether the view controller has not been released while back pagination was running
if (self.roomId == nil) {
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;
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];
}
// 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];
[_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];
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];
}];
} }
} }
- (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 #pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([@"hideUnsupportedMessages" isEqualToString:keyPath]) { if ([@"displayAllEvents" isEqualToString:keyPath]) {
// Back to recents (Room details are not available until the end of initial sync)
[self.navigationController popViewControllerAnimated:NO];
} else if ([@"hideUnsupportedMessages" isEqualToString:keyPath]) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[self configureView]; [self configureView];
}); });
@ -800,7 +792,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
return members.count; return members.count;
} }
if (backPaginationAddedItemsNb) { if (backPaginationAddedMsgNb) {
// Here some old messages have been added to messages during back pagination. // 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 // Stop table refreshing, the table will be refreshed at the end of pagination
return 0; return 0;
@ -1093,7 +1085,6 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
// Check table view members vs messages // Check table view members vs messages
if (tableView == self.membersTableView) { if (tableView == self.membersTableView) {
// List action(s) available on this member // List action(s) available on this member
// TODO: Check user's power level before allowing an action (kick, ban, ...)
MXRoomMember *roomMember = [members objectAtIndex:indexPath.row]; MXRoomMember *roomMember = [members objectAtIndex:indexPath.row];
MatrixHandler *mxHandler = [MatrixHandler sharedHandler]; MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
@ -1120,92 +1111,117 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
} }
}]; }];
} else { } else {
// Check user's power level before allowing an action (kick, ban, ...)
MXRoomPowerLevels *powerLevels = [mxRoom.state powerLevels];
NSUInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:mxHandler.userId];
NSUInteger memberPowerLevel = [powerLevels powerLevelOfUserWithUserID:roomMember.userId];
// Consider membership of the selected member // Consider membership of the selected member
switch (roomMember.membership) { switch (roomMember.membership) {
case MXMembershipInvite: case MXMembershipInvite:
case MXMembershipJoin: { case MXMembershipJoin: {
self.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an action:" message:nil style:CustomAlertStyleActionSheet]; // Check conditions to be able to kick someone
[self.actionMenu addActionWithTitle:@"Kick" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) { if (userPowerLevel >= [powerLevels kick] && userPowerLevel >= memberPowerLevel) {
if (weakSelf) { self.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an action:" message:nil style:CustomAlertStyleActionSheet];
weakSelf.actionMenu = nil; [self.actionMenu addActionWithTitle:@"Kick" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
[[MatrixHandler sharedHandler].mxRestClient kickUser:roomMember.userId if (weakSelf) {
fromRoom:weakSelf.roomId weakSelf.actionMenu = nil;
reason:nil [[MatrixHandler sharedHandler].mxRestClient kickUser:roomMember.userId
success:^{ fromRoom:weakSelf.roomId
} reason:nil
failure:^(NSError *error) { success:^{
NSLog(@"Kick %@ failed: %@", roomMember.userId, error); }
//Alert user failure:^(NSError *error) {
[[AppDelegate theDelegate] showErrorAsAlert:error]; NSLog(@"Kick %@ failed: %@", roomMember.userId, error);
}]; //Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}];
}
// Check conditions to be able to ban someone
if (userPowerLevel >= [powerLevels ban] && userPowerLevel >= memberPowerLevel) {
if (!self.actionMenu) {
self.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an action:" message:nil style:CustomAlertStyleActionSheet];
} }
}]; [self.actionMenu addActionWithTitle:@"Ban" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
[self.actionMenu addActionWithTitle:@"Ban" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) { if (weakSelf) {
if (weakSelf) { weakSelf.actionMenu = nil;
weakSelf.actionMenu = nil; [[MatrixHandler sharedHandler].mxRestClient banUser:roomMember.userId
[[MatrixHandler sharedHandler].mxRestClient banUser:roomMember.userId inRoom:weakSelf.roomId
inRoom:weakSelf.roomId reason:nil
reason:nil success:^{
success:^{ }
} failure:^(NSError *error) {
failure:^(NSError *error) { NSLog(@"Ban %@ failed: %@", roomMember.userId, error);
NSLog(@"Ban %@ failed: %@", roomMember.userId, error); //Alert user
//Alert user [[AppDelegate theDelegate] showErrorAsAlert:error];
[[AppDelegate theDelegate] showErrorAsAlert:error]; }];
}]; }
} }];
}]; }
break; break;
} }
case MXMembershipLeave: { case MXMembershipLeave: {
self.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an action:" message:nil style:CustomAlertStyleActionSheet]; // Check conditions to be able to invite someone
[self.actionMenu addActionWithTitle:@"Invite" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) { if (userPowerLevel >= [powerLevels invite]) {
if (weakSelf) { self.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an action:" message:nil style:CustomAlertStyleActionSheet];
weakSelf.actionMenu = nil; [self.actionMenu addActionWithTitle:@"Invite" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
[[MatrixHandler sharedHandler].mxRestClient inviteUser:roomMember.userId if (weakSelf) {
toRoom:weakSelf.roomId weakSelf.actionMenu = nil;
success:^{ [[MatrixHandler sharedHandler].mxRestClient inviteUser:roomMember.userId
} toRoom:weakSelf.roomId
failure:^(NSError *error) { success:^{
NSLog(@"Invite %@ failed: %@", roomMember.userId, error); }
//Alert user failure:^(NSError *error) {
[[AppDelegate theDelegate] showErrorAsAlert:error]; NSLog(@"Invite %@ failed: %@", roomMember.userId, error);
}]; //Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}];
}
// Check conditions to be able to ban someone
if (userPowerLevel >= [powerLevels ban] && userPowerLevel >= memberPowerLevel) {
if (!self.actionMenu) {
self.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an action:" message:nil style:CustomAlertStyleActionSheet];
} }
}]; [self.actionMenu addActionWithTitle:@"Ban" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
[self.actionMenu addActionWithTitle:@"Ban" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) { if (weakSelf) {
if (weakSelf) { weakSelf.actionMenu = nil;
weakSelf.actionMenu = nil; [[MatrixHandler sharedHandler].mxRestClient banUser:roomMember.userId
[[MatrixHandler sharedHandler].mxRestClient banUser:roomMember.userId inRoom:weakSelf.roomId
inRoom:weakSelf.roomId reason:nil
reason:nil success:^{
success:^{ }
} failure:^(NSError *error) {
failure:^(NSError *error) { NSLog(@"Ban %@ failed: %@", roomMember.userId, error);
NSLog(@"Ban %@ failed: %@", roomMember.userId, error); //Alert user
//Alert user [[AppDelegate theDelegate] showErrorAsAlert:error];
[[AppDelegate theDelegate] showErrorAsAlert:error]; }];
}]; }
} }];
}]; }
break; break;
} }
case MXMembershipBan: { case MXMembershipBan: {
self.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an action:" message:nil style:CustomAlertStyleActionSheet]; // Check conditions to be able to unban someone
[self.actionMenu addActionWithTitle:@"Unban" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) { if (userPowerLevel >= [powerLevels ban] && userPowerLevel >= memberPowerLevel) {
if (weakSelf) { self.actionMenu = [[CustomAlert alloc] initWithTitle:@"Select an action:" message:nil style:CustomAlertStyleActionSheet];
weakSelf.actionMenu = nil; [self.actionMenu addActionWithTitle:@"Unban" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
[[MatrixHandler sharedHandler].mxRestClient unbanUser:roomMember.userId if (weakSelf) {
inRoom:weakSelf.roomId weakSelf.actionMenu = nil;
success:^{ [[MatrixHandler sharedHandler].mxRestClient unbanUser:roomMember.userId
} inRoom:weakSelf.roomId
failure:^(NSError *error) { success:^{
NSLog(@"Unban %@ failed: %@", roomMember.userId, error); }
//Alert user failure:^(NSError *error) {
[[AppDelegate theDelegate] showErrorAsAlert:error]; NSLog(@"Unban %@ failed: %@", roomMember.userId, error);
}]; //Alert user
} [[AppDelegate theDelegate] showErrorAsAlert:error];
}]; }];
}
}];
}
break; break;
} }
default: { default: {
@ -1214,14 +1230,18 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
} }
} }
// Display the action sheet (if any) // Notify user when his power is too weak
if (!self.actionMenu) {
self.actionMenu = [[CustomAlert alloc] initWithTitle:nil message:@"You are not authorized to change the status of this member" style:CustomAlertStyleAlert];
}
// Display the action sheet (or the alert)
if (self.actionMenu) { if (self.actionMenu) {
self.actionMenu.cancelButtonIndex = [self.actionMenu addActionWithTitle:@"Cancel" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) { self.actionMenu.cancelButtonIndex = [self.actionMenu addActionWithTitle:@"Cancel" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
weakSelf.actionMenu = nil; weakSelf.actionMenu = nil;
}]; }];
[self.actionMenu showInViewController:self]; [self.actionMenu showInViewController:self];
} }
[tableView deselectRowAtIndexPath:indexPath animated:YES]; [tableView deselectRowAtIndexPath:indexPath animated:YES];
} else if (tableView == self.messagesTableView) { } else if (tableView == self.messagesTableView) {
// Dismiss keyboard when user taps on messages table view content // Dismiss keyboard when user taps on messages table view content
@ -1235,7 +1255,9 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
// paginate ? // paginate ?
if (scrollView.contentOffset.y < -64) if (scrollView.contentOffset.y < -64)
{ {
[self triggerBackPagination]; dispatch_async(dispatch_get_main_queue(), ^{
[self triggerBackPagination];
});
} }
} }
} }
@ -1258,8 +1280,26 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
if (textField == self.roomNameTextField) { if (textField == self.roomNameTextField) {
self.roomNameTextField.borderStyle = UITextBorderStyleRoundedRect; // Check whether the user has enough power to rename the room
self.roomNameTextField.backgroundColor = [UIColor whiteColor]; MXRoomPowerLevels *powerLevels = [mxRoom.state powerLevels];
NSUInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:[MatrixHandler sharedHandler].userId];
if (userPowerLevel >= [powerLevels minimumPowerLevelForPostingEventAsStateEvent:kMXEventTypeStringRoomName]) {
self.roomNameTextField.borderStyle = UITextBorderStyleRoundedRect;
self.roomNameTextField.backgroundColor = [UIColor whiteColor];
return YES;
} else {
// Alert user
__weak typeof(self) weakSelf = self;
if (self.actionMenu) {
[self.actionMenu dismiss:NO];
}
self.actionMenu = [[CustomAlert alloc] initWithTitle:nil message:@"You are not authorized to edit this room name" style:CustomAlertStyleAlert];
self.actionMenu.cancelButtonIndex = [self.actionMenu addActionWithTitle:@"Cancel" style:CustomAlertActionStyleDefault handler:^(CustomAlert *alert) {
weakSelf.actionMenu = nil;
}];
[self.actionMenu showInViewController:self];
}
return NO;
} }
return YES; return YES;
} }
@ -1268,14 +1308,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
if (textField == self.roomNameTextField) { if (textField == self.roomNameTextField) {
self.roomNameTextField.borderStyle = UITextBorderStyleNone; self.roomNameTextField.borderStyle = UITextBorderStyleNone;
self.roomNameTextField.backgroundColor = [UIColor clearColor]; self.roomNameTextField.backgroundColor = [UIColor clearColor];
}
}
- (BOOL)textFieldShouldReturn:(UITextField*) textField {
// "Done" key has been pressed
[textField resignFirstResponder];
if (textField == self.roomNameTextField) {
NSString *roomName = self.roomNameTextField.text; NSString *roomName = self.roomNameTextField.text;
if ([roomName isEqualToString:mxRoom.state.name] == NO) { if ([roomName isEqualToString:mxRoom.state.name] == NO) {
[self.activityIndicator startAnimating]; [self.activityIndicator startAnimating];
@ -1296,6 +1329,11 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
}]; }];
} }
} }
}
- (BOOL)textFieldShouldReturn:(UITextField*) textField {
// "Done" key has been pressed
[textField resignFirstResponder];
return YES; return YES;
} }
@ -1409,17 +1447,12 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
while (index--) { while (index--) {
message = [messages objectAtIndex:index]; message = [messages objectAtIndex:index];
if ([message containsEventId:localEvent.eventId]) { if ([message containsEventId:localEvent.eventId]) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
localEvent.content = msgContent; localEvent.content = msgContent;
if (message.messageType == RoomMessageTypeText) { if (message.messageType == RoomMessageTypeText) {
[message removeEvent:localEvent.eventId]; [message removeEvent:localEvent.eventId];
[message addEvent:localEvent withRoomState:mxRoom.state]; [message addEvent:localEvent withRoomState:mxRoom.state];
if (message.attributedTextMessage.length) { if (!message.attributedTextMessage.length) {
// Refresh table display
[self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} else {
[messages removeObjectAtIndex:index]; [messages removeObjectAtIndex:index];
[self.messagesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} }
} else { } else {
// Create a new message // Create a new message
@ -1427,15 +1460,14 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
if (message) { if (message) {
// Refresh table display // Refresh table display
[messages replaceObjectAtIndex:index withObject:message]; [messages replaceObjectAtIndex:index withObject:message];
[self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} else { } else {
[messages removeObjectAtIndex:index]; [messages removeObjectAtIndex:index];
[self.messagesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} }
} }
break; break;
} }
} }
[self.messagesTableView reloadData];
} else { } else {
// Create a temporary event to displayed outgoing message (local echo) // Create a temporary event to displayed outgoing message (local echo)
NSString* localEventId = [NSString stringWithFormat:@"%@%@", kLocalEchoEventIdPrefix, [[NSProcessInfo processInfo] globallyUniqueString]]; NSString* localEventId = [NSString stringWithFormat:@"%@%@", kLocalEchoEventIdPrefix, [[NSProcessInfo processInfo] globallyUniqueString]];
@ -1449,19 +1481,16 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
localEvent.originServerTs = kMXUndefinedTimestamp; localEvent.originServerTs = kMXUndefinedTimestamp;
// Check whether this new event may be grouped with last message // Check whether this new event may be grouped with last message
RoomMessage *lastMessage = [messages lastObject]; RoomMessage *lastMessage = [messages lastObject];
if (lastMessage && [lastMessage addEvent:localEvent withRoomState:mxRoom.state]) { if (lastMessage == nil || [lastMessage addEvent:localEvent withRoomState:mxRoom.state] == NO) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:(messages.count - 1) inSection:0]; // Create a new item
[self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} else {
lastMessage = [[RoomMessage alloc] initWithEvent:localEvent andRoomState:mxRoom.state]; lastMessage = [[RoomMessage alloc] initWithEvent:localEvent andRoomState:mxRoom.state];
if (lastMessage) { if (lastMessage) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:messages.count inSection:0];
[messages addObject:lastMessage]; [messages addObject:lastMessage];
[self.messagesTableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} else { } else {
NSLog(@"ERROR: Unable to add local event: %@", localEvent.description); NSLog(@"ERROR: Unable to add local event: %@", localEvent.description);
} }
} }
[self.messagesTableView reloadData];
[self scrollToBottomAnimated:NO]; [self scrollToBottomAnimated:NO];
} }
@ -1482,7 +1511,6 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
while (index--) { while (index--) {
RoomMessage *message = [messages objectAtIndex:index]; RoomMessage *message = [messages objectAtIndex:index];
if ([message containsEventId:localEvent.eventId]) { if ([message containsEventId:localEvent.eventId]) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
if (message.messageType == RoomMessageTypeText) { if (message.messageType == RoomMessageTypeText) {
[message removeEvent:localEvent.eventId]; [message removeEvent:localEvent.eventId];
if (isEventAlreadyAddedToRoom == NO) { if (isEventAlreadyAddedToRoom == NO) {
@ -1490,12 +1518,8 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
localEvent.eventId = event_id; localEvent.eventId = event_id;
[message addEvent:localEvent withRoomState:mxRoom.state]; [message addEvent:localEvent withRoomState:mxRoom.state];
} }
if (message.attributedTextMessage.length) { if (! message.attributedTextMessage.length) {
// Refresh table display
[self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} else {
[messages removeObjectAtIndex:index]; [messages removeObjectAtIndex:index];
[self.messagesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} }
} else { } else {
message = nil; message = nil;
@ -1507,15 +1531,14 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
if (message) { if (message) {
// Refresh table display // Refresh table display
[messages replaceObjectAtIndex:index withObject:message]; [messages replaceObjectAtIndex:index withObject:message];
[self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} else { } else {
[messages removeObjectAtIndex:index]; [messages removeObjectAtIndex:index];
[self.messagesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} }
} }
break; break;
} }
} }
[self.messagesTableView reloadData];
} failure:^(NSError *error) { } failure:^(NSError *error) {
[self handleError:error forLocalEvent:localEvent]; [self handleError:error forLocalEvent:localEvent];
}]; }];
@ -1564,13 +1587,11 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
// Update table sources // Update table sources
RoomMessage *message = [[RoomMessage alloc] initWithEvent:mxEvent andRoomState:mxRoom.state]; RoomMessage *message = [[RoomMessage alloc] initWithEvent:mxEvent andRoomState:mxRoom.state];
if (message) { if (message) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:messages.count inSection:0];
[messages addObject:message]; [messages addObject:message];
[self.messagesTableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} else { } else {
NSLog(@"ERROR: Unable to add local event for attachment: %@", mxEvent.description); NSLog(@"ERROR: Unable to add local event for attachment: %@", mxEvent.description);
} }
[self.messagesTableView reloadData];
[self scrollToBottomAnimated:NO]; [self scrollToBottomAnimated:NO];
return mxEvent; return mxEvent;
} }
@ -1588,17 +1609,12 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
RoomMessage *message = [messages objectAtIndex:index]; RoomMessage *message = [messages objectAtIndex:index];
if ([message containsEventId:localEvent.eventId]) { if ([message containsEventId:localEvent.eventId]) {
NSLog(@"Posted event: %@", localEvent.description); NSLog(@"Posted event: %@", localEvent.description);
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
if (message.messageType == RoomMessageTypeText) { if (message.messageType == RoomMessageTypeText) {
[message removeEvent:localEvent.eventId]; [message removeEvent:localEvent.eventId];
localEvent.eventId = kFailedEventId; localEvent.eventId = kFailedEventId;
[message addEvent:localEvent withRoomState:mxRoom.state]; [message addEvent:localEvent withRoomState:mxRoom.state];
if (message.attributedTextMessage.length) { if (!message.attributedTextMessage.length) {
// Refresh table display
[self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} else {
[messages removeObjectAtIndex:index]; [messages removeObjectAtIndex:index];
[self.messagesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} }
} else { } else {
// Create a new message // Create a new message
@ -1607,15 +1623,14 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
if (message) { if (message) {
// Refresh table display // Refresh table display
[messages replaceObjectAtIndex:index withObject:message]; [messages replaceObjectAtIndex:index withObject:message];
[self.messagesTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} else { } else {
[messages removeObjectAtIndex:index]; [messages removeObjectAtIndex:index];
[self.messagesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
} }
} }
break; break;
} }
} }
[self.messagesTableView reloadData];
} }
- (BOOL)isIRCStyleCommand:(NSString*)text{ - (BOOL)isIRCStyleCommand:(NSString*)text{
@ -1878,7 +1893,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
NSData *videoData = [NSData dataWithContentsOfURL:tmpVideoLocation]; NSData *videoData = [NSData dataWithContentsOfURL:tmpVideoLocation];
[[NSFileManager defaultManager] removeItemAtPath:[tmpVideoLocation path] error:nil]; [[NSFileManager defaultManager] removeItemAtPath:[tmpVideoLocation path] error:nil];
if (videoData) { if (videoData) {
if (videoData.length < UPLOAD_FILE_SIZE) { if (videoData.length < ROOMVIEWCONTROLLER_UPLOAD_FILE_SIZE) {
[videoInfo setValue:[NSNumber numberWithUnsignedInteger:videoData.length] forKey:@"size"]; [videoInfo setValue:[NSNumber numberWithUnsignedInteger:videoData.length] forKey:@"size"];
[mxHandler.mxRestClient uploadContent:videoData mimeType:videoInfo[@"mimetype"] timeout:30 success:^(NSString *url) { [mxHandler.mxRestClient uploadContent:videoData mimeType:videoInfo[@"mimetype"] timeout:30 success:^(NSString *url) {
[videoContent setValue:url forKey:@"url"]; [videoContent setValue:url forKey:@"url"];