diff --git a/matrixConsole.xcodeproj/project.pbxproj b/matrixConsole.xcodeproj/project.pbxproj index a96a74363..66e2c1e50 100644 --- a/matrixConsole.xcodeproj/project.pbxproj +++ b/matrixConsole.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 3198D9E11A68338B00556695 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3198D9E31A68338B00556695 /* Localizable.strings */; }; - 32501A9B1A9B978400C5938F /* MXCRegistrationWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = 32501A9A1A9B978400C5938F /* MXCRegistrationWebView.m */; }; 710CC3BF1A6E9F14006EE973 /* matrixUser.png in Resources */ = {isa = PBXBuildFile; fileRef = 710CC3BE1A6E9F14006EE973 /* matrixUser.png */; }; 710CC3C21A70F28F006EE973 /* MXCContactField.m in Sources */ = {isa = PBXBuildFile; fileRef = 710CC3C11A70F28F006EE973 /* MXCContactField.m */; }; 71193D241A6D64F900E59A9E /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71193D231A6D64F900E59A9E /* AddressBook.framework */; }; @@ -56,7 +55,6 @@ F08DB7811A9B7C9300B73403 /* PublicRoomTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F08DB7801A9B7C9300B73403 /* PublicRoomTableCell.m */; }; F08DCBDB1A093BFA008C65B6 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F08DCBDA1A093BFA008C65B6 /* MobileCoreServices.framework */; }; F08E67961A77965A00AABD4C /* MXC3PID.m in Sources */ = {isa = PBXBuildFile; fileRef = F08E67931A77965A00AABD4C /* MXC3PID.m */; }; - F0AC79331A8394510056D042 /* AuthInputsView.m in Sources */ = {isa = PBXBuildFile; fileRef = F0AC79321A8394510056D042 /* AuthInputsView.m */; }; F0ADEFFB1AD7D2B3008A4F21 /* RoomMembersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F0ADEFFA1AD7D2B3008A4F21 /* RoomMembersViewController.m */; }; F0CEA5AE19E6895E00E47915 /* logoHighRes.png in Resources */ = {isa = PBXBuildFile; fileRef = F0CEA5AC19E6895E00E47915 /* logoHighRes.png */; }; F0CEA5AF19E6895E00E47915 /* tab_recents.png in Resources */ = {isa = PBXBuildFile; fileRef = F0CEA5AD19E6895E00E47915 /* tab_recents.png */; }; @@ -83,8 +81,6 @@ /* Begin PBXFileReference section */ 13057A57E74FD5504196F47F /* Pods-matrixConsole.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-matrixConsole.release.xcconfig"; path = "Pods/Target Support Files/Pods-matrixConsole/Pods-matrixConsole.release.xcconfig"; sourceTree = ""; }; 3198D9E21A68338B00556695 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; - 32501A991A9B978400C5938F /* MXCRegistrationWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCRegistrationWebView.h; sourceTree = ""; }; - 32501A9A1A9B978400C5938F /* MXCRegistrationWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCRegistrationWebView.m; sourceTree = ""; }; 710CC3BE1A6E9F14006EE973 /* matrixUser.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = matrixUser.png; sourceTree = ""; }; 710CC3C01A70F28F006EE973 /* MXCContactField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCContactField.h; sourceTree = ""; }; 710CC3C11A70F28F006EE973 /* MXCContactField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCContactField.m; sourceTree = ""; }; @@ -162,8 +158,6 @@ F08DCBDA1A093BFA008C65B6 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; F08E67921A77965A00AABD4C /* MXC3PID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXC3PID.h; sourceTree = ""; }; F08E67931A77965A00AABD4C /* MXC3PID.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXC3PID.m; sourceTree = ""; }; - F0AC79311A8394510056D042 /* AuthInputsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AuthInputsView.h; sourceTree = ""; }; - F0AC79321A8394510056D042 /* AuthInputsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AuthInputsView.m; sourceTree = ""; }; F0ADEFF91AD7D2B3008A4F21 /* RoomMembersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RoomMembersViewController.h; sourceTree = ""; }; F0ADEFFA1AD7D2B3008A4F21 /* RoomMembersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RoomMembersViewController.m; sourceTree = ""; }; F0CEA5AC19E6895E00E47915 /* logoHighRes.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logoHighRes.png; sourceTree = ""; }; @@ -302,14 +296,10 @@ F03EF5FC19F1762000A0EE52 /* View */ = { isa = PBXGroup; children = ( - F0AC79311A8394510056D042 /* AuthInputsView.h */, - F0AC79321A8394510056D042 /* AuthInputsView.m */, 7176294D1A77FED800927125 /* ContactDetailsTableCell.h */, 7176294E1A77FED800927125 /* ContactDetailsTableCell.m */, 71193D2A1A6E433900E59A9E /* ContactTableCell.h */, 71193D2B1A6E433900E59A9E /* ContactTableCell.m */, - 32501A991A9B978400C5938F /* MXCRegistrationWebView.h */, - 32501A9A1A9B978400C5938F /* MXCRegistrationWebView.m */, F08DB77F1A9B7C9300B73403 /* PublicRoomTableCell.h */, F08DB7801A9B7C9300B73403 /* PublicRoomTableCell.m */, F0E84D3E1A1F9AEC005F2E42 /* RecentsTableViewCell.h */, @@ -570,7 +560,6 @@ files = ( 7176294F1A77FED800927125 /* ContactDetailsTableCell.m in Sources */, F04A8AD81A3B3DF4008AC915 /* RoomTitleView.m in Sources */, - 32501A9B1A9B978400C5938F /* MXCRegistrationWebView.m in Sources */, F07A80DB19DD9DE700B621A1 /* AppDelegate.m in Sources */, F0ADEFFB1AD7D2B3008A4F21 /* RoomMembersViewController.m in Sources */, F08DB7811A9B7C9300B73403 /* PublicRoomTableCell.m in Sources */, @@ -600,7 +589,6 @@ F03EF5F719F171EB00A0EE52 /* AuthenticationViewController.m in Sources */, F0D3C30F1A01330F0000D49E /* SettingsTableViewCell.m in Sources */, 71D2E4EC1A49814B000DE015 /* RoomMemberActionsCell.m in Sources */, - F0AC79331A8394510056D042 /* AuthInputsView.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/matrixConsole/API/APNSHandler.m b/matrixConsole/API/APNSHandler.m index c41a47c01..ab52c2970 100644 --- a/matrixConsole/API/APNSHandler.m +++ b/matrixConsole/API/APNSHandler.m @@ -13,10 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. */ +#import #import "APNSHandler.h" #import "AppDelegate.h" -#import "MatrixHandler.h" NSString *const kAPNSHandlerHasBeenUpdated = @"kAPNSHandlerHasBeenUpdated"; @@ -91,7 +91,7 @@ static APNSHandler *sharedHandler = nil; - (void)setIsActive:(BOOL)isActive { // Refuse to try & turn push on if we're not logged in, it's nonsensical. - if (![MatrixHandler sharedHandler].accessToken) { + if (![MXKAccountManager sharedManager].accounts.count) { NSLog(@"[APNSHandler] Not setting push token because we're not logged in"); return; } @@ -126,18 +126,23 @@ static APNSHandler *sharedHandler = nil; } NSObject *kind = isActive ? @"http" : [NSNull null]; - - MXRestClient *restCli = [MatrixHandler sharedHandler].mxRestClient; - [restCli setPusherWithPushkey:b64Token kind:kind appId:appId appDisplayName:@"Matrix Console iOS" deviceDisplayName:[[UIDevice currentDevice] name] profileTag:profileTag lang:deviceLang data:pushData success:^{ - [[NSUserDefaults standardUserDefaults] setBool:transientActivity forKey:@"apnsIsActive"]; - [[NSUserDefaults standardUserDefaults] synchronize]; + + // TODO GFO Handle correctly multi-session here + NSArray *mxAccounts = [MXKAccountManager sharedManager].accounts; + for (MXKAccount *account in mxAccounts) { + MXRestClient *restCli = account.mxRestClient; - [[NSNotificationCenter defaultCenter] postNotificationName:kAPNSHandlerHasBeenUpdated object:nil]; - } failure:^(NSError *error) { - NSLog(@"[APNSHandler] Failed to send APNS token! (%@)", error); - - [[NSNotificationCenter defaultCenter] postNotificationName:kAPNSHandlerHasBeenUpdated object:nil]; - }]; + [restCli setPusherWithPushkey:b64Token kind:kind appId:appId appDisplayName:@"Matrix Console iOS" deviceDisplayName:[[UIDevice currentDevice] name] profileTag:profileTag lang:deviceLang data:pushData success:^{ + [[NSUserDefaults standardUserDefaults] setBool:transientActivity forKey:@"apnsIsActive"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + [[NSNotificationCenter defaultCenter] postNotificationName:kAPNSHandlerHasBeenUpdated object:nil]; + } failure:^(NSError *error) { + NSLog(@"[APNSHandler] Failed to send APNS token for %@! (%@)", account.mxCredentials.userId, error); + + [[NSNotificationCenter defaultCenter] postNotificationName:kAPNSHandlerHasBeenUpdated object:nil]; + }]; + } } @end diff --git a/matrixConsole/API/MatrixHandler.h b/matrixConsole/API/MatrixHandler.h index 5cd96813e..8b3969728 100644 --- a/matrixConsole/API/MatrixHandler.h +++ b/matrixConsole/API/MatrixHandler.h @@ -21,35 +21,14 @@ @property (strong, nonatomic) MXRestClient *mxRestClient; @property (strong, nonatomic) MXSession *mxSession; -@property (strong, nonatomic) NSString *homeServerURL; -@property (strong, nonatomic) NSString *homeServer; -@property (strong, nonatomic) NSString *userLogin; -@property (strong, nonatomic) NSString *userId; -@property (strong, nonatomic, readonly) NSString *localPartFromUserId; -@property (strong, nonatomic) NSString *accessToken; -@property (strong, nonatomic) NSString *identityServerURL; - -// Matrix user's settings -@property (nonatomic) MXPresence userPresence; - + (MatrixHandler *)sharedHandler; -- (void)pauseInBackgroundTask; -- (void)resume; -- (void)logout; - -// Flush and restore Matrix data -- (void)reload:(BOOL)clearCache; - // Searches if a private OneToOne room has been started with this user // Returns the room ID (nil if not found) - (NSString*)privateOneToOneRoomIdWithUserId:(NSString*)userId; // Reopens an existing private OneToOne room with this userId or creates a new one (if it doesn't exist) - (void)startPrivateOneToOneRoomWithUserId:(NSString*)userId; -// Enables inApp notifications for a dedicated room if they were disabled -- (void)restoreInAppNotificationsForRoomId:(NSString*)roomID; - // user power level in a dedicated room - (CGFloat)getPowerLevel:(MXRoomMember *)roomMember inRoom:(MXRoom *)room; diff --git a/matrixConsole/API/MatrixHandler.m b/matrixConsole/API/MatrixHandler.m index 294980754..085ba7aec 100644 --- a/matrixConsole/API/MatrixHandler.m +++ b/matrixConsole/API/MatrixHandler.m @@ -17,44 +17,8 @@ #import "MatrixHandler.h" #import "AppDelegate.h" -#import "MXFileStore.h" -#import "MXTools.h" - -#import "AFNetworkReachabilityManager.h" - -#import - static MatrixHandler *sharedHandler = nil; -@interface MatrixHandler () { - // We will notify user only once on session failure - BOOL notifyOpenSessionFailure; - NSTimer* initialServerSyncTimer; - - // Handle user's settings change - id userUpdateListener; - // Handle events notification - id notificationCenterListener; - // Reachability observer - id reachabilityObserver; - - // Used for logging application start up - NSDate *openSessionStartDate; - - MXKEventFormatter *eventFormatter; -} - -@property (strong, nonatomic) MXFileStore *mxFileStore; - -@property (strong, nonatomic) MXKAlert *mxNotification; -@property (nonatomic) UIBackgroundTaskIdentifier bgTask; - -// When the user cancels an inApp notification -// assume that any messagge room will be ignored -// until the next launch / debackground -@property (nonatomic,readwrite) NSMutableArray* unnotifiedRooms; -@end - @implementation MatrixHandler + (MatrixHandler *)sharedHandler { @@ -67,488 +31,20 @@ static MatrixHandler *sharedHandler = nil; return sharedHandler; } -#pragma mark - - --(MatrixHandler *)init { - if (self = [super init]) { - _userPresence = MXPresenceUnknown; - notifyOpenSessionFailure = YES; - - // Read potential homeserver url in shared defaults object - if (self.homeServerURL) { - self.mxRestClient = [[MXRestClient alloc] initWithHomeServer:self.homeServerURL]; - if (self.identityServerURL) { - [self.mxRestClient setIdentityServer:self.identityServerURL]; - } - - if (self.accessToken) { - [self openSession]; - } - } - - _unnotifiedRooms = [[NSMutableArray alloc] init]; - - [[MXKAppSettings standardAppSettings] addObserver:self forKeyPath:@"enableInAppNotifications" options:0 context:nil]; - [[MXKAppSettings standardAppSettings] addObserver:self forKeyPath:@"showAllEventsInRoomHistory" options:0 context:nil]; - } - return self; +- (MXSession*)mxSession { + // Only the first account is presently used + MXKAccount *account = [[MXKAccountManager sharedManager].accounts firstObject]; + return account.mxSession; } -- (void)dealloc { - [[MXKAppSettings standardAppSettings] removeObserver:self forKeyPath:@"enableInAppNotifications"]; - [[MXKAppSettings standardAppSettings] removeObserver:self forKeyPath:@"showAllEventsInRoomHistory"]; - - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [[NSNotificationCenter defaultCenter] removeObserver:reachabilityObserver]; - reachabilityObserver = nil; - - [initialServerSyncTimer invalidate]; - initialServerSyncTimer = nil; - - _unnotifiedRooms = nil; - - [self closeSession]; - self.mxSession = nil; - - if (self.mxNotification) { - [self.mxNotification dismiss:NO]; - self.mxNotification = nil; - } +- (MXRestClient*)mxRestClient { + // Only the first account is presently used + MXKAccount *account = [[MXKAccountManager sharedManager].accounts firstObject]; + return account.mxRestClient; } -- (void)openSession { - MXCredentials *credentials = [[MXCredentials alloc] initWithHomeServer:self.homeServerURL - userId:self.userId - accessToken:self.accessToken]; - openSessionStartDate = [NSDate date]; - self.mxRestClient = [[MXRestClient alloc] initWithCredentials:credentials]; - if (self.mxRestClient) { - // Set identity server (if any) - if (self.identityServerURL) { - [self.mxRestClient setIdentityServer:self.identityServerURL]; - } - - // Use MXFileStore as MXStore to permanently store events - _mxFileStore = [[MXFileStore alloc] init]; - - self.mxSession = [[MXSession alloc] initWithMatrixRestClient:self.mxRestClient]; - - // Build MXEvent -> NSString formatter - eventFormatter = [[MXKEventFormatter alloc] initWithMatrixSession:self.mxSession]; - eventFormatter.isForSubtitle = YES; - - __weak typeof(self) weakSelf = self; - [self.mxSession setStore:_mxFileStore success:^{ - // Complete session registration by launching live stream - typeof(self) self = weakSelf; - [self launchInitialServerSync]; - } failure:^(NSError *error) { - // This cannot happen. Loading of MXFileStore cannot fail. - typeof(self) self = weakSelf; - self.mxSession = nil; - }]; - } -} - -- (void)launchInitialServerSync { - // Complete the session registration when store data is ready. - - // Cancel potential reachability observer and pending action - [[NSNotificationCenter defaultCenter] removeObserver:reachabilityObserver]; - reachabilityObserver = nil; - [initialServerSyncTimer invalidate]; - initialServerSyncTimer = nil; - - // Sanity check - if (self.mxSession.state != MXSessionStateStoreDataReady) { - NSLog(@"[MatrixHandler] Initial server sync is applicable only when store data is ready to complete session initialisation"); - return; - } - - // Launch mxSession - [self.mxSession start:^{ - NSLog(@"[MatrixHandler] The app is ready. Matrix SDK session has been started in %0.fms.", [[NSDate date] timeIntervalSinceDate:openSessionStartDate] * 1000); - [self setUserPresence:MXPresenceOnline andStatusMessage:nil completion:nil]; - - // Register listener to update user's information - userUpdateListener = [self.mxSession.myUser listenToUserUpdate:^(MXEvent *event) { - // Consider only events related to user's presence - if (event.eventType == MXEventTypePresence) { - MXPresence presence = [MXTools presence:event.content[@"presence"]]; - if (self.userPresence != presence) { - // Handle user presence on multiple devices (keep the more pertinent) - if (self.userPresence == MXPresenceOnline) { - if (presence == MXPresenceUnavailable || presence == MXPresenceOffline) { - // Force the local presence to overwrite the user presence on server side - [self setUserPresence:_userPresence andStatusMessage:nil completion:nil]; - return; - } - } else if (self.userPresence == MXPresenceUnavailable) { - if (presence == MXPresenceOffline) { - // Force the local presence to overwrite the user presence on server side - [self setUserPresence:_userPresence andStatusMessage:nil completion:nil]; - return; - } - } - self.userPresence = presence; - } - } - }]; - - // Check whether the app user wants notifications on new events - if ([[MXKAppSettings standardAppSettings] enableInAppNotifications]) { - [self enableInAppNotifications:YES]; - } - } failure:^(NSError *error) { - NSLog(@"[MatrixHandler] Initial Sync failed: %@", error); - if (notifyOpenSessionFailure) { - //Alert user only once - notifyOpenSessionFailure = NO; - [[AppDelegate theDelegate] showErrorAsAlert:error]; - } - - // Check network reachability - if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorNotConnectedToInternet) { - // Add observer to launch a new attempt according to reachability. - reachabilityObserver = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingReachabilityDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - NSNumber *statusItem = note.userInfo[AFNetworkingReachabilityNotificationStatusItem]; - if (statusItem) { - AFNetworkReachabilityStatus reachabilityStatus = statusItem.integerValue; - if (reachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi || reachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN) { - // New attempt - [self launchInitialServerSync]; - } - } - }]; - } else { - // Postpone a new attempt in 10 sec - initialServerSyncTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(launchInitialServerSync) userInfo:self repeats:NO]; - } - }]; -} - -- (void)closeSession { - - if (notificationCenterListener) { - [self.mxSession.notificationCenter removeListener:notificationCenterListener]; - notificationCenterListener = nil; - } - if (userUpdateListener) { - [self.mxSession.myUser removeListener:userUpdateListener]; - userUpdateListener = nil; - } - - [self.mxSession close]; - self.mxSession = nil; - - [self.mxRestClient close]; - - if (self.homeServerURL) { - self.mxRestClient = [[MXRestClient alloc] initWithHomeServer:self.homeServerURL]; - if (self.identityServerURL) { - [self.mxRestClient setIdentityServer:self.identityServerURL]; - } - } else { - self.mxRestClient = nil; - } - - notifyOpenSessionFailure = YES; -} - -#pragma mark - - -- (void)pauseInBackgroundTask { - // Hide potential notification - if (self.mxNotification) { - [self.mxNotification dismiss:NO]; - self.mxNotification = nil; - } - - _unnotifiedRooms = [[NSMutableArray alloc] init]; - - if (self.mxSession && self.mxSession.state == MXSessionStateRunning) { - _bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ - [[UIApplication sharedApplication] endBackgroundTask:_bgTask]; - _bgTask = UIBackgroundTaskInvalid; - - NSLog(@"[MatrixHandler] pauseInBackgroundTask : %08lX expired", (unsigned long)_bgTask); - }]; - - NSLog(@"[MatrixHandler] pauseInBackgroundTask : %08lX starts", (unsigned long)_bgTask); - // Pause SDK - [self.mxSession pause]; - // Update user presence - __weak typeof(self) weakSelf = self; - [self setUserPresence:MXPresenceUnavailable andStatusMessage:nil completion:^{ - NSLog(@"[MatrixHandler] pauseInBackgroundTask : %08lX ends", (unsigned long)weakSelf.bgTask); - [[UIApplication sharedApplication] endBackgroundTask:weakSelf.bgTask]; - weakSelf.bgTask = UIBackgroundTaskInvalid; - NSLog(@"[MatrixHandler] >>>>> background pause task finished"); - }]; - } else { - // Cancel pending actions - [[NSNotificationCenter defaultCenter] removeObserver:reachabilityObserver]; - reachabilityObserver = nil; - [initialServerSyncTimer invalidate]; - initialServerSyncTimer = nil; - } -} - -- (void)resume { - if (self.mxSession) { - if (self.mxSession.state == MXSessionStatePaused) { - // Resume SDK and update user presence - [self.mxSession resume:^{ - [self setUserPresence:MXPresenceOnline andStatusMessage:nil completion:nil]; - }]; - } else if (self.mxSession.state == MXSessionStateStoreDataReady) { - // The session initialisation was uncompleted, we try to complete it here. - [self launchInitialServerSync]; - } - - if (_bgTask) { - // Cancel background task - [[UIApplication sharedApplication] endBackgroundTask:_bgTask]; - _bgTask = UIBackgroundTaskInvalid; - NSLog(@"[MatrixHandler] pauseInBackgroundTask : %08lX cancelled", (unsigned long)_bgTask); - } - } -} - -- (void)logout { - NSLog(@"[MatrixHandler] logout"); - - //[self setUserPresence:MXPresenceOffline andStatusMessage:nil completion:nil]; - - // Reset access token (mxSession is closed by setter) - self.accessToken = nil; - self.userId = nil; - self.homeServer = nil; - - _unnotifiedRooms = [[NSMutableArray alloc] init]; - // Keep userLogin, homeServerUrl -} - -- (void)reload:(BOOL)clearCache { - if (self.mxSession) { - [self closeSession]; - notifyOpenSessionFailure = NO; - - // Force back to Recents list if room details is displayed (Room details are not available until the end of initial sync) - [[AppDelegate theDelegate].masterTabBarController popRoomViewControllerAnimated:NO]; - - [[MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mxSession] reset]; - - if (clearCache) { - // clear the media cache - [MXKMediaManager clearCache]; - - [_mxFileStore deleteAllData]; - } - - if (self.accessToken) { - [self openSession]; - } - } -} - -- (void)enableInAppNotifications:(BOOL)isEnabled { - if (isEnabled) { - // Register on notification center - notificationCenterListener = [self.mxSession.notificationCenter listenToNotifications:^(MXEvent *event, MXRoomState *roomState, MXPushRule *rule) { - - // Apply first the event filter defined in the related room data source - MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mxSession]; - MXKRoomDataSource *roomDataSource = [roomDataSourceManager roomDataSourceForRoom:event.roomId create:NO]; - if (!roomDataSource || [roomDataSource.eventsFilterForMessages indexOfObject:event.type] == NSNotFound) { - // Ignore - return; - } - - // Check conditions to display this notification - if (![[AppDelegate theDelegate].masterTabBarController.visibleRoomId isEqualToString:event.roomId] - && ![[AppDelegate theDelegate].masterTabBarController isPresentingMediaPicker] - && ([self.unnotifiedRooms indexOfObject:event.roomId] == NSNotFound)) { - - MXKEventFormatterError error; - NSString* messageText = [eventFormatter stringFromEvent:event withRoomState:roomState error:&error]; - if (messageText.length && (error == MXKEventFormatterErrorNone)) { - - // Removing existing notification (if any) - if (self.mxNotification) { - [self.mxNotification dismiss:NO]; - } - - // Check whether tweak is required - for (MXPushRuleAction *ruleAction in rule.actions) { - if (ruleAction.actionType == MXPushRuleActionTypeSetTweak) { - if ([[ruleAction.parameters valueForKey:@"set_tweak"] isEqualToString:@"sound"]) { - // Play system sound (VoicemailReceived) - AudioServicesPlaySystemSound (1002); - } - } - } - - __weak typeof(self) weakSelf = self; - self.mxNotification = [[MXKAlert alloc] initWithTitle:roomState.displayname - message:messageText - style:MXKAlertStyleAlert]; - self.mxNotification.cancelButtonIndex = [self.mxNotification addActionWithTitle:@"Cancel" - style:MXKAlertActionStyleDefault - handler:^(MXKAlert *alert) { - weakSelf.mxNotification = nil; - [weakSelf.unnotifiedRooms addObject:event.roomId]; - }]; - [self.mxNotification addActionWithTitle:@"View" - style:MXKAlertActionStyleDefault - handler:^(MXKAlert *alert) { - weakSelf.mxNotification = nil; - // Show the room - [[AppDelegate theDelegate].masterTabBarController showRoom:event.roomId]; - }]; - - [self.mxNotification showInViewController:[[AppDelegate theDelegate].masterTabBarController selectedViewController]]; - } - } - }]; - } else { - if (notificationCenterListener) { - [self.mxSession.notificationCenter removeListener:notificationCenterListener]; - notificationCenterListener = nil; - } - if (self.mxNotification) { - [self.mxNotification dismiss:NO]; - self.mxNotification = nil; - } - } -} - -#pragma mark User's profile - -- (NSString *)homeServerURL { - return [[NSUserDefaults standardUserDefaults] objectForKey:@"homeserverurl"]; -} - -- (void)setHomeServerURL:(NSString *)inHomeServerURL { - if (inHomeServerURL.length) { - [[NSUserDefaults standardUserDefaults] setObject:inHomeServerURL forKey:@"homeserverurl"]; - self.mxRestClient = [[MXRestClient alloc] initWithHomeServer:inHomeServerURL]; - // Set identity server (if any) - if (self.identityServerURL) { - [self.mxRestClient setIdentityServer:self.identityServerURL]; - } - } else { - [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"homeserverurl"]; - // Reinitialize matrix handler - [self logout]; - } - [[NSUserDefaults standardUserDefaults] synchronize]; -} - -- (NSString *)homeServer { - return [[NSUserDefaults standardUserDefaults] objectForKey:@"homeserver"]; -} - -- (void)setHomeServer:(NSString *)inHomeserver { - if (inHomeserver.length) { - [[NSUserDefaults standardUserDefaults] setObject:inHomeserver forKey:@"homeserver"]; - } else { - [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"homeserver"]; - } - [[NSUserDefaults standardUserDefaults] synchronize]; -} - -- (NSString *)userLogin { - return [[NSUserDefaults standardUserDefaults] objectForKey:@"userlogin"]; -} - -- (void)setUserLogin:(NSString *)inUserLogin { - if (inUserLogin.length) { - [[NSUserDefaults standardUserDefaults] setObject:inUserLogin forKey:@"userlogin"]; - } else { - [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"userlogin"]; - } - [[NSUserDefaults standardUserDefaults] synchronize]; -} - -- (NSString *)userId { - return [[NSUserDefaults standardUserDefaults] objectForKey:@"userid"]; -} - -- (void)setUserId:(NSString *)inUserId { - if (inUserId.length) { - [[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 { - [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"userid"]; - [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"localuserid"]; - } - [[NSUserDefaults standardUserDefaults] synchronize]; -} - -- (NSString *)localPartFromUserId { - return [[NSUserDefaults standardUserDefaults] objectForKey:@"localuserid"]; -} - -- (NSString *)accessToken { - return [[NSUserDefaults standardUserDefaults] objectForKey:@"accesstoken"]; -} - -- (void)setAccessToken:(NSString *)inAccessToken { - if (inAccessToken.length) { - [[NSUserDefaults standardUserDefaults] setObject:inAccessToken forKey:@"accesstoken"]; - [[AppDelegate theDelegate] registerUserNotificationSettings]; - [self openSession]; - } else { - [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"accesstoken"]; - [self closeSession]; - } - [[NSUserDefaults standardUserDefaults] synchronize]; -} - -- (NSString *)identityServerURL { - return [[NSUserDefaults standardUserDefaults] objectForKey:@"identityserverurl"]; -} - -- (void)setIdentityServerURL:(NSString *)inIdentityServerURL { - if (inIdentityServerURL.length) { - [[NSUserDefaults standardUserDefaults] setObject:inIdentityServerURL forKey:@"identityserverurl"]; - } else { - [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"identityserverurl"]; - } - [[NSUserDefaults standardUserDefaults] synchronize]; - - // Update the current restClient - if (self.mxRestClient) { - [self.mxRestClient setIdentityServer:self.identityServerURL]; - } -} - -#pragma mark - Matrix user's settings - -- (void)setUserPresence:(MXPresence)userPresence andStatusMessage:(NSString *)statusMessage completion:(void (^)(void))completion { - self.userPresence = userPresence; - // Update user presence on server side - [self.mxSession.myUser setPresence:userPresence andStatusMessage:statusMessage success:^{ - NSLog(@"[MatrixHandler] Set user presence (%lu) succeeded", (unsigned long)userPresence); - if (completion) { - completion(); - } - } failure:^(NSError *error) { - NSLog(@"[MatrixHandler] Set user presence (%lu) failed: %@", (unsigned long)userPresence, error); - }]; -} +// FIXME GFO Move the following methods in SDK and Remove MatrixHandler class #pragma mark - Room handling @@ -602,7 +98,7 @@ static MatrixHandler *sharedHandler = nil; topic:nil success:^(MXRoom *room) { // invite the other user only if it is defined and not onself - if (userId && ![self.userId isEqualToString:userId]) { + if (userId && ![self.mxSession.myUser.userId isEqualToString:userId]) { // add the user [room inviteUser:userId success:^{ } failure:^(NSError *error) { @@ -624,13 +120,6 @@ static MatrixHandler *sharedHandler = nil; } } -- (void)restoreInAppNotificationsForRoomId:(NSString*)roomID { - if (roomID) { - // Enable inApp notification for this room - [self.unnotifiedRooms removeObject:roomID]; - } -} - - (CGFloat)getPowerLevel:(MXRoomMember *)roomMember inRoom:(MXRoom *)room { CGFloat powerLevel = 0; @@ -680,17 +169,4 @@ static MatrixHandler *sharedHandler = nil; return nil; } } - -#pragma mark - KVO - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if ([@"showAllEventsInRoomHistory" isEqualToString:keyPath]) { - // Flush and restore Matrix data - [self reload:NO]; - } - else if ([@"enableInAppNotifications" isEqualToString:keyPath]) { - [self enableInAppNotifications:[[MXKAppSettings standardAppSettings] enableInAppNotifications]]; - } -} - @end diff --git a/matrixConsole/API/RageShakeManager.m b/matrixConsole/API/RageShakeManager.m index 5c773f118..3888c6e9f 100644 --- a/matrixConsole/API/RageShakeManager.m +++ b/matrixConsole/API/RageShakeManager.m @@ -194,7 +194,6 @@ static RageShakeManager* sharedInstance = nil; NSString* appVersion = [AppDelegate theDelegate].appVersion; NSString* build = [AppDelegate theDelegate].build; - MatrixHandler *mxHandler = [MatrixHandler sharedHandler]; NSMutableString* message = [[NSMutableString alloc] init]; @@ -202,14 +201,18 @@ static RageShakeManager* sharedInstance = nil; [message appendFormat:@"-----> my comments <-----\n\n\n"]; + [message appendFormat:@"------------------------------\n"]; + [message appendFormat:@"Account info\n"]; + + NSArray *mxAccounts = [MXKAccountManager sharedManager].accounts; + for (MXKAccount* account in mxAccounts) { + [message appendFormat:@"userId: %@\n", account.mxCredentials.userId]; + [message appendFormat:@"displayname: %@\n", account.mxSession.myUser.displayname]; + [message appendFormat:@"homeServerURL: %@\n", account.mxCredentials.homeServer]; + } + [message appendFormat:@"------------------------------\n"]; [message appendFormat:@"Application info\n"]; - [message appendFormat:@"userId: %@\n", mxHandler.userId]; - [message appendFormat:@"displayname: %@\n", mxHandler.mxSession.myUser.displayname]; - [message appendFormat:@"\n"]; - [message appendFormat:@"homeServerURL: %@\n", mxHandler.homeServerURL]; - [message appendFormat:@"homeServer: %@\n", mxHandler.homeServer]; - [message appendFormat:@"\n"]; [message appendFormat:@"Console version: %@\n", appVersion]; [message appendFormat:@"MatrixKit version: %@\n", MatrixKitVersion]; [message appendFormat:@"MatrixSDK version: %@\n", MatrixSDKVersion]; diff --git a/matrixConsole/AppDelegate.h b/matrixConsole/AppDelegate.h index 68a7fba76..7cbf3613f 100644 --- a/matrixConsole/AppDelegate.h +++ b/matrixConsole/AppDelegate.h @@ -38,6 +38,8 @@ - (void)registerUserNotificationSettings; +- (void)reloadMatrixSessions:(BOOL)clearCache; + - (void)logout; - (MXKAlert*)showErrorAsAlert:(NSError*)error; diff --git a/matrixConsole/AppDelegate.m b/matrixConsole/AppDelegate.m index 9ac7e30e2..c6f998fe8 100644 --- a/matrixConsole/AppDelegate.m +++ b/matrixConsole/AppDelegate.m @@ -17,13 +17,14 @@ #import "AppDelegate.h" #import "APNSHandler.h" #import "RoomViewController.h" -#import "MatrixHandler.h" #import "SettingsViewController.h" #import "ContactManager.h" #import "RageShakeManager.h" #import "AFNetworkReachabilityManager.h" +#import + #define MAKE_STRING(x) #x #define MAKE_NS_STRING(x) @MAKE_STRING(x) @@ -33,8 +34,13 @@ // matrix session observer used to detect new opened sessions. id matrixSessionStateObserver; + + // matrix account observer used to detect new added accounts. + id matrixAccountsObserver; } +@property (strong, nonatomic) MXKAlert *mxInAppNotification; + @end @implementation AppDelegate @@ -103,6 +109,7 @@ #pragma mark - UIApplicationDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. if ([self.window.rootViewController isKindOfClass:[MasterTabBarController class]]) { @@ -145,28 +152,8 @@ [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; [[NSUserDefaults standardUserDefaults] synchronize]; - // Register matrix session state observer in order to handle new opened session - matrixSessionStateObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionStateDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { - - MXSession *mxSession = (MXSession*)notif.object; - - // Check whether the concerned session is a new one - if (mxSession.state == MXSessionStateInitialised) { - - // Report this new session to contact manager - [[ContactManager sharedManager] setMxSession:mxSession]; - - // Update all view controllers thanks to tab bar controller - self.masterTabBarController.mxSession = mxSession; - } - }]; - - // Check whether we're logged in - if ([MatrixHandler sharedHandler].accessToken) { - [self registerUserNotificationSettings]; - // When user is already logged, we launch the app on Recents - [self.masterTabBarController setSelectedIndex:TABBAR_RECENTS_INDEX]; - } + // Add matrix observers and initialize matrix sessions. + [self initMatrixSessions]; } // clear the notifications counter @@ -192,10 +179,20 @@ self.isOffline = NO; [[AFNetworkReachabilityManager sharedManager] stopMonitoring]; - // check if some media msut be released to reduce the cache size + // check if some media must be released to reduce the cache size [MXKMediaManager reduceCacheSizeToInsert:0]; - // Suspend Matrix handler - [[MatrixHandler sharedHandler] pauseInBackgroundTask]; + + // Hide potential notification + if (self.mxInAppNotification) { + [self.mxInAppNotification dismiss:NO]; + self.mxInAppNotification = nil; + } + + // Suspend all running matrix sessions + NSArray *mxAccounts = [MXKAccountManager sharedManager].accounts; + for (MXKAccount *account in mxAccounts) { + [account pauseInBackgroundTask]; + } // clear the notifications counter [self clearNotifications]; @@ -217,8 +214,11 @@ // Start monitoring reachability [[AFNetworkReachabilityManager sharedManager] startMonitoring]; - // Resume Matrix handler - [[MatrixHandler sharedHandler] resume]; + // Resume all existing matrix sessions + NSArray *mxAccounts = [MXKAccountManager sharedManager].accounts; + for (MXKAccount *account in mxAccounts) { + [account resume]; + } // refresh the contacts list [[ContactManager sharedManager] fullRefresh]; @@ -291,7 +291,95 @@ } } -#pragma mark - +#pragma mark - Matrix sessions handling + +- (void)initMatrixSessions { + + // Register matrix session state observer in order to handle new opened session + matrixSessionStateObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionStateDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + + MXSession *mxSession = (MXSession*)notif.object; + + // Check whether the concerned session is a new one + if (mxSession.state == MXSessionStateInitialised) { + + // Report this new session to contact manager + [[ContactManager sharedManager] setMxSession:mxSession]; + + // Update all view controllers thanks to tab bar controller + self.masterTabBarController.mxSession = mxSession; + + // Check whether the app user wants notifications on new events + if ([[MXKAppSettings standardAppSettings] enableInAppNotifications]) { + [self enableInAppNotifications:YES]; + } + } + }]; + + // Register an observer in order to handle new account + matrixAccountsObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidAddAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + + NSString *userId = (NSString*)notif.object; + + // TODO GFO: handle here multi sessions + + // Launch matrix session for this new account + MXKAccount *account = [[MXKAccountManager sharedManager] accountForUserId:userId]; + if (account) { + [self registerUserNotificationSettings]; + + // Use MXFileStore as MXStore to permanently store events. + MXFileStore *mxFileStore = [[MXFileStore alloc] init]; + [account openSessionWithStore:mxFileStore]; + } + }]; + + // Observe settings changes + [[MXKAppSettings standardAppSettings] addObserver:self forKeyPath:@"enableInAppNotifications" options:0 context:nil]; + [[MXKAppSettings standardAppSettings] addObserver:self forKeyPath:@"showAllEventsInRoomHistory" options:0 context:nil]; + + // Check whether we're logged in + NSArray *mxAccounts = [MXKAccountManager sharedManager].accounts; + if (mxAccounts.count) { + + [self registerUserNotificationSettings]; + + // When user is already logged, we launch the app on Recents + [self.masterTabBarController setSelectedIndex:TABBAR_RECENTS_INDEX]; + + // Launch a matrix session only for the first account (TODO launch a session for each account). + // Use MXFileStore as MXStore to permanently store events. + MXFileStore *mxFileStore = [[MXFileStore alloc] init]; + [mxAccounts.firstObject openSessionWithStore:mxFileStore]; + } +} + +- (void)reloadMatrixSessions:(BOOL)clearCache { + + // Reload all running matrix sessions + NSArray *mxAccounts = [MXKAccountManager sharedManager].accounts; + for (MXKAccount *account in mxAccounts) { + + if (account.mxSession) { + id store = account.mxSession.store; + + [[MXKRoomDataSourceManager sharedManagerForMatrixSession:account.mxSession] reset]; + + if (clearCache) { + [store deleteAllData]; + } + [account openSessionWithStore:store]; + } + } + + // Force back to Recents list if room details is displayed (Room details are not available until the end of initial sync) + [self.masterTabBarController popRoomViewControllerAnimated:NO]; + + if (clearCache) { + // clear the media cache + [MXKMediaManager clearCache]; + } +} - (void)logout { @@ -302,8 +390,8 @@ // Clear cache [MXKMediaManager clearCache]; - // Logout Matrix - [[MatrixHandler sharedHandler] logout]; + // Logout all matrix account + [[MXKAccountManager sharedManager] logout]; // Reset mxSession information in all view controllers self.masterTabBarController.mxSession = nil; @@ -363,6 +451,87 @@ [[UIApplication sharedApplication] cancelAllLocalNotifications]; } +- (void)enableInAppNotifications:(BOOL)isEnabled { + + // Update In-App notifications in all running matrix sessions + NSArray *mxAccounts = [MXKAccountManager sharedManager].accounts; + for (MXKAccount *account in mxAccounts) { + + if (account.mxSession) { + if (isEnabled) { + // Build MXEvent -> NSString formatter + MXKEventFormatter *eventFormatter = [[MXKEventFormatter alloc] initWithMatrixSession:account.mxSession]; + eventFormatter.isForSubtitle = YES; + + [account listenToNotifications:^(MXEvent *event, MXRoomState *roomState, MXPushRule *rule) { + + // Check conditions to display this notification (TODO GFO multi-session handling) + if (![self.masterTabBarController.visibleRoomId isEqualToString:event.roomId] + && ![self.masterTabBarController isPresentingMediaPicker]) { + + MXKEventFormatterError error; + NSString* messageText = [eventFormatter stringFromEvent:event withRoomState:roomState error:&error]; + if (messageText.length && (error == MXKEventFormatterErrorNone)) { + + // Removing existing notification (if any) + if (self.mxInAppNotification) { + [self.mxInAppNotification dismiss:NO]; + } + + // Check whether tweak is required + for (MXPushRuleAction *ruleAction in rule.actions) { + if (ruleAction.actionType == MXPushRuleActionTypeSetTweak) { + if ([[ruleAction.parameters valueForKey:@"set_tweak"] isEqualToString:@"sound"]) { + // Play system sound (VoicemailReceived) + AudioServicesPlaySystemSound (1002); + } + } + } + + __weak typeof(self) weakSelf = self; + self.mxInAppNotification = [[MXKAlert alloc] initWithTitle:roomState.displayname + message:messageText + style:MXKAlertStyleAlert]; + self.mxInAppNotification.cancelButtonIndex = [self.mxInAppNotification addActionWithTitle:@"Cancel" + style:MXKAlertActionStyleDefault + handler:^(MXKAlert *alert) { + weakSelf.mxInAppNotification = nil; + [account updateNotificationListenerForRoomId:event.roomId ignore:YES]; + }]; + [self.mxInAppNotification addActionWithTitle:@"View" + style:MXKAlertActionStyleDefault + handler:^(MXKAlert *alert) { + weakSelf.mxInAppNotification = nil; + // Show the room + [weakSelf.masterTabBarController showRoom:event.roomId]; + }]; + + [self.mxInAppNotification showInViewController:[self.masterTabBarController selectedViewController]]; + } + } + }]; + } else { + [account removeNotificationListener]; + } + } + } + + if (self.mxInAppNotification) { + [self.mxInAppNotification dismiss:NO]; + self.mxInAppNotification = nil; + } +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if ([@"showAllEventsInRoomHistory" isEqualToString:keyPath]) { + // Flush and restore Matrix data + [self reloadMatrixSessions:NO]; + } + else if ([@"enableInAppNotifications" isEqualToString:keyPath]) { + [self enableInAppNotifications:[[MXKAppSettings standardAppSettings] enableInAppNotifications]]; + } +} + #pragma mark - SplitViewController delegate - (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController { diff --git a/matrixConsole/Base.lproj/Main.storyboard b/matrixConsole/Base.lproj/Main.storyboard index 487819b5f..205917797 100644 --- a/matrixConsole/Base.lproj/Main.storyboard +++ b/matrixConsole/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -587,433 +587,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1647,7 +1222,6 @@ - diff --git a/matrixConsole/View/AuthInputsView.h b/matrixConsole/View/AuthInputsView.h deleted file mode 100644 index 704ac1133..000000000 --- a/matrixConsole/View/AuthInputsView.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - 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 - -// Authentication type: register or login -typedef enum { - AuthenticationTypeRegister, - AuthenticationTypeLogin -} -AuthenticationType; - -@class AuthInputsView; - -@protocol AuthInputsViewDelegate -@optional -- (void)authInputsDoneKeyHasBeenPressed:(AuthInputsView *)authInputsView; -@end - -@interface AuthInputsView : UIView - -@property (nonatomic) AuthenticationType authType; -@property (nonatomic) id delegate; -// Optional fields added in case of registration -@property (weak, nonatomic) IBOutlet UITextField *displayNameTextField; - -- (CGFloat)actualHeight; -- (BOOL)areAllRequiredFieldsFilled; -- (void)dismissKeyboard; - -- (void)nextStep; -- (void)resetStep; -@end - -@interface AuthInputsPasswordBasedView : AuthInputsView -@property (weak, nonatomic) IBOutlet UITextField *userLoginTextField; -@property (weak, nonatomic) IBOutlet UITextField *passWordTextField; -// Optional fields added in case of registration -@property (weak, nonatomic) IBOutlet UITextField *emailTextField; -@property (weak, nonatomic) IBOutlet UILabel *emailInfoLabel; -@end - -@interface AuthInputsEmailCodeBasedView : AuthInputsView -@property (weak, nonatomic) IBOutlet UITextField *userLoginTextField; -@property (weak, nonatomic) IBOutlet UITextField *emailAndTokenTextField; -@property (weak, nonatomic) IBOutlet UILabel *promptEmailTokenLabel; -@end diff --git a/matrixConsole/View/AuthInputsView.m b/matrixConsole/View/AuthInputsView.m deleted file mode 100644 index 8223dad78..000000000 --- a/matrixConsole/View/AuthInputsView.m +++ /dev/null @@ -1,209 +0,0 @@ -/* - 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 "AuthInputsView.h" -#import "MatrixHandler.h" - -@implementation AuthInputsView - -- (CGFloat)actualHeight { - return self.frame.size.height; -} - -- (BOOL)areAllRequiredFieldsFilled { - // Currently no field to check here - return YES; -} - -- (void)setAuthType:(AuthenticationType)authType { - if (authType == AuthenticationTypeLogin) { - self.displayNameTextField.hidden = YES; - } else { - self.displayNameTextField.hidden = NO; - } - _authType = authType; -} - -- (void)dismissKeyboard { - [self.displayNameTextField resignFirstResponder]; -} - -- (void)nextStep { - self.displayNameTextField.hidden = YES; -} - -- (void)resetStep { - self.authType = _authType; -} - -@end - -#pragma mark - AuthInputsPasswordBasedView - -@implementation AuthInputsPasswordBasedView - -- (CGFloat)actualHeight { - if (self.authType == AuthenticationTypeLogin) { - return self.displayNameTextField.frame.origin.y; - } - return super.actualHeight; -} - -- (BOOL)areAllRequiredFieldsFilled { - BOOL ret = [super areAllRequiredFieldsFilled]; - - // Check user login and pass fields - ret = (ret && self.userLoginTextField.text.length && self.passWordTextField.text.length); - return ret; -} - -- (void)setAuthType:(AuthenticationType)authType { - if (authType == AuthenticationTypeLogin) { - self.passWordTextField.returnKeyType = UIReturnKeyDone; - self.emailTextField.hidden = YES; - self.emailInfoLabel.hidden = YES; - } else { - self.passWordTextField.returnKeyType = UIReturnKeyNext; - self.emailTextField.hidden = NO; - self.emailInfoLabel.hidden = NO; - } - super.authType = authType; - - // Prefill text field - self.userLoginTextField.text = [[MatrixHandler sharedHandler] userLogin]; - self.passWordTextField.text = nil; -} - -- (void)dismissKeyboard { - [self.userLoginTextField resignFirstResponder]; - [self.passWordTextField resignFirstResponder]; - [self.emailTextField resignFirstResponder]; - - [super dismissKeyboard]; -} - -#pragma mark UITextField delegate - -- (void)textFieldDidEndEditing:(UITextField *)textField { - if (textField == self.userLoginTextField) { - [[MatrixHandler sharedHandler] setUserLogin:textField.text]; - } -} - -- (BOOL)textFieldShouldReturn:(UITextField*)textField { - if (textField.returnKeyType == UIReturnKeyDone) { - // "Done" key has been pressed - [textField resignFirstResponder]; - - if (self.delegate && [self.delegate respondsToSelector:@selector(authInputsDoneKeyHasBeenPressed:)]) { - // Launch authentication now - [self.delegate authInputsDoneKeyHasBeenPressed:self]; - } - } else { - //"Next" key has been pressed - if (textField == self.userLoginTextField) { - [self.passWordTextField becomeFirstResponder]; - } else if (textField == self.passWordTextField) { - [self.displayNameTextField becomeFirstResponder]; - } else if (textField == self.displayNameTextField) { - [self.emailTextField becomeFirstResponder]; - } - } - - return YES; -} -@end - -#pragma mark - AuthInputsEmailCodeBasedView - -@implementation AuthInputsEmailCodeBasedView - -- (CGFloat)actualHeight { - if (self.authType == AuthenticationTypeLogin) { - return self.displayNameTextField.frame.origin.y; - } - return super.actualHeight; -} - -- (BOOL)areAllRequiredFieldsFilled { - BOOL ret = [super areAllRequiredFieldsFilled]; - - // Check required fields //FIXME what are required fields in this authentication flow? - ret = (ret && self.userLoginTextField.text.length && self.emailAndTokenTextField.text.length); - return ret; -} - -- (void)setAuthType:(AuthenticationType)authType { - // Set initial layout - self.userLoginTextField.hidden = NO; - self.promptEmailTokenLabel.hidden = YES; - - if (authType == AuthenticationTypeLogin) { - self.emailAndTokenTextField.returnKeyType = UIReturnKeyDone; - } else { - self.emailAndTokenTextField.returnKeyType = UIReturnKeyNext; - } - - super.authType = authType; -} - -- (void)dismissKeyboard { - [self.userLoginTextField resignFirstResponder]; - [self.emailAndTokenTextField resignFirstResponder]; - - [super dismissKeyboard]; -} - -- (void)nextStep { - // Consider here the email token has been requested with success - [super nextStep]; - - self.userLoginTextField.hidden = YES; - self.promptEmailTokenLabel.hidden = NO; - self.emailAndTokenTextField.returnKeyType = UIReturnKeyDone; -} - -#pragma mark UITextField delegate - -- (void)textFieldDidEndEditing:(UITextField *)textField { - if (textField == self.userLoginTextField) { - [[MatrixHandler sharedHandler] setUserLogin:textField.text]; - } - // FIXME store user's email in matrixSDKHandler like userId -} - -- (BOOL)textFieldShouldReturn:(UITextField*)textField { - if (textField.returnKeyType == UIReturnKeyDone) { - // "Done" key has been pressed - [textField resignFirstResponder]; - - if (self.delegate && [self.delegate respondsToSelector:@selector(authInputsDoneKeyHasBeenPressed:)]) { - // Launch authentication now - [self.delegate authInputsDoneKeyHasBeenPressed:self]; - } - } else { - //"Next" key has been pressed - if (textField == self.userLoginTextField) { - [self.emailAndTokenTextField becomeFirstResponder]; - } else if (textField == self.emailAndTokenTextField) { - [self.displayNameTextField becomeFirstResponder]; - } - } - - return YES; -} - -@end \ No newline at end of file diff --git a/matrixConsole/View/MXCRegistrationWebView.h b/matrixConsole/View/MXCRegistrationWebView.h deleted file mode 100644 index 115a49ecf..000000000 --- a/matrixConsole/View/MXCRegistrationWebView.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright 2015 OpenMarket Ltd - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -#include - -@interface MXCRegistrationWebView : UIWebView - -/** - Open a registration fallback page into the webview. - - @param fallbackPage the fallback page hosted by a home server. - @param success the block called when the user has been successfully registered. - */ -- (void)openFallbackPage:(NSString*)fallbackPage success:(void (^)(MXCredentials *credentials))success; - -@end diff --git a/matrixConsole/View/MXCRegistrationWebView.m b/matrixConsole/View/MXCRegistrationWebView.m deleted file mode 100644 index b84ca6155..000000000 --- a/matrixConsole/View/MXCRegistrationWebView.m +++ /dev/null @@ -1,106 +0,0 @@ -/* - Copyright 2015 OpenMarket Ltd - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MXCRegistrationWebView.h" - -// Generic method to make a bridge between JS and the UIWebView -NSString *kMXCJavascriptSendObjectMessage = @"window.matrixRegistration.sendObjectMessage = function(parameters) { \ - var iframe = document.createElement('iframe'); \ - iframe.setAttribute('src', 'js:' + JSON.stringify(parameters)); \ - \ - document.documentElement.appendChild(iframe); \ - iframe.parentNode.removeChild(iframe); \ - iframe = null; \ -};"; - -// The function the fallback page calls when the registration is complete -NSString *kMXCJavascriptOnRegistered = @"window.matrixRegistration.onRegistered = function(homeserverUrl, userId, accessToken) { \ - matrixRegistration.sendObjectMessage({ \ - 'action': 'onRegistered', \ - 'homeServer': homeserverUrl, \ - 'userId': userId, \ - 'accessToken': accessToken \ - }); \ -};"; - -@interface MXCRegistrationWebView () { - // The block called when the registration is successful - void (^onSuccess)(MXCredentials *); - - // Activity indicator - UIActivityIndicatorView *activityIndicator; -} -@end - -@implementation MXCRegistrationWebView - -- (void)dealloc { - if (activityIndicator) { - [activityIndicator removeFromSuperview]; - activityIndicator = nil; - } -} - -- (void)openFallbackPage:(NSString *)fallbackPage success:(void (^)(MXCredentials *))success { - self.delegate = self; - onSuccess = success; - - // Add activity indicator - activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; - activityIndicator.center = self.center; - [self addSubview:activityIndicator]; - [activityIndicator startAnimating]; - - [self loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:fallbackPage]]]; -} - --(void)webViewDidFinishLoad:(UIWebView *)webView { - if (activityIndicator) { - [activityIndicator stopAnimating]; - [activityIndicator removeFromSuperview]; - activityIndicator = nil; - } - - [self stringByEvaluatingJavaScriptFromString:kMXCJavascriptSendObjectMessage]; - [self stringByEvaluatingJavaScriptFromString:kMXCJavascriptOnRegistered]; -} - -- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { - NSString *urlString = [[request URL] absoluteString]; - - if ([urlString hasPrefix:@"js:"]) { - // Listen only to scheme of the JS-UIWebView bridge - NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding]; - NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; - - NSError *error; - NSDictionary *parameters = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers - error:&error]; - - if (!error) { - if ([@"onRegistered" isEqualToString:parameters[@"action"]]) { - // Translate the JS registration event to MXCredentials - MXCredentials *credentials = [[MXCredentials alloc] initWithHomeServer:parameters[@"homeServer"] userId:parameters[@"userId"] accessToken:parameters[@"accessToken"]]; - // And inform the client - onSuccess(credentials); - } - } - return NO; - } - return YES; -} - -@end diff --git a/matrixConsole/ViewController/AuthenticationViewController.h b/matrixConsole/ViewController/AuthenticationViewController.h index 606f7c731..c15e1a746 100644 --- a/matrixConsole/ViewController/AuthenticationViewController.h +++ b/matrixConsole/ViewController/AuthenticationViewController.h @@ -16,9 +16,7 @@ #import -#import "AuthInputsView.h" - -@interface AuthenticationViewController : MXKViewController +@interface AuthenticationViewController : MXKAuthenticationViewController @end diff --git a/matrixConsole/ViewController/AuthenticationViewController.m b/matrixConsole/ViewController/AuthenticationViewController.m index 119d5aefc..7c5fa9b85 100644 --- a/matrixConsole/ViewController/AuthenticationViewController.m +++ b/matrixConsole/ViewController/AuthenticationViewController.m @@ -16,563 +16,37 @@ #import "AuthenticationViewController.h" -#import "MatrixHandler.h" -#import "AppDelegate.h" -#import "MXCRegistrationWebView.h" - #import "RageShakeManager.h" -@interface AuthenticationViewController () { - // Current request in progress - MXHTTPOperation *mxCurrentOperation; - - // Array of flows supported by the home server and implemented by the app (for the current auth type) - NSMutableArray *supportedFlows; - - // The current view in which auth inputs are displayed - AuthInputsView *currentAuthInputsView; - - // reference to any opened alert view - MXKAlert *alert; -} - -// Return true if the provided flow (kMXLoginFlowType) is supported by the application -+ (BOOL)isImplementedFlowType:(NSString*)flowType forAuthType:(AuthenticationType)authType; - -// The current authentication type -@property (nonatomic) AuthenticationType authType; -@property (nonatomic) MXLoginFlow *selectedFlow; - -@property (strong, nonatomic) IBOutlet UIScrollView *authenticationScrollView; -@property (weak, nonatomic) IBOutlet UIView *contentView; -@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewHeightConstraint; - -@property (weak, nonatomic) IBOutlet UILabel *createAccountLabel; - -@property (weak, nonatomic) IBOutlet UIView *authInputsContainerView; -@property (weak, nonatomic) IBOutlet NSLayoutConstraint *authInputContainerViewHeightConstraint; -@property (weak, nonatomic) IBOutlet AuthInputsPasswordBasedView *authInputsPasswordBasedView; -@property (weak, nonatomic) IBOutlet AuthInputsEmailCodeBasedView *authInputsEmailCodeBasedView; - -@property (weak, nonatomic) IBOutlet UITextField *homeServerTextField; -@property (weak, nonatomic) IBOutlet UILabel *homeServerInfoLabel; -@property (weak, nonatomic) IBOutlet UITextField *identityServerTextField; -@property (weak, nonatomic) IBOutlet UILabel *identityServerInfoLabel; - -@property (weak, nonatomic) IBOutlet UIButton *submitButton; -@property (weak, nonatomic) IBOutlet UIButton *authSwitchButton; - -@property (strong, nonatomic) IBOutlet UIActivityIndicatorView *authenticationActivityIndicator; -@property (weak, nonatomic) IBOutlet UILabel *noFlowLabel; -@property (weak, nonatomic) IBOutlet UIButton *retryButton; - -@property (weak, nonatomic) IBOutlet UIView *registrationFallbackContentView; -@property (weak, nonatomic) IBOutlet MXCRegistrationWebView *registrationFallbackWebView; -@property (weak, nonatomic) IBOutlet UIButton *cancelRegistrationFallbackButton; - -@end - @implementation AuthenticationViewController - (void)viewDidLoad { [super viewDidLoad]; - // Do any additional setup after loading the view, typically from a nib. - // Force contentView in full width - NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:self.contentView - attribute:NSLayoutAttributeLeading - relatedBy:0 - toItem:self.view - attribute:NSLayoutAttributeLeft - multiplier:1.0 - constant:0]; - [self.view addConstraint:leftConstraint]; - - NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:self.contentView - attribute:NSLayoutAttributeTrailing - relatedBy:0 - toItem:self.view - attribute:NSLayoutAttributeRight - multiplier:1.0 - constant:0]; - [self.view addConstraint:rightConstraint]; - - _authenticationScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth; - - _submitButton.enabled = NO; - _authSwitchButton.enabled = YES; - _authInputsPasswordBasedView.delegate = self; - _authInputsEmailCodeBasedView.delegate = self; - - supportedFlows = [NSMutableArray array]; - - _homeServerTextField.text = [[MatrixHandler sharedHandler] homeServerURL]; - _identityServerTextField.text = [[MatrixHandler sharedHandler] identityServerURL]; - - // Set rageShake handler + // Setup `MXKAuthenticationViewController` properties self.rageShakeManager = [RageShakeManager sharedManager]; + self.defaultHomeServerUrl = [[NSUserDefaults standardUserDefaults] objectForKey:@"homeserverurl"]; + self.defaultIdentityServerUrl = [[NSUserDefaults standardUserDefaults] objectForKey:@"identityserverurl"]; - // Set initial auth type - _authType = AuthenticationTypeLogin; + // The view controller dismiss itself on successful login. + self.delegate = self; } -- (void)dealloc { - supportedFlows = nil; - if (mxCurrentOperation){ - [mxCurrentOperation cancel]; - mxCurrentOperation = nil; +#pragma mark - MXKAuthenticationViewControllerDelegate + +- (void)authenticationViewController:(MXKAuthenticationViewController *)authenticationViewController didLogWithUserId:(NSString *)userId { + + // Report server url typed by the user as default url. + if (self.homeServerTextField.text.length) { + [[NSUserDefaults standardUserDefaults] setObject:self.homeServerTextField.text forKey:@"homeserverurl"]; } -} - -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} - -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - - // Update supported authentication flow - self.authType = _authType; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onKeyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onKeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; - - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onTextFieldChange:) name:UITextFieldTextDidChangeNotification object:nil]; -} - -- (void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - - [self dismissKeyboard]; - - // close any opened alert - if (alert) { - [alert dismiss:NO]; - alert = nil; + if (self.identityServerTextField.text.length) { + [[NSUserDefaults standardUserDefaults] setObject:self.identityServerTextField.text forKey:@"identityserverurl"]; } - [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNetworkingReachabilityDidChangeNotification object:nil]; + [[NSUserDefaults standardUserDefaults] synchronize]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil]; -} - -#pragma mark - - -+ (BOOL)isImplementedFlowType:(NSString*)flowType forAuthType:(AuthenticationType)authType { - if (authType == AuthenticationTypeLogin) { - if ([flowType isEqualToString:kMXLoginFlowTypePassword] - /*|| [flowType isEqualToString:kMXLoginFlowTypeEmailCode]*/) { - return YES; - } - } else { // AuthenticationTypeRegister - // No registration flow is supported yet - } - - return NO; -} - -- (void)setAuthType:(AuthenticationType)authType { - if (authType == AuthenticationTypeLogin) { - _createAccountLabel.hidden = YES; - [_submitButton setTitle:@"Login" forState:UIControlStateNormal]; - [_submitButton setTitle:@"Login" forState:UIControlStateHighlighted]; - [_authSwitchButton setTitle:@"Create account" forState:UIControlStateNormal]; - [_authSwitchButton setTitle:@"Create account" forState:UIControlStateHighlighted]; - } else { - _createAccountLabel.hidden = NO; - [_submitButton setTitle:@"Sign up" forState:UIControlStateNormal]; - [_submitButton setTitle:@"Sign up" forState:UIControlStateHighlighted]; - [_authSwitchButton setTitle:@"Back" forState:UIControlStateNormal]; - [_authSwitchButton setTitle:@"Back" forState:UIControlStateHighlighted]; - } - - _authType = authType; - - // Update supported authentication flow - [self refreshSupportedAuthFlow]; -} - -- (void)setSelectedFlow:(MXLoginFlow *)selectedFlow { - // Hide views which depend on auth flow - _submitButton.hidden = YES; - _authInputsPasswordBasedView.hidden = YES; - _authInputsEmailCodeBasedView.hidden = YES; - _noFlowLabel.hidden = YES; - _retryButton.hidden = YES; - currentAuthInputsView = nil; - - // Select the right auth inputs view - if ([selectedFlow.type isEqualToString:kMXLoginFlowTypePassword]) { - currentAuthInputsView = _authInputsPasswordBasedView; - } else if ([selectedFlow.type isEqualToString:kMXLoginFlowTypeEmailCode]) { - currentAuthInputsView = _authInputsEmailCodeBasedView; - } - - if (currentAuthInputsView) { - _submitButton.hidden = NO; - currentAuthInputsView.hidden = NO; - currentAuthInputsView.authType = _authType; - _authInputContainerViewHeightConstraint.constant = currentAuthInputsView.actualHeight; - } else { - // No input fields are displayed - _authInputContainerViewHeightConstraint.constant = 80; - } - - [self.view layoutIfNeeded]; - - // Refresh content view height - _contentViewHeightConstraint.constant = _authSwitchButton.frame.origin.y + _authSwitchButton.frame.size.height + 15; - - _selectedFlow = selectedFlow; -} - -- (void)setUserInteractionEnabled:(BOOL)isEnabled { - _submitButton.enabled = (isEnabled && currentAuthInputsView.areAllRequiredFieldsFilled && _homeServerTextField.text.length); - _authSwitchButton.enabled = isEnabled; - - _homeServerTextField.enabled = isEnabled; - _identityServerTextField.enabled = isEnabled; -} - -- (void)refreshSupportedAuthFlow { - MatrixHandler *mxHandler = [MatrixHandler sharedHandler]; - - // Remove reachability observer - [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNetworkingReachabilityDidChangeNotification object:nil]; - - // Cancel potential request in progress - [mxCurrentOperation cancel]; - mxCurrentOperation = nil; - - [_authenticationActivityIndicator startAnimating]; - self.selectedFlow = nil; - if (_authType == AuthenticationTypeLogin) { - mxCurrentOperation = [mxHandler.mxRestClient getLoginFlow:^(NSArray *flows) { - [self handleHomeServerFlows:flows]; - } failure:^(NSError *error) { - NSLog(@"[AuthenticationVC] Failed to get Login flows: %@", error); - [self onFailureDuringMXOperation:error]; - }]; - } else { -// mxCurrentOperation = [mxHandler.mxRestClient getRegisterFlow:^(NSArray *flows) { -// [self handleHomeServerFlows:flows]; -// } failure:^(NSError *error) { -// NSLog(@"[AuthenticationVC] Failed to get Register flows: %@", error); -// [self onFailureDuringMXOperation:error]; -// }]; - - // Currently no registration flow are supported, we switch directly to the fallback page - [self showRegistrationFallBackView:[mxHandler.mxRestClient registerFallback]]; - } -} - -- (void)handleHomeServerFlows:(NSArray *)flows { - [_authenticationActivityIndicator stopAnimating]; - - [supportedFlows removeAllObjects]; - for (MXLoginFlow* flow in flows) { - if ([AuthenticationViewController isImplementedFlowType:flow.type forAuthType:_authType]) { - // Check here all stages - BOOL isSupported = YES; - if (flow.stages.count) { - for (NSString *stage in flow.stages) { - if ([AuthenticationViewController isImplementedFlowType:stage forAuthType:_authType] == NO) { - isSupported = NO; - break; - } - } - } - - if (isSupported) { - [supportedFlows addObject:flow]; - } - } - } - - if (supportedFlows.count) { - // FIXME display supported flows - // Currently we select the first one - self.selectedFlow = [supportedFlows firstObject]; - } - - if (!_selectedFlow) { - // Notify user that no flow is supported - if (_authType == AuthenticationTypeLogin) { - _noFlowLabel.text = @"Currently we do not support Login flows defined by this Home Server."; - } else { - _noFlowLabel.text = @"Registration is not currently supported."; - } - NSLog(@"[AuthenticationVC] Warning: %@", _noFlowLabel.text); - - _noFlowLabel.hidden = NO; - _retryButton.hidden = NO; - } -} - -- (void)onFailureDuringMXOperation:(NSError*)error { - mxCurrentOperation = nil; - - if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == kCFURLErrorCancelled) { - // Ignore this error - return; - } - - [_authenticationActivityIndicator stopAnimating]; - - // Alert user - NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey]; - if (!title) - { - title = @"Error"; - } - NSString *msg = [error.userInfo valueForKey:NSLocalizedDescriptionKey]; - - alert = [[MXKAlert alloc] initWithTitle:title message:msg style:MXKAlertStyleAlert]; - alert.cancelButtonIndex = [alert addActionWithTitle:@"Dismiss" style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert) {}]; - [alert showInViewController:self]; - - // Display failure reason - _noFlowLabel.hidden = NO; - _noFlowLabel.text = [error.userInfo valueForKey:NSLocalizedDescriptionKey]; - if (!_noFlowLabel.text.length) { - _noFlowLabel.text = @"We failed to retrieve authentication information from this Home Server"; - } - _retryButton.hidden = NO; - - // Handle specific error code here - if ([error.domain isEqualToString:NSURLErrorDomain]) { - // Check network reachability - if (error.code == NSURLErrorNotConnectedToInternet) { - // Add reachability observer in order to launch a new request when network will be available - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReachabilityStatusChange:) name:AFNetworkingReachabilityDidChangeNotification object:nil]; - } else if (error.code == kCFURLErrorTimedOut) { - // Send a new request in 2 sec - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self refreshSupportedAuthFlow]; - }); - } - } -} - -- (void)onReachabilityStatusChange:(NSNotification *)notif { - AFNetworkReachabilityManager *reachabilityManager = [AFNetworkReachabilityManager sharedManager]; - AFNetworkReachabilityStatus status = reachabilityManager.networkReachabilityStatus; - - if (status == AFNetworkReachabilityStatusReachableViaWiFi || status == AFNetworkReachabilityStatusReachableViaWWAN) { - dispatch_async(dispatch_get_main_queue(), ^{ - [self refreshSupportedAuthFlow]; - }); - } else if (status == AFNetworkReachabilityStatusNotReachable) { - _noFlowLabel.text = @"Please check your network connectivity"; - } -} - -- (IBAction)onButtonPressed:(id)sender { - [self dismissKeyboard]; - - if (sender == _submitButton) { - MatrixHandler *matrix = [MatrixHandler sharedHandler]; - if (matrix.mxRestClient) { - // Disable user interaction to prevent multiple requests - [self setUserInteractionEnabled:NO]; - [_authenticationActivityIndicator startAnimating]; - - if (_authType == AuthenticationTypeLogin) { - if ([_selectedFlow.type isEqualToString:kMXLoginFlowTypePassword]) { - [matrix.mxRestClient loginWithUser:matrix.userLogin andPassword:_authInputsPasswordBasedView.passWordTextField.text - success:^(MXCredentials *credentials){ - [_authenticationActivityIndicator stopAnimating]; - - // Report credentials - [matrix setUserId:credentials.userId]; - [matrix setAccessToken:credentials.accessToken]; - // Extract homeServer name from userId - NSArray *components = [credentials.userId componentsSeparatedByString:@":"]; - if (components.count == 2) { - [matrix setHomeServer:[components lastObject]]; - } else { - NSLog(@"[AuthenticationVC] Warning: the userId is not correctly formatted: %@", credentials.userId); - } - - [self dismissViewControllerAnimated:YES completion:nil]; - } - failure:^(NSError *error){ - [self onFailureDuringAuthRequest:error]; - }]; - } else { - // FIXME - [self onFailureDuringAuthRequest:[NSError errorWithDomain:nil code:0 userInfo:@{@"error": @"Not supported yet"}]]; - } - } else { - // FIXME - [self onFailureDuringAuthRequest:[NSError errorWithDomain:nil code:0 userInfo:@{@"error": @"Not supported yet"}]]; - } - } - } else if (sender == _authSwitchButton){ - if (_authType == AuthenticationTypeLogin) { - self.authType = AuthenticationTypeRegister; - } else { - self.authType = AuthenticationTypeLogin; - } - } else if (sender == _retryButton) { - [self refreshSupportedAuthFlow]; - } else if (sender == _cancelRegistrationFallbackButton) { - // Hide fallback webview - [self hideRegistrationFallbackView]; - self.authType = AuthenticationTypeLogin; - } -} - -- (void)onFailureDuringAuthRequest:(NSError *)error { - [_authenticationActivityIndicator stopAnimating]; - [self setUserInteractionEnabled:YES]; - - NSLog(@"[AuthenticationVC] Auth request failed: %@", error); - - // translate the error code to a human message - NSString* message = error.localizedDescription; - NSDictionary* dict = error.userInfo; - - // detect if it is a Matrix SDK issue - if (dict) { - NSString* localizedError = [dict valueForKey:@"error"]; - NSString* errCode = [dict valueForKey:@"errcode"]; - - if (errCode) { - if ([errCode isEqualToString:@"M_FORBIDDEN"]) { - message = @"Invalid username/password"; - } else if (localizedError.length > 0) { - message = localizedError; - } else if ([errCode isEqualToString:@"M_UNKNOWN_TOKEN"]) { - message = @"The access token specified was not recognised"; - } else if ([errCode isEqualToString:@"M_BAD_JSON"]) { - message = @"Malformed JSON"; - } else if ([errCode isEqualToString:@"M_NOT_JSON"]) { - message = @"Did not contain valid JSON"; - } else if ([errCode isEqualToString:@"M_LIMIT_EXCEEDED"]) { - message = @"Too many requests have been sent"; - } else if ([errCode isEqualToString:@"M_USER_IN_USE"]) { - message = @"This user name is already used"; - } else if ([errCode isEqualToString:@"M_LOGIN_EMAIL_URL_NOT_YET"]) { - message = @"The email link which has not been clicked yet"; - } else { - message = errCode; - } - } - } - - //Alert user - alert = [[MXKAlert alloc] initWithTitle:@"Login Failed" message:message style:MXKAlertStyleAlert]; - [alert addActionWithTitle:@"Dismiss" style:MXKAlertActionStyleCancel handler:^(MXKAlert *alert) {}]; - [alert showInViewController:self]; -} - -#pragma mark - Keyboard handling - -- (void)onKeyboardWillShow:(NSNotification *)notif { - NSValue *rectVal = notif.userInfo[UIKeyboardFrameEndUserInfoKey]; - CGRect endRect = rectVal.CGRectValue; - - UIEdgeInsets insets = self.authenticationScrollView.contentInset; - // Handle portrait/landscape mode - insets.bottom = (endRect.origin.y == 0) ? endRect.size.width : endRect.size.height; - self.authenticationScrollView.contentInset = insets; -} - -- (void)onKeyboardWillHide:(NSNotification *)notif { - UIEdgeInsets insets = self.authenticationScrollView.contentInset; - insets.bottom = 0; - self.authenticationScrollView.contentInset = insets; -} - -- (void)dismissKeyboard { - // Hide the keyboard - [currentAuthInputsView dismissKeyboard]; - [_homeServerTextField resignFirstResponder]; - [_identityServerTextField resignFirstResponder]; -} - -#pragma mark - UITextField delegate - -- (void)onTextFieldChange:(NSNotification *)notif { - NSString *homeServerURL = _homeServerTextField.text; - - if (currentAuthInputsView.areAllRequiredFieldsFilled && homeServerURL.length) { - _submitButton.enabled = YES; - } else { - _submitButton.enabled = NO; - } -} - -- (void)textFieldDidEndEditing:(UITextField *)textField { - MatrixHandler *mxHandler = [MatrixHandler sharedHandler]; - if (textField == _homeServerTextField) { - if (![[mxHandler homeServerURL] isEqualToString:textField.text]) { - [mxHandler setHomeServerURL:textField.text]; - if (!textField.text.length) { - // Force refresh with default value - textField.text = [mxHandler homeServerURL]; - } - // Refresh UI - [self refreshSupportedAuthFlow]; - } - } - else if (textField == _identityServerTextField) { - [mxHandler setIdentityServerURL:textField.text]; - if (!textField.text.length) { - // Force refresh with default value - textField.text = [mxHandler identityServerURL]; - } - } -} - -- (BOOL)textFieldShouldReturn:(UITextField*)textField { - if (textField.returnKeyType == UIReturnKeyDone) { - // "Done" key has been pressed - [textField resignFirstResponder]; - } - return YES; -} - -#pragma mark - AuthInputsViewDelegate delegate - -- (void)authInputsDoneKeyHasBeenPressed:(AuthInputsView *)authInputsView { - if (_submitButton.isEnabled) { - // Launch authentication now - [self onButtonPressed:_submitButton]; - } -} - -#pragma mark - Registration Fallback - -- (void)showRegistrationFallBackView:(NSString*)fallbackPage { - _authenticationScrollView.hidden = YES; - _registrationFallbackContentView.hidden = NO; - - [_registrationFallbackWebView openFallbackPage:fallbackPage success:^(MXCredentials *credentials) { - // Report credentials - MatrixHandler *mxHandler = [MatrixHandler sharedHandler]; - [mxHandler setUserId:credentials.userId]; - [mxHandler setAccessToken:credentials.accessToken]; - // Extract homeServer name from userId - NSArray *components = [credentials.userId componentsSeparatedByString:@":"]; - if (components.count == 2) { - [mxHandler setHomeServer:[components lastObject]]; - } else { - NSLog(@"[AuthenticationVC] Warning: the userId is not correctly formatted: %@", credentials.userId); - } - - [self dismissViewControllerAnimated:YES completion:nil]; - }]; -} - -- (void)hideRegistrationFallbackView { - [_registrationFallbackWebView stopLoading]; - _authenticationScrollView.hidden = NO; - _registrationFallbackContentView.hidden = YES; + // Dismiss on successful login + [self dismissViewControllerAnimated:YES completion:nil]; } @end diff --git a/matrixConsole/ViewController/MasterTabBarController.m b/matrixConsole/ViewController/MasterTabBarController.m index 83698af0d..1e084baf6 100644 --- a/matrixConsole/ViewController/MasterTabBarController.m +++ b/matrixConsole/ViewController/MasterTabBarController.m @@ -108,7 +108,7 @@ [super viewDidAppear:animated]; // Check whether we're not logged in - if (![MatrixHandler sharedHandler].accessToken) { + if (![MXKAccountManager sharedManager].accounts.count) { [self showAuthenticationScreen]; } } @@ -215,7 +215,16 @@ } - (void)setVisibleRoomId:(NSString *)aVisibleRoomId { - [[MatrixHandler sharedHandler] restoreInAppNotificationsForRoomId:aVisibleRoomId]; + + // Presently only the first account is used + // TODO GFO: handle multi-session + MXKAccount *account = [[MXKAccountManager sharedManager].accounts firstObject]; + if (account) { + // Enable inApp notification for this room + [account updateNotificationListenerForRoomId:aVisibleRoomId ignore:NO]; + } + + _visibleRoomId = aVisibleRoomId; } diff --git a/matrixConsole/ViewController/RoomViewController.m b/matrixConsole/ViewController/RoomViewController.m index b625bef5b..89480592f 100644 --- a/matrixConsole/ViewController/RoomViewController.m +++ b/matrixConsole/ViewController/RoomViewController.m @@ -230,7 +230,7 @@ if (textField == _roomTitleView.displayNameTextField) { // Check whether the user has enough power to rename the room MXRoomPowerLevels *powerLevels = [self.roomDataSource.room.state powerLevels]; - NSUInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:[MatrixHandler sharedHandler].userId]; + NSUInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxSession.myUser.userId]; if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomName]) { // Only the room name is edited here, update the text field with the room name textField.text = self.roomDataSource.room.state.name; @@ -253,7 +253,7 @@ } else if (textField == _roomTitleView.topicTextField) { // Check whether the user has enough power to edit room topic MXRoomPowerLevels *powerLevels = [self.roomDataSource.room.state powerLevels]; - NSUInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:[MatrixHandler sharedHandler].userId]; + NSUInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mxSession.myUser.userId]; if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomTopic]) { textField.backgroundColor = [UIColor whiteColor]; [self.roomTitleView stopTopicAnimation]; diff --git a/matrixConsole/ViewController/SettingsViewController.m b/matrixConsole/ViewController/SettingsViewController.m index 4e2e0eff3..4c0f6e485 100644 --- a/matrixConsole/ViewController/SettingsViewController.m +++ b/matrixConsole/ViewController/SettingsViewController.m @@ -1057,7 +1057,7 @@ NSString* const kCommandsDescriptionText = @"The following commands are availabl // tap on clear application cache if ((indexPath.section == SETTINGS_SECTION_ROOMS_INDEX) && (indexPath.row == SETTINGS_SECTION_ROOMS_CLEAR_CACHE_INDEX)) { // clear caches - [[MatrixHandler sharedHandler] reload:YES]; + [[AppDelegate theDelegate] reloadMatrixSessions:YES]; } else if (indexPath.section == SETTINGS_SECTION_CONTACTS_INDEX) {