Update Console by using MXKAuthenticationViewController and MXKAccount classes.

This commit is contained in:
giomfo 2015-04-30 16:19:12 +02:00
parent b3f3b785f0
commit 216f7d1cf9
17 changed files with 274 additions and 2003 deletions

View file

@ -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 = "<group>"; };
3198D9E21A68338B00556695 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
32501A991A9B978400C5938F /* MXCRegistrationWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCRegistrationWebView.h; sourceTree = "<group>"; };
32501A9A1A9B978400C5938F /* MXCRegistrationWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCRegistrationWebView.m; sourceTree = "<group>"; };
710CC3BE1A6E9F14006EE973 /* matrixUser.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = matrixUser.png; sourceTree = "<group>"; };
710CC3C01A70F28F006EE973 /* MXCContactField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCContactField.h; sourceTree = "<group>"; };
710CC3C11A70F28F006EE973 /* MXCContactField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCContactField.m; sourceTree = "<group>"; };
@ -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 = "<group>"; };
F08E67931A77965A00AABD4C /* MXC3PID.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXC3PID.m; sourceTree = "<group>"; };
F0AC79311A8394510056D042 /* AuthInputsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AuthInputsView.h; sourceTree = "<group>"; };
F0AC79321A8394510056D042 /* AuthInputsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AuthInputsView.m; sourceTree = "<group>"; };
F0ADEFF91AD7D2B3008A4F21 /* RoomMembersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RoomMembersViewController.h; sourceTree = "<group>"; };
F0ADEFFA1AD7D2B3008A4F21 /* RoomMembersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RoomMembersViewController.m; sourceTree = "<group>"; };
F0CEA5AC19E6895E00E47915 /* logoHighRes.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logoHighRes.png; sourceTree = "<group>"; };
@ -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;
};

View file

@ -13,10 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
#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

View file

@ -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;

View file

@ -17,44 +17,8 @@
#import "MatrixHandler.h"
#import "AppDelegate.h"
#import "MXFileStore.h"
#import "MXTools.h"
#import "AFNetworkReachabilityManager.h"
#import <AudioToolbox/AudioToolbox.h>
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

View file

@ -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];

View file

@ -38,6 +38,8 @@
- (void)registerUserNotificationSettings;
- (void)reloadMatrixSessions:(BOOL)clearCache;
- (void)logout;
- (MXKAlert*)showErrorAsAlert:(NSError*)error;

View file

@ -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 <AudioToolbox/AudioToolbox.h>
#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<MXStore> 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 {

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7531" systemVersion="14D131" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="GsA-m1-kGB">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6751" systemVersion="14D131" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="GsA-m1-kGB">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7520"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6736"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
@ -587,433 +587,8 @@
<view key="view" contentMode="scaleToFill" id="wIi-Yi-2pi">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="udG-Mx-F4c">
<rect key="frame" x="0.0" y="20" width="600" height="580"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="d6h-O8-aBs" userLabel="Content View">
<rect key="frame" x="0.0" y="0.0" width="600" height="640"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="logoHighRes.png" translatesAutoresizingMaskIntoConstraints="NO" id="9oD-IQ-d8J">
<rect key="frame" x="180" y="33" width="240" height="102"/>
<constraints>
<constraint firstAttribute="width" constant="240" id="YfR-TZ-HGu"/>
<constraint firstAttribute="height" constant="102" id="qiX-ir-FRf"/>
</constraints>
</imageView>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Create account:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pvw-BQ-kaV">
<rect key="frame" x="88" y="147" width="110" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sQb-Oi-ER6" userLabel="AuthInputsContainerView">
<rect key="frame" x="150" y="170" width="300" height="180"/>
<subviews>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="O8N-hu-Ewh" userLabel="AuthInputsPasswordBasedView" customClass="AuthInputsPasswordBasedView">
<rect key="frame" x="0.0" y="0.0" width="300" height="179"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Matrix ID (e.g. @bob:matrix.org or bob)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="DkI-2d-TR5">
<rect key="frame" x="14" y="8" width="272" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="WJU-Hb-iFm"/>
<constraint firstAttribute="width" constant="272" id="gzb-uu-iH0"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" returnKeyType="next"/>
<connections>
<outlet property="delegate" destination="O8N-hu-Ewh" id="xI6-ue-Bbw"/>
</connections>
</textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Password" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="gDT-bj-MHE">
<rect key="frame" x="14" y="46" width="272" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="272" id="awG-2v-nb2"/>
<constraint firstAttribute="height" constant="30" id="wHP-r9-WAA"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done" secureTextEntry="YES"/>
<connections>
<outlet property="delegate" destination="O8N-hu-Ewh" id="enP-M1-s27"/>
</connections>
</textField>
<textField hidden="YES" opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Display name (e.g. Bob Obson)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Q4X-yt-15e" userLabel="DisplayName">
<rect key="frame" x="14" y="84" width="272" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="272" id="A1t-Ov-vXU"/>
<constraint firstAttribute="height" constant="30" id="RLA-my-oRJ"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="next" secureTextEntry="YES"/>
<connections>
<outlet property="delegate" destination="O8N-hu-Ewh" id="zHD-oq-zzj"/>
</connections>
</textField>
<textField hidden="YES" opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Email address (optional)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="hnv-Gw-Cnj" userLabel="Email">
<rect key="frame" x="14" y="126" width="272" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="IMW-ae-dUp"/>
<constraint firstAttribute="width" constant="272" id="hKn-H5-T9d"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done" secureTextEntry="YES"/>
<connections>
<outlet property="delegate" destination="O8N-hu-Ewh" id="F7K-1W-Nnc"/>
</connections>
</textField>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="9ig-1b-AUZ" userLabel="EmailInfoLabel">
<rect key="frame" x="14" y="156" width="272" height="15"/>
<string key="text">Specify an email address lets other users find you on Matrix more easily, and will give you a way to reset your password in the future.</string>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="9ig-1b-AUZ" secondAttribute="bottom" constant="8" id="7NI-xg-RWo"/>
<constraint firstItem="gDT-bj-MHE" firstAttribute="top" secondItem="DkI-2d-TR5" secondAttribute="bottom" constant="8" id="DxY-3A-IYe"/>
<constraint firstAttribute="centerX" secondItem="hnv-Gw-Cnj" secondAttribute="centerX" id="Ew9-Ir-5ZG"/>
<constraint firstAttribute="centerX" secondItem="Q4X-yt-15e" secondAttribute="centerX" id="Fd9-Vj-hlF"/>
<constraint firstItem="hnv-Gw-Cnj" firstAttribute="top" secondItem="Q4X-yt-15e" secondAttribute="bottom" constant="12" id="MWH-Pv-fFJ"/>
<constraint firstItem="9ig-1b-AUZ" firstAttribute="leading" secondItem="O8N-hu-Ewh" secondAttribute="leading" constant="14" id="OYt-bj-cVD"/>
<constraint firstAttribute="centerX" secondItem="gDT-bj-MHE" secondAttribute="centerX" id="PVZ-Fq-dKH"/>
<constraint firstAttribute="trailing" secondItem="9ig-1b-AUZ" secondAttribute="trailing" constant="14" id="S77-1a-wps"/>
<constraint firstItem="9ig-1b-AUZ" firstAttribute="top" secondItem="hnv-Gw-Cnj" secondAttribute="bottom" id="Soy-zj-uRP"/>
<constraint firstItem="DkI-2d-TR5" firstAttribute="top" secondItem="O8N-hu-Ewh" secondAttribute="top" constant="8" id="VWB-qs-WRa"/>
<constraint firstAttribute="centerX" secondItem="9ig-1b-AUZ" secondAttribute="centerX" id="dqb-WC-1x4"/>
<constraint firstItem="Q4X-yt-15e" firstAttribute="top" secondItem="gDT-bj-MHE" secondAttribute="bottom" constant="8" id="fH8-wa-qMI"/>
<constraint firstAttribute="width" constant="300" id="lGG-Df-1bn"/>
<constraint firstAttribute="centerX" secondItem="DkI-2d-TR5" secondAttribute="centerX" id="sKP-5W-sQk"/>
</constraints>
<connections>
<outlet property="displayNameTextField" destination="Q4X-yt-15e" id="WdS-ab-6Bv"/>
<outlet property="emailInfoLabel" destination="9ig-1b-AUZ" id="dK0-5x-JgB"/>
<outlet property="emailTextField" destination="hnv-Gw-Cnj" id="hsS-Rv-43G"/>
<outlet property="passWordTextField" destination="gDT-bj-MHE" id="cqv-gD-CQe"/>
<outlet property="userLoginTextField" destination="DkI-2d-TR5" id="1a2-ak-xgB"/>
</connections>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="G6Y-YQ-PvU" userLabel="AuthInputsEmailCodeBasedView" customClass="AuthInputsEmailCodeBasedView">
<rect key="frame" x="0.0" y="0.0" width="300" height="122"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Matrix ID (e.g. @bob:matrix.org or bob)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="e5U-3R-SLf">
<rect key="frame" x="14" y="8" width="272" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="dAe-Fy-Ro8"/>
<constraint firstAttribute="width" constant="272" id="vJi-7m-V1p"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" returnKeyType="next"/>
<connections>
<outlet property="delegate" destination="G6Y-YQ-PvU" id="SYJ-uu-LGE"/>
</connections>
</textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Email address" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="E10-8t-uAc" userLabel="Email-Token">
<rect key="frame" x="14" y="46" width="272" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="aSd-cp-L7s"/>
<constraint firstAttribute="width" constant="272" id="szQ-eZ-pTJ"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done" secureTextEntry="YES"/>
<connections>
<outlet property="delegate" destination="G6Y-YQ-PvU" id="LKy-hf-UiJ"/>
</connections>
</textField>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Please enter your email validation token:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EjR-UX-T9v">
<rect key="frame" x="20" y="8" width="261" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<textField hidden="YES" opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Display name (e.g. Bob Obson)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="6Pu-F7-Xd3" userLabel="DisplayName">
<rect key="frame" x="14" y="84" width="272" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="272" id="2UG-Lc-4TW"/>
<constraint firstAttribute="height" constant="30" id="n5C-7V-kJk"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done" secureTextEntry="YES"/>
<connections>
<outlet property="delegate" destination="G6Y-YQ-PvU" id="sdq-n3-nd8"/>
</connections>
</textField>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="centerX" secondItem="e5U-3R-SLf" secondAttribute="centerX" id="7C3-hj-pCD"/>
<constraint firstItem="6Pu-F7-Xd3" firstAttribute="top" secondItem="E10-8t-uAc" secondAttribute="bottom" constant="8" id="Cu8-aC-0Ri"/>
<constraint firstAttribute="centerX" secondItem="EjR-UX-T9v" secondAttribute="centerX" id="Utq-C1-03Y"/>
<constraint firstItem="E10-8t-uAc" firstAttribute="top" secondItem="e5U-3R-SLf" secondAttribute="bottom" constant="8" id="VT1-qc-FBw"/>
<constraint firstAttribute="centerX" secondItem="E10-8t-uAc" secondAttribute="centerX" id="XCM-Eo-2h5"/>
<constraint firstAttribute="centerX" secondItem="6Pu-F7-Xd3" secondAttribute="centerX" id="bHU-Aa-fRf"/>
<constraint firstAttribute="bottom" secondItem="6Pu-F7-Xd3" secondAttribute="bottom" constant="8" id="dmV-e8-Ak8"/>
<constraint firstItem="E10-8t-uAc" firstAttribute="top" secondItem="EjR-UX-T9v" secondAttribute="bottom" constant="8" id="gwg-Ln-72n"/>
<constraint firstItem="EjR-UX-T9v" firstAttribute="top" secondItem="G6Y-YQ-PvU" secondAttribute="top" constant="8" id="ktN-en-LeS"/>
<constraint firstItem="e5U-3R-SLf" firstAttribute="top" secondItem="G6Y-YQ-PvU" secondAttribute="top" constant="8" id="pVD-lt-GF6"/>
<constraint firstAttribute="width" constant="300" id="uor-7L-vf9"/>
</constraints>
<connections>
<outlet property="displayNameTextField" destination="6Pu-F7-Xd3" id="dz7-PB-74N"/>
<outlet property="emailAndTokenTextField" destination="E10-8t-uAc" id="vWQ-ED-DWS"/>
<outlet property="promptEmailTokenLabel" destination="EjR-UX-T9v" id="4AO-pW-pp2"/>
<outlet property="userLoginTextField" destination="e5U-3R-SLf" id="ywh-V7-6GX"/>
</connections>
</view>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="J51-Cj-6mb">
<rect key="frame" x="140" y="80" width="20" height="20"/>
</activityIndicatorView>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Currently we do not support authentication flows defined by this Home Server" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="eg7-eh-lZv" userLabel="noFlowLabel">
<rect key="frame" x="9" y="8" width="283" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="70b-I3-Cuk" userLabel="retryButton">
<rect key="frame" x="128" y="30" width="45" height="30"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="45" id="4zv-VS-Uz6"/>
<constraint firstAttribute="height" constant="30" id="eae-iR-11I"/>
</constraints>
<state key="normal" title="Retry">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="ZlD-EU-ncw" eventType="touchUpInside" id="VMc-vT-H6d"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="centerX" secondItem="70b-I3-Cuk" secondAttribute="centerX" id="2G2-j8-QdO"/>
<constraint firstAttribute="centerY" secondItem="J51-Cj-6mb" secondAttribute="centerY" id="6R3-QL-izy"/>
<constraint firstItem="G6Y-YQ-PvU" firstAttribute="top" secondItem="sQb-Oi-ER6" secondAttribute="top" id="7zC-MV-yX9"/>
<constraint firstAttribute="centerX" secondItem="G6Y-YQ-PvU" secondAttribute="centerX" id="Eft-0W-r1l"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="eg7-eh-lZv" secondAttribute="trailing" constant="8" id="Ffa-SN-6DV"/>
<constraint firstItem="eg7-eh-lZv" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="sQb-Oi-ER6" secondAttribute="leading" constant="8" id="NQi-Wl-keg"/>
<constraint firstItem="O8N-hu-Ewh" firstAttribute="top" secondItem="sQb-Oi-ER6" secondAttribute="top" id="Qhd-nd-xQH"/>
<constraint firstAttribute="centerX" secondItem="eg7-eh-lZv" secondAttribute="centerX" id="Rpn-e0-6X2"/>
<constraint firstAttribute="centerX" secondItem="O8N-hu-Ewh" secondAttribute="centerX" id="eAO-Dq-dp1"/>
<constraint firstItem="eg7-eh-lZv" firstAttribute="top" secondItem="sQb-Oi-ER6" secondAttribute="top" constant="8" id="fqE-nJ-QQK"/>
<constraint firstAttribute="height" constant="180" id="gDV-QE-CXd"/>
<constraint firstAttribute="centerX" secondItem="J51-Cj-6mb" secondAttribute="centerX" id="jiI-Qk-jML"/>
<constraint firstItem="70b-I3-Cuk" firstAttribute="top" secondItem="eg7-eh-lZv" secondAttribute="bottom" constant="5" id="pGi-gI-AOX"/>
<constraint firstAttribute="width" constant="300" id="xde-gy-DHI"/>
</constraints>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fnl-LF-rEL" userLabel="SubmitBtn">
<rect key="frame" x="273" y="360" width="55" height="33"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<state key="normal" title="Submit">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="ZlD-EU-ncw" eventType="touchUpInside" id="ER9-1w-3CL"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5a8-4J-rqy" userLabel="HomeServerView">
<rect key="frame" x="124" y="408" width="352" height="30"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Home Server:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TW9-CJ-pH0">
<rect key="frame" x="0.0" y="6" width="96" height="18"/>
<constraints>
<constraint firstAttribute="width" constant="96" id="TVu-Hn-mwn"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="URL (e.g. https://matrix.org)" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="iBz-4w-0cv">
<rect key="frame" x="102" y="0.0" width="250" height="30"/>
<constraints>
<constraint firstAttribute="width" priority="750" constant="250" id="3Jy-CR-r4b"/>
<constraint firstAttribute="height" constant="30" id="waN-MT-6d2"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done"/>
<connections>
<outlet property="delegate" destination="ZlD-EU-ncw" id="fJ9-39-2ag"/>
</connections>
</textField>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="iBz-4w-0cv" firstAttribute="leading" secondItem="TW9-CJ-pH0" secondAttribute="trailing" constant="6" id="41G-OL-N3Z"/>
<constraint firstItem="TW9-CJ-pH0" firstAttribute="top" secondItem="5a8-4J-rqy" secondAttribute="top" constant="6" id="IzW-2x-BS6"/>
<constraint firstAttribute="bottom" secondItem="iBz-4w-0cv" secondAttribute="bottom" id="TXJ-ht-9MJ"/>
<constraint firstAttribute="bottom" secondItem="TW9-CJ-pH0" secondAttribute="bottom" constant="6" id="YTz-Xx-gKA"/>
<constraint firstAttribute="trailing" secondItem="iBz-4w-0cv" secondAttribute="trailing" id="YUC-u1-UJC"/>
<constraint firstItem="iBz-4w-0cv" firstAttribute="top" secondItem="5a8-4J-rqy" secondAttribute="top" id="jpl-f7-eHN"/>
<constraint firstItem="TW9-CJ-pH0" firstAttribute="leading" secondItem="5a8-4J-rqy" secondAttribute="leading" id="mXd-CX-Ehu"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Your home server stores all your conservation and account data." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="LKF-ef-JTA" userLabel="HomeServerInfoLabel">
<rect key="frame" x="107" y="438" width="387" height="16"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="F4V-zC-19g" userLabel="IdentityServerView">
<rect key="frame" x="119" y="462" width="363" height="30"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Identity Server:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MHk-DJ-BFj">
<rect key="frame" x="0.0" y="6" width="107" height="18"/>
<constraints>
<constraint firstAttribute="width" constant="107" id="JcY-aa-68S"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="URL (e.g. https://matrix.org)" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="yfy-fB-piy">
<rect key="frame" x="113" y="0.0" width="250" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="1F5-e4-bJG"/>
<constraint firstAttribute="width" priority="750" constant="250" id="l5u-xy-hOo"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done"/>
<connections>
<outlet property="delegate" destination="ZlD-EU-ncw" id="7a0-Jw-Wg8"/>
</connections>
</textField>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="MHk-DJ-BFj" secondAttribute="bottom" constant="6" id="FAd-Dl-1I7"/>
<constraint firstAttribute="trailing" secondItem="yfy-fB-piy" secondAttribute="trailing" id="Vcv-Uh-OAO"/>
<constraint firstItem="yfy-fB-piy" firstAttribute="top" secondItem="F4V-zC-19g" secondAttribute="top" id="ek8-U6-AJf"/>
<constraint firstItem="yfy-fB-piy" firstAttribute="leading" secondItem="MHk-DJ-BFj" secondAttribute="trailing" constant="6" id="qQD-zl-EVh"/>
<constraint firstAttribute="bottom" secondItem="yfy-fB-piy" secondAttribute="bottom" id="sVj-ei-7Yd"/>
<constraint firstItem="MHk-DJ-BFj" firstAttribute="leading" secondItem="F4V-zC-19g" secondAttribute="leading" id="tvn-1T-DLz"/>
<constraint firstItem="MHk-DJ-BFj" firstAttribute="top" secondItem="F4V-zC-19g" secondAttribute="top" constant="6" id="xHO-pQ-fCR"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="0ot-Cn-Okj" userLabel="IdentityServerInfoLabel">
<rect key="frame" x="8" y="492" width="584" height="16"/>
<string key="text">Matrix provides identity servers to track which emails etc. belong to which Matrix IDs. Only https://matrix.org currently exists.</string>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bYl-BD-hfc" userLabel="authSwitchButton">
<rect key="frame" x="248" y="520" width="105" height="30"/>
<state key="normal" title="Create account">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="ZlD-EU-ncw" eventType="touchUpInside" id="MWy-Uy-9Nc"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="centerX" secondItem="0ot-Cn-Okj" secondAttribute="centerX" id="01i-Qj-xeO"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="5a8-4J-rqy" secondAttribute="trailing" constant="8" id="0M5-HS-blg"/>
<constraint firstAttribute="height" constant="640" id="42v-uQ-LsL"/>
<constraint firstAttribute="centerX" secondItem="LKF-ef-JTA" secondAttribute="centerX" id="69A-yA-IEW"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="F4V-zC-19g" secondAttribute="trailing" constant="8" id="8ab-Z4-kg9"/>
<constraint firstItem="5a8-4J-rqy" firstAttribute="top" secondItem="fnl-LF-rEL" secondAttribute="bottom" constant="15" id="BDw-eQ-BRA"/>
<constraint firstItem="0ot-Cn-Okj" firstAttribute="top" secondItem="F4V-zC-19g" secondAttribute="bottom" id="INC-BK-58n"/>
<constraint firstAttribute="centerX" secondItem="9oD-IQ-d8J" secondAttribute="centerX" id="JLJ-dk-JdK"/>
<constraint firstAttribute="centerX" secondItem="5a8-4J-rqy" secondAttribute="centerX" id="L9E-M9-JZR"/>
<constraint firstAttribute="centerX" secondItem="pvw-BQ-kaV" secondAttribute="centerX" multiplier="2.1" id="PQx-8d-2WE"/>
<constraint firstAttribute="centerX" secondItem="fnl-LF-rEL" secondAttribute="centerX" id="Sgz-AH-2EN"/>
<constraint firstItem="5a8-4J-rqy" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="d6h-O8-aBs" secondAttribute="leading" constant="8" id="VRZ-uz-BCX"/>
<constraint firstItem="F4V-zC-19g" firstAttribute="top" secondItem="LKF-ef-JTA" secondAttribute="bottom" constant="8" id="YYC-7e-ggI"/>
<constraint firstItem="LKF-ef-JTA" firstAttribute="top" secondItem="5a8-4J-rqy" secondAttribute="bottom" id="arP-7h-TB2"/>
<constraint firstAttribute="width" constant="600" placeholder="YES" id="b29-CD-6mb"/>
<constraint firstItem="sQb-Oi-ER6" firstAttribute="top" secondItem="pvw-BQ-kaV" secondAttribute="bottom" constant="5" id="dUA-me-Oly"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="0ot-Cn-Okj" secondAttribute="trailing" constant="8" id="gz6-Ih-fPV"/>
<constraint firstItem="fnl-LF-rEL" firstAttribute="top" secondItem="sQb-Oi-ER6" secondAttribute="bottom" constant="10" id="icu-FA-ijI"/>
<constraint firstItem="0ot-Cn-Okj" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="d6h-O8-aBs" secondAttribute="leading" constant="8" id="lBq-aj-naj"/>
<constraint firstItem="bYl-BD-hfc" firstAttribute="top" secondItem="0ot-Cn-Okj" secondAttribute="bottom" constant="12" id="lbG-dp-rtx"/>
<constraint firstItem="LKF-ef-JTA" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="d6h-O8-aBs" secondAttribute="leading" constant="8" id="pn0-tX-vwe"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="LKF-ef-JTA" secondAttribute="trailing" constant="8" id="prS-UK-oXJ"/>
<constraint firstAttribute="centerX" secondItem="bYl-BD-hfc" secondAttribute="centerX" id="rLh-01-K2X"/>
<constraint firstAttribute="centerX" secondItem="F4V-zC-19g" secondAttribute="centerX" id="wgS-c7-SWD"/>
<constraint firstItem="sQb-Oi-ER6" firstAttribute="top" secondItem="9oD-IQ-d8J" secondAttribute="bottom" constant="35" id="whU-LM-RDN"/>
<constraint firstAttribute="centerX" secondItem="sQb-Oi-ER6" secondAttribute="centerX" id="xGd-ig-cIb"/>
<constraint firstItem="sQb-Oi-ER6" firstAttribute="top" secondItem="d6h-O8-aBs" secondAttribute="top" constant="170" id="ynf-Lz-N7e"/>
<constraint firstItem="F4V-zC-19g" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="d6h-O8-aBs" secondAttribute="leading" constant="8" id="zTN-SN-HQK"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="d6h-O8-aBs" firstAttribute="leading" secondItem="udG-Mx-F4c" secondAttribute="leading" id="0AZ-PL-Sqy"/>
<constraint firstItem="d6h-O8-aBs" firstAttribute="top" secondItem="udG-Mx-F4c" secondAttribute="top" id="qxm-bJ-sOv"/>
<constraint firstAttribute="trailing" secondItem="d6h-O8-aBs" secondAttribute="trailing" id="xYP-vJ-Pau"/>
<constraint firstAttribute="bottom" secondItem="d6h-O8-aBs" secondAttribute="bottom" id="zJF-4I-K30"/>
</constraints>
</scrollView>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="m1P-2H-e3q" userLabel="RegistrationFallbackContentView">
<rect key="frame" x="0.0" y="20" width="600" height="580"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Wy8-eL-0qs">
<rect key="frame" x="542" y="5" width="50" height="35"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="SNe-ts-mx1"/>
<constraint firstAttribute="height" constant="35" id="cxj-k5-5eB"/>
</constraints>
<state key="normal" title="Cancel">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="ZlD-EU-ncw" eventType="touchUpInside" id="Z6U-iX-emw"/>
</connections>
</button>
<webView contentMode="scaleToFill" scalesPageToFit="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zwS-9o-UR1" userLabel="registrationWebView" customClass="MXCRegistrationWebView">
<rect key="frame" x="0.0" y="40" width="600" height="540"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</webView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="zwS-9o-UR1" firstAttribute="leading" secondItem="m1P-2H-e3q" secondAttribute="leading" id="PdC-BM-cGq"/>
<constraint firstItem="zwS-9o-UR1" firstAttribute="top" secondItem="Wy8-eL-0qs" secondAttribute="bottom" id="SHg-kb-HYZ"/>
<constraint firstAttribute="trailing" secondItem="Wy8-eL-0qs" secondAttribute="trailing" constant="8" id="dAE-oR-gyO"/>
<constraint firstItem="Wy8-eL-0qs" firstAttribute="top" secondItem="m1P-2H-e3q" secondAttribute="top" constant="5" id="jN2-LM-zDK"/>
<constraint firstAttribute="bottom" secondItem="zwS-9o-UR1" secondAttribute="bottom" id="mcB-1V-H10"/>
<constraint firstAttribute="trailing" secondItem="zwS-9o-UR1" secondAttribute="trailing" id="pET-Lk-c56"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="m1P-2H-e3q" firstAttribute="top" secondItem="crg-iM-twR" secondAttribute="bottom" id="4H8-cd-dqk"/>
<constraint firstAttribute="trailing" secondItem="udG-Mx-F4c" secondAttribute="trailing" id="A3Y-Nc-49v"/>
<constraint firstItem="gbK-Nm-HUT" firstAttribute="top" secondItem="udG-Mx-F4c" secondAttribute="bottom" id="CT4-Dg-1Dx"/>
<constraint firstItem="gbK-Nm-HUT" firstAttribute="top" secondItem="m1P-2H-e3q" secondAttribute="bottom" id="TvL-CC-lFt"/>
<constraint firstItem="udG-Mx-F4c" firstAttribute="leading" secondItem="wIi-Yi-2pi" secondAttribute="leading" id="gtx-Th-HKc"/>
<constraint firstAttribute="trailing" secondItem="m1P-2H-e3q" secondAttribute="trailing" id="hGg-r7-gFx"/>
<constraint firstItem="m1P-2H-e3q" firstAttribute="leading" secondItem="wIi-Yi-2pi" secondAttribute="leading" id="jyc-F5-4sW"/>
<constraint firstItem="udG-Mx-F4c" firstAttribute="top" secondItem="wIi-Yi-2pi" secondAttribute="top" constant="20" symbolic="YES" id="ku3-Lq-Cxe"/>
</constraints>
</view>
<connections>
<outlet property="authInputContainerViewHeightConstraint" destination="gDV-QE-CXd" id="w2j-CW-jC6"/>
<outlet property="authInputsContainerView" destination="sQb-Oi-ER6" id="16R-BA-8pJ"/>
<outlet property="authInputsEmailCodeBasedView" destination="G6Y-YQ-PvU" id="8IM-sx-LeP"/>
<outlet property="authInputsPasswordBasedView" destination="O8N-hu-Ewh" id="1ZN-jj-Txd"/>
<outlet property="authSwitchButton" destination="bYl-BD-hfc" id="RmX-1U-gbu"/>
<outlet property="authenticationActivityIndicator" destination="J51-Cj-6mb" id="kKy-b0-RS1"/>
<outlet property="authenticationScrollView" destination="udG-Mx-F4c" id="tzx-0l-1q0"/>
<outlet property="cancelRegistrationFallbackButton" destination="Wy8-eL-0qs" id="zDb-ys-RHw"/>
<outlet property="contentView" destination="d6h-O8-aBs" id="hId-Ic-rv4"/>
<outlet property="contentViewHeightConstraint" destination="42v-uQ-LsL" id="d3Y-uv-J8T"/>
<outlet property="createAccountLabel" destination="pvw-BQ-kaV" id="HvM-wg-gEw"/>
<outlet property="homeServerInfoLabel" destination="LKF-ef-JTA" id="0EM-71-189"/>
<outlet property="homeServerTextField" destination="iBz-4w-0cv" id="mPB-je-wz5"/>
<outlet property="identityServerInfoLabel" destination="0ot-Cn-Okj" id="fch-En-eR0"/>
<outlet property="identityServerTextField" destination="yfy-fB-piy" id="gwN-t1-TX9"/>
<outlet property="noFlowLabel" destination="eg7-eh-lZv" id="eQi-Nt-5Ik"/>
<outlet property="registrationFallbackContentView" destination="m1P-2H-e3q" id="cTq-S4-f9R"/>
<outlet property="registrationFallbackWebView" destination="zwS-9o-UR1" id="7g9-vt-riy"/>
<outlet property="retryButton" destination="70b-I3-Cuk" id="jHb-ZS-hrR"/>
<outlet property="submitButton" destination="fnl-LF-rEL" id="a9E-g2-2C2"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="mvZ-se-pqQ" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
@ -1647,7 +1222,6 @@
<resources>
<image name="default-profile.png" width="160" height="160"/>
<image name="icon_users.png" width="35" height="25"/>
<image name="logoHighRes.png" width="480" height="204"/>
<image name="matrixUser.png" width="16" height="16"/>
<image name="tab_home.ico" width="16" height="16"/>
<image name="tab_settings.png" width="25" height="25"/>

View file

@ -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 <UIKit/UIKit.h>
// Authentication type: register or login
typedef enum {
AuthenticationTypeRegister,
AuthenticationTypeLogin
}
AuthenticationType;
@class AuthInputsView;
@protocol AuthInputsViewDelegate <NSObject>
@optional
- (void)authInputsDoneKeyHasBeenPressed:(AuthInputsView *)authInputsView;
@end
@interface AuthInputsView : UIView <UITextFieldDelegate>
@property (nonatomic) AuthenticationType authType;
@property (nonatomic) id <AuthInputsViewDelegate> 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

View file

@ -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

View file

@ -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 <UIKit/UIKit.h>
#include <MXRestClient.h>
@interface MXCRegistrationWebView : UIWebView <UIWebViewDelegate>
/**
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

View file

@ -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

View file

@ -16,9 +16,7 @@
#import <MatrixKit/MatrixKit.h>
#import "AuthInputsView.h"
@interface AuthenticationViewController : MXKViewController <UITextFieldDelegate, AuthInputsViewDelegate>
@interface AuthenticationViewController : MXKAuthenticationViewController <MXKAuthenticationViewControllerDelegate>
@end

View file

@ -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

View file

@ -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;
}

View file

@ -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];

View file

@ -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) {