Console: Improve Authentication screen to support multiple kinds of flow during login and registration.

This commit is contained in:
giomfo 2015-02-10 16:12:58 +01:00
parent 204267f4f3
commit 75e1a606ac
9 changed files with 1031 additions and 207 deletions

View file

@ -65,6 +65,7 @@
F08B6FCC1A1DE7F80094A35B /* matrixConsole.jpg in Resources */ = {isa = PBXBuildFile; fileRef = F08B6FCB1A1DE7F80094A35B /* matrixConsole.jpg */; }; F08B6FCC1A1DE7F80094A35B /* matrixConsole.jpg in Resources */ = {isa = PBXBuildFile; fileRef = F08B6FCB1A1DE7F80094A35B /* matrixConsole.jpg */; };
F08DCBDB1A093BFA008C65B6 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F08DCBDA1A093BFA008C65B6 /* MobileCoreServices.framework */; }; F08DCBDB1A093BFA008C65B6 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F08DCBDA1A093BFA008C65B6 /* MobileCoreServices.framework */; };
F08E67961A77965A00AABD4C /* MXC3PID.m in Sources */ = {isa = PBXBuildFile; fileRef = F08E67931A77965A00AABD4C /* MXC3PID.m */; }; F08E67961A77965A00AABD4C /* MXC3PID.m in Sources */ = {isa = PBXBuildFile; fileRef = F08E67931A77965A00AABD4C /* MXC3PID.m */; };
F0AC79331A8394510056D042 /* AuthInputsView.m in Sources */ = {isa = PBXBuildFile; fileRef = F0AC79321A8394510056D042 /* AuthInputsView.m */; };
F0CA8D3D1A80DE08004320A4 /* EventDetailsView.m in Sources */ = {isa = PBXBuildFile; fileRef = F0CA8D3C1A80DE08004320A4 /* EventDetailsView.m */; }; F0CA8D3D1A80DE08004320A4 /* EventDetailsView.m in Sources */ = {isa = PBXBuildFile; fileRef = F0CA8D3C1A80DE08004320A4 /* EventDetailsView.m */; };
F0CEA5AE19E6895E00E47915 /* logoHighRes.png in Resources */ = {isa = PBXBuildFile; fileRef = F0CEA5AC19E6895E00E47915 /* logoHighRes.png */; }; F0CEA5AE19E6895E00E47915 /* logoHighRes.png in Resources */ = {isa = PBXBuildFile; fileRef = F0CEA5AC19E6895E00E47915 /* logoHighRes.png */; };
F0CEA5AF19E6895E00E47915 /* tab_recents.png in Resources */ = {isa = PBXBuildFile; fileRef = F0CEA5AD19E6895E00E47915 /* tab_recents.png */; }; F0CEA5AF19E6895E00E47915 /* tab_recents.png in Resources */ = {isa = PBXBuildFile; fileRef = F0CEA5AD19E6895E00E47915 /* tab_recents.png */; };
@ -189,6 +190,8 @@
F08DCBDA1A093BFA008C65B6 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; 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>"; }; 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>"; }; 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>"; };
F0CA8D3B1A80DE08004320A4 /* EventDetailsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventDetailsView.h; sourceTree = "<group>"; }; F0CA8D3B1A80DE08004320A4 /* EventDetailsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventDetailsView.h; sourceTree = "<group>"; };
F0CA8D3C1A80DE08004320A4 /* EventDetailsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EventDetailsView.m; sourceTree = "<group>"; }; F0CA8D3C1A80DE08004320A4 /* EventDetailsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EventDetailsView.m; sourceTree = "<group>"; };
F0CEA5AC19E6895E00E47915 /* logoHighRes.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logoHighRes.png; sourceTree = "<group>"; }; F0CEA5AC19E6895E00E47915 /* logoHighRes.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logoHighRes.png; sourceTree = "<group>"; };
@ -328,6 +331,8 @@
F03EF5FC19F1762000A0EE52 /* View */ = { F03EF5FC19F1762000A0EE52 /* View */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
F0AC79311A8394510056D042 /* AuthInputsView.h */,
F0AC79321A8394510056D042 /* AuthInputsView.m */,
7176294D1A77FED800927125 /* ContactDetailsTableCell.h */, 7176294D1A77FED800927125 /* ContactDetailsTableCell.h */,
7176294E1A77FED800927125 /* ContactDetailsTableCell.m */, 7176294E1A77FED800927125 /* ContactDetailsTableCell.m */,
71193D2A1A6E433900E59A9E /* ContactTableCell.h */, 71193D2A1A6E433900E59A9E /* ContactTableCell.h */,
@ -649,6 +654,7 @@
F03EF5F719F171EB00A0EE52 /* AuthenticationViewController.m in Sources */, F03EF5F719F171EB00A0EE52 /* AuthenticationViewController.m in Sources */,
F0D3C30F1A01330F0000D49E /* SettingsTableViewCell.m in Sources */, F0D3C30F1A01330F0000D49E /* SettingsTableViewCell.m in Sources */,
71D2E4EC1A49814B000DE015 /* RoomMemberActionsCell.m in Sources */, 71D2E4EC1A49814B000DE015 /* RoomMemberActionsCell.m in Sources */,
F0AC79331A8394510056D042 /* AuthInputsView.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View file

@ -38,6 +38,7 @@ typedef enum : NSUInteger {
@property (strong, nonatomic) NSString *userId; @property (strong, nonatomic) NSString *userId;
@property (strong, nonatomic, readonly) NSString *localPartFromUserId; @property (strong, nonatomic, readonly) NSString *localPartFromUserId;
@property (strong, nonatomic) NSString *accessToken; @property (strong, nonatomic) NSString *accessToken;
@property (strong, nonatomic) NSString *identityServerURL;
// The type of events to display // The type of events to display
@property (strong, nonatomic) NSArray *eventsFilterForMessages; @property (strong, nonatomic) NSArray *eventsFilterForMessages;

View file

@ -52,8 +52,6 @@ static MatrixSDKHandler *sharedHandler = nil;
@implementation MatrixSDKHandler @implementation MatrixSDKHandler
@synthesize homeServerURL, homeServer, userLogin, userId, accessToken;
+ (MatrixSDKHandler *)sharedHandler { + (MatrixSDKHandler *)sharedHandler {
@synchronized(self) { @synchronized(self) {
if(sharedHandler == nil) if(sharedHandler == nil)
@ -79,6 +77,9 @@ static MatrixSDKHandler *sharedHandler = nil;
// Read potential homeserver url in shared defaults object // Read potential homeserver url in shared defaults object
if (self.homeServerURL) { if (self.homeServerURL) {
self.mxRestClient = [[MXRestClient alloc] initWithHomeServer:self.homeServerURL]; self.mxRestClient = [[MXRestClient alloc] initWithHomeServer:self.homeServerURL];
if (self.identityServerURL) {
[self.mxRestClient setIdentityServer:self.identityServerURL];
}
if (self.accessToken) { if (self.accessToken) {
[self openSession]; [self openSession];
@ -99,6 +100,11 @@ static MatrixSDKHandler *sharedHandler = nil;
self.mxRestClient = [[MXRestClient alloc] initWithCredentials:credentials]; self.mxRestClient = [[MXRestClient alloc] initWithCredentials:credentials];
if (self.mxRestClient) { if (self.mxRestClient) {
// Set identity server (if any)
if (self.identityServerURL) {
[self.mxRestClient setIdentityServer:self.identityServerURL];
}
// Use MXFileStore as MXStore to permanently store events // Use MXFileStore as MXStore to permanently store events
_mxFileStore = [[MXFileStore alloc] init]; _mxFileStore = [[MXFileStore alloc] init];
@ -204,6 +210,9 @@ static MatrixSDKHandler *sharedHandler = nil;
[self.mxRestClient close]; [self.mxRestClient close];
if (self.homeServerURL) { if (self.homeServerURL) {
self.mxRestClient = [[MXRestClient alloc] initWithHomeServer:self.homeServerURL]; self.mxRestClient = [[MXRestClient alloc] initWithHomeServer:self.homeServerURL];
if (self.identityServerURL) {
[self.mxRestClient setIdentityServer:self.identityServerURL];
}
} else { } else {
self.mxRestClient = nil; self.mxRestClient = nil;
} }
@ -386,10 +395,14 @@ static MatrixSDKHandler *sharedHandler = nil;
return [[NSUserDefaults standardUserDefaults] objectForKey:@"homeserverurl"]; return [[NSUserDefaults standardUserDefaults] objectForKey:@"homeserverurl"];
} }
- (void)setHomeServerURL:(NSString *)inHomeserverURL { - (void)setHomeServerURL:(NSString *)inHomeServerURL {
if (inHomeserverURL.length) { if (inHomeServerURL.length) {
[[NSUserDefaults standardUserDefaults] setObject:inHomeserverURL forKey:@"homeserverurl"]; [[NSUserDefaults standardUserDefaults] setObject:inHomeServerURL forKey:@"homeserverurl"];
self.mxRestClient = [[MXRestClient alloc] initWithHomeServer:inHomeserverURL]; self.mxRestClient = [[MXRestClient alloc] initWithHomeServer:inHomeServerURL];
// Set identity server (if any)
if (self.identityServerURL) {
[self.mxRestClient setIdentityServer:self.identityServerURL];
}
} else { } else {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"homeserverurl"]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"homeserverurl"];
// Reinitialize matrix handler // Reinitialize matrix handler
@ -471,6 +484,24 @@ static MatrixSDKHandler *sharedHandler = nil;
[[NSUserDefaults standardUserDefaults] synchronize]; [[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 #pragma mark - Matrix user's settings
- (void)setUserPresence:(MXPresence)userPresence andStatusMessage:(NSString *)statusMessage completion:(void (^)(void))completion { - (void)setUserPresence:(MXPresence)userPresence andStatusMessage:(NSString *)statusMessage completion:(void (^)(void))completion {

View file

@ -1037,62 +1037,313 @@
<rect key="frame" x="0.0" y="20" width="600" height="580"/> <rect key="frame" x="0.0" y="20" width="600" height="580"/>
<subviews> <subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="d6h-O8-aBs" userLabel="Content View"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="d6h-O8-aBs" userLabel="Content View">
<rect key="frame" x="0.0" y="0.0" width="600" height="400"/> <rect key="frame" x="0.0" y="0.0" width="600" height="640"/>
<subviews> <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="164" y="185" 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="ZlD-EU-ncw" id="q6h-TW-hTR"/>
</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="164" y="225" 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="ZlD-EU-ncw" id="eB5-lZ-ZaG"/>
</connections>
</textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="URL (e.g. http://matrix.org:80)" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="iBz-4w-0cv">
<rect key="frame" x="247" y="305" width="210" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="210" id="0Oj-hY-AuE"/>
<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>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="logoHighRes.png" translatesAutoresizingMaskIntoConstraints="NO" id="9oD-IQ-d8J"> <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="logoHighRes.png" translatesAutoresizingMaskIntoConstraints="NO" id="9oD-IQ-d8J">
<rect key="frame" x="180" y="68" width="240" height="102"/> <rect key="frame" x="180" y="33" width="240" height="102"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="240" id="YfR-TZ-HGu"/> <constraint firstAttribute="width" constant="240" id="YfR-TZ-HGu"/>
<constraint firstAttribute="height" constant="102" id="qiX-ir-FRf"/> <constraint firstAttribute="height" constant="102" id="qiX-ir-FRf"/>
</constraints> </constraints>
</imageView> </imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fnl-LF-rEL"> <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="281" y="265" width="38" height="30"/> <rect key="frame" x="88" y="147" width="110" height="18"/>
<state key="normal" title="Login"> <fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<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" cocoaTouchSystemColor="darkTextColor"/>
<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" cocoaTouchSystemColor="darkTextColor"/>
<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" 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"/> <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state> </state>
<connections> <connections>
<action selector="onButtonPressed:" destination="ZlD-EU-ncw" eventType="touchUpInside" id="ER9-1w-3CL"/> <action selector="onButtonPressed:" destination="ZlD-EU-ncw" eventType="touchUpInside" id="ER9-1w-3CL"/>
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bYl-BD-hfc"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5a8-4J-rqy" userLabel="HomeServerView">
<rect key="frame" x="248" y="345" width="105" height="30"/> <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" cocoaTouchSystemColor="darkTextColor"/>
<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" cocoaTouchSystemColor="darkTextColor"/>
<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" cocoaTouchSystemColor="darkTextColor"/>
<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" cocoaTouchSystemColor="darkTextColor"/>
<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"> <state key="normal" title="Create account">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/> <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state> </state>
@ -1100,45 +1351,46 @@
<action selector="onButtonPressed:" destination="ZlD-EU-ncw" eventType="touchUpInside" id="MWy-Uy-9Nc"/> <action selector="onButtonPressed:" destination="ZlD-EU-ncw" eventType="touchUpInside" id="MWy-Uy-9Nc"/>
</connections> </connections>
</button> </button>
<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="142" y="309" width="106" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<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="290" y="190" width="20" height="20"/>
</activityIndicatorView>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints> <constraints>
<constraint firstItem="DkI-2d-TR5" firstAttribute="top" secondItem="9oD-IQ-d8J" secondAttribute="bottom" constant="15" id="00V-X5-82v"/> <constraint firstAttribute="centerX" secondItem="0ot-Cn-Okj" secondAttribute="centerX" id="01i-Qj-xeO"/>
<constraint firstAttribute="height" constant="400" id="42v-uQ-LsL"/> <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="5a8-4J-rqy" secondAttribute="trailing" constant="8" id="0M5-HS-blg"/>
<constraint firstItem="TW9-CJ-pH0" firstAttribute="top" secondItem="gDT-bj-MHE" secondAttribute="bottom" constant="54" id="49b-un-Aog"/> <constraint firstAttribute="height" constant="640" id="42v-uQ-LsL"/>
<constraint firstItem="iBz-4w-0cv" firstAttribute="top" secondItem="fnl-LF-rEL" secondAttribute="bottom" constant="10" id="6Vs-3k-pOs"/> <constraint firstAttribute="centerX" secondItem="LKF-ef-JTA" secondAttribute="centerX" id="69A-yA-IEW"/>
<constraint firstItem="bYl-BD-hfc" firstAttribute="top" secondItem="iBz-4w-0cv" secondAttribute="bottom" constant="10" id="Gba-vG-eZb"/> <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="F4V-zC-19g" secondAttribute="trailing" constant="8" id="8ab-Z4-kg9"/>
<constraint firstItem="fnl-LF-rEL" firstAttribute="top" secondItem="gDT-bj-MHE" secondAttribute="bottom" constant="10" id="Is5-SQ-bcL"/> <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="9oD-IQ-d8J" secondAttribute="centerX" id="JLJ-dk-JdK"/>
<constraint firstAttribute="centerY" secondItem="J51-Cj-6mb" secondAttribute="centerY" id="RJl-T1-G3f"/> <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 firstAttribute="centerX" secondItem="fnl-LF-rEL" secondAttribute="centerX" id="Sgz-AH-2EN"/>
<constraint firstAttribute="centerX" secondItem="TW9-CJ-pH0" secondAttribute="centerX" constant="105" id="V29-yw-qHo"/> <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 firstAttribute="width" constant="600" placeholder="YES" id="b29-CD-6mb"/>
<constraint firstAttribute="centerX" secondItem="DkI-2d-TR5" secondAttribute="centerX" id="dS4-GI-IBQ"/> <constraint firstItem="sQb-Oi-ER6" firstAttribute="top" secondItem="pvw-BQ-kaV" secondAttribute="bottom" constant="5" id="dUA-me-Oly"/>
<constraint firstItem="gDT-bj-MHE" firstAttribute="top" secondItem="DkI-2d-TR5" secondAttribute="bottom" constant="10" id="fE4-6M-Yc0"/> <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="0ot-Cn-Okj" secondAttribute="trailing" constant="8" id="gz6-Ih-fPV"/>
<constraint firstAttribute="centerY" secondItem="DkI-2d-TR5" secondAttribute="centerY" id="mc2-E4-S5v"/> <constraint firstItem="fnl-LF-rEL" firstAttribute="top" secondItem="sQb-Oi-ER6" secondAttribute="bottom" constant="10" id="icu-FA-ijI"/>
<constraint firstAttribute="centerX" secondItem="gDT-bj-MHE" secondAttribute="centerX" id="oih-er-0Uz"/> <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="bYl-BD-hfc" secondAttribute="centerX" id="rLh-01-K2X"/>
<constraint firstAttribute="centerX" secondItem="iBz-4w-0cv" secondAttribute="centerX" constant="-52" id="umi-df-iLV"/> <constraint firstAttribute="centerX" secondItem="F4V-zC-19g" secondAttribute="centerX" id="wgS-c7-SWD"/>
<constraint firstAttribute="centerX" secondItem="J51-Cj-6mb" secondAttribute="centerX" id="xKM-Lu-0MM"/> <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> </constraints>
</view> </view>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints> <constraints>
<constraint firstItem="d6h-O8-aBs" firstAttribute="leading" secondItem="udG-Mx-F4c" secondAttribute="leading" id="0AZ-PL-Sqy"/> <constraint firstItem="d6h-O8-aBs" firstAttribute="leading" secondItem="udG-Mx-F4c" secondAttribute="leading" id="0AZ-PL-Sqy"/>
<constraint firstAttribute="bottom" secondItem="d6h-O8-aBs" secondAttribute="bottom" constant="180" id="3LQ-0u-kCA"/>
<constraint firstItem="d6h-O8-aBs" firstAttribute="top" secondItem="udG-Mx-F4c" secondAttribute="top" id="qxm-bJ-sOv"/> <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="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> </constraints>
</scrollView> </scrollView>
</subviews> </subviews>
@ -1152,14 +1404,22 @@
</view> </view>
<connections> <connections>
<outlet property="activityIndicator" destination="J51-Cj-6mb" id="hYh-tr-rjl"/> <outlet property="activityIndicator" destination="J51-Cj-6mb" id="hYh-tr-rjl"/>
<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="contentView" destination="d6h-O8-aBs" id="hId-Ic-rv4"/> <outlet property="contentView" destination="d6h-O8-aBs" id="hId-Ic-rv4"/>
<outlet property="contentViewBottomConstraint" destination="3LQ-0u-kCA" id="LuL-eo-fCe"/> <outlet property="contentViewHeightConstraint" destination="42v-uQ-LsL" id="d3Y-uv-J8T"/>
<outlet property="createAccountBtn" destination="bYl-BD-hfc" id="Kvu-Go-kEM"/> <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="homeServerTextField" destination="iBz-4w-0cv" id="mPB-je-wz5"/>
<outlet property="loginBtn" destination="fnl-LF-rEL" id="2aj-8W-Q6g"/> <outlet property="identityServerInfoLabel" destination="0ot-Cn-Okj" id="fch-En-eR0"/>
<outlet property="passWordTextField" destination="gDT-bj-MHE" id="pP1-zd-BQn"/> <outlet property="identityServerTextField" destination="yfy-fB-piy" id="gwN-t1-TX9"/>
<outlet property="noFlowLabel" destination="eg7-eh-lZv" id="eQi-Nt-5Ik"/>
<outlet property="retryButton" destination="70b-I3-Cuk" id="jHb-ZS-hrR"/>
<outlet property="scrollView" destination="udG-Mx-F4c" id="Aot-SZ-PxD"/> <outlet property="scrollView" destination="udG-Mx-F4c" id="Aot-SZ-PxD"/>
<outlet property="userLoginTextField" destination="DkI-2d-TR5" id="2ZH-ps-oDN"/> <outlet property="submitButton" destination="fnl-LF-rEL" id="a9E-g2-2C2"/>
</connections> </connections>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="mvZ-se-pqQ" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="mvZ-se-pqQ" userLabel="First Responder" sceneMemberID="firstResponder"/>

View file

@ -0,0 +1,60 @@
/*
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

@ -0,0 +1,209 @@
/*
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 "MatrixSDKHandler.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 = [[MatrixSDKHandler 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) {
[[MatrixSDKHandler 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) {
[[MatrixSDKHandler 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

@ -16,7 +16,9 @@
#import "RageShakableViewController.h" #import "RageShakableViewController.h"
@interface AuthenticationViewController : RageShakableViewController <UITextFieldDelegate> #import "AuthInputsView.h"
@interface AuthenticationViewController : RageShakableViewController <UITextFieldDelegate, AuthInputsViewDelegate>
@end @end

View file

@ -20,24 +20,51 @@
#import "AppDelegate.h" #import "AppDelegate.h"
#import "MXCAlert.h" #import "MXCAlert.h"
@interface AuthenticationViewController () #import "AFNetworkReachabilityManager.h"
{
@interface AuthenticationViewController () {
// Current request in progress
NSOperation *mxAuthFlowRequest;
// 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 // reference to any opened alert view
MXCAlert *alert; MXCAlert *alert;
} }
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewBottomConstraint;
// Return true if the provided flow (kMXLoginFlowType) is supported by the application
+ (BOOL)isImplementedFlowType:(NSString*)flowType;
// The current authentication type
@property (nonatomic) AuthenticationType authType;
@property (nonatomic) MXLoginFlow *selectedFlow;
@property (strong, nonatomic) IBOutlet UIScrollView *scrollView; @property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
@property (weak, nonatomic) IBOutlet UIView *contentView; @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 UITextField *homeServerTextField;
@property (weak, nonatomic) IBOutlet UITextField *userLoginTextField; @property (weak, nonatomic) IBOutlet UILabel *homeServerInfoLabel;
@property (weak, nonatomic) IBOutlet UITextField *passWordTextField; @property (weak, nonatomic) IBOutlet UITextField *identityServerTextField;
@property (weak, nonatomic) IBOutlet UILabel *identityServerInfoLabel;
@property (weak, nonatomic) IBOutlet UIButton *loginBtn; @property (weak, nonatomic) IBOutlet UIButton *submitButton;
@property (weak, nonatomic) IBOutlet UIButton *createAccountBtn; @property (weak, nonatomic) IBOutlet UIButton *authSwitchButton;
@property (strong, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator; @property (strong, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
@property (weak, nonatomic) IBOutlet UILabel *noFlowLabel;
@property (weak, nonatomic) IBOutlet UIButton *retryButton;
@end @end
@ -47,9 +74,6 @@
[super viewDidLoad]; [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib. // Do any additional setup after loading the view, typically from a nib.
// Finalize scrollView content size
_contentViewBottomConstraint.constant = 0;
// Force contentView in full width // Force contentView in full width
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:self.contentView NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:self.contentView
attribute:NSLayoutAttributeLeading attribute:NSLayoutAttributeLeading
@ -68,13 +92,31 @@
multiplier:1.0 multiplier:1.0
constant:0]; constant:0];
[self.view addConstraint:rightConstraint]; [self.view addConstraint:rightConstraint];
// Prefill text field _scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_userLoginTextField.text = [[MatrixSDKHandler sharedHandler] userLogin];
_submitButton.enabled = NO;
_authSwitchButton.enabled = YES;
_authInputsPasswordBasedView.delegate = self;
_authInputsEmailCodeBasedView.delegate = self;
supportedFlows = [NSMutableArray array];
_homeServerTextField.text = [[MatrixSDKHandler sharedHandler] homeServerURL]; _homeServerTextField.text = [[MatrixSDKHandler sharedHandler] homeServerURL];
_passWordTextField.text = nil; _identityServerTextField.text = [[MatrixSDKHandler sharedHandler] identityServerURL];
_loginBtn.enabled = NO;
_loginBtn.alpha = 0.5; // Set default auth type
dispatch_async(dispatch_get_main_queue(), ^{
self.authType = AuthenticationTypeLogin;
});
}
- (void)dealloc {
supportedFlows = nil;
if (mxAuthFlowRequest){
[mxAuthFlowRequest cancel];
mxAuthFlowRequest = nil;
}
} }
- (void)didReceiveMemoryWarning { - (void)didReceiveMemoryWarning {
@ -85,6 +127,9 @@
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
// Update supported authentication flow
[self refreshSupportedAuthFlow];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onKeyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; [[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(onKeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
@ -94,18 +139,311 @@
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated]; [super viewWillDisappear:animated];
[self dismissKeyboard];
// close any opened alert // close any opened alert
if (alert) { if (alert) {
[alert dismiss:NO]; [alert dismiss:NO];
alert = nil; alert = nil;
} }
[[NSNotificationCenter defaultCenter] removeObserver:self name:AFNetworkingReachabilityDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil];
} }
#pragma mark -
+ (BOOL)isImplementedFlowType:(NSString*)flowType {
if ([flowType isEqualToString:kMXLoginFlowTypePassword] || [flowType isEqualToString:kMXLoginFlowTypeEmailCode]) {
return YES;
}
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 {
MatrixSDKHandler *mxHandler = [MatrixSDKHandler sharedHandler];
// Stop reachability monitoring
[[AFNetworkReachabilityManager sharedManager] stopMonitoring];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AFNetworkingReachabilityDidChangeNotification object:nil];
// Cancel protential request in progress
[mxAuthFlowRequest cancel];
mxAuthFlowRequest = nil;
[_activityIndicator startAnimating];
self.selectedFlow = nil;
if (_authType == AuthenticationTypeLogin) {
mxAuthFlowRequest = [mxHandler.mxRestClient getLoginFlow:^(NSArray *flows) {
[self handleHomeServerFlows:flows];
} failure:^(NSError *error) {
[self onFailureDuringFlowRefresh:error];
}];
} else {
mxAuthFlowRequest = [mxHandler.mxRestClient getRegisterFlow:^(NSArray *flows) {
[self handleHomeServerFlows:flows];
} failure:^(NSError *error) {
[self onFailureDuringFlowRefresh:error];
}];
}
}
- (void)handleHomeServerFlows:(NSArray *)flows {
[_activityIndicator stopAnimating];
[supportedFlows removeAllObjects];
for (MXLoginFlow* flow in flows) {
if ([AuthenticationViewController isImplementedFlowType:flow.type]) {
[supportedFlows addObject:flow];
}
}
if (supportedFlows.count) {
// FIXME display supported flows
// Currently we select password based auth
for (MXLoginFlow* flow in supportedFlows) {
if ([flow.type isEqualToString:kMXLoginFlowTypePassword]) {
self.selectedFlow = flow;
break;
} else if ([flow.type isEqualToString:kMXLoginFlowTypeEmailCode]) {
self.selectedFlow = flow;
break;
}
}
}
if (!_selectedFlow) {
// Notify user that no flow is supported
_noFlowLabel.text = [NSString stringWithFormat:@"Currently we do not support %@ flows defined by this Home Server", _authType == AuthenticationTypeLogin ? @"Login" : @"Registration"];
_noFlowLabel.hidden = NO;
_retryButton.hidden = NO;
}
}
- (void)onFailureDuringFlowRefresh:(NSError*)error {
[_activityIndicator stopAnimating];
if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == kCFURLErrorCancelled) {
// Ignore this error
return;
}
NSLog(@"GET auth flows failed: %@", error);
// Alert user
NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey];
if (!title)
{
title = @"Error";
}
NSString *msg = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
alert = [[MXCAlert alloc] initWithTitle:title message:msg style:MXCAlertStyleAlert];
alert.cancelButtonIndex = [alert addActionWithTitle:@"Dismiss" style:MXCAlertActionStyleDefault handler:^(MXCAlert *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 flow from this Home Server";
}
_retryButton.hidden = NO;
// Handle specific error code here
if ([error.domain isEqualToString:NSURLErrorDomain]) {
// Check network reachability
if (error.code == NSURLErrorNotConnectedToInternet) {
// Start monitoring in order to launch a new request when network will be available
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReachabilityStatusChange:) name:AFNetworkingReachabilityDidChangeNotification object:nil];
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
} 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) {
MatrixSDKHandler *matrix = [MatrixSDKHandler sharedHandler];
if (matrix.mxRestClient) {
// Disable user interaction to prevent multiple requests
[self setUserInteractionEnabled:NO];
[_activityIndicator startAnimating];
if (_authType == AuthenticationTypeLogin) {
if ([_selectedFlow.type isEqualToString:kMXLoginFlowTypePassword]) {
[matrix.mxRestClient loginWithUser:matrix.userLogin andPassword:_authInputsPasswordBasedView.passWordTextField.text
success:^(MXCredentials *credentials){
[_activityIndicator 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(@"Unexpected error: 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];
}
}
- (void)onFailureDuringAuthRequest:(NSError *)error {
[_activityIndicator stopAnimating];
[self setUserInteractionEnabled:YES];
NSLog(@"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 = [[MXCAlert alloc] initWithTitle:@"Login Failed" message:message style:MXCAlertStyleAlert];
[alert addActionWithTitle:@"Dismiss" style:MXCAlertActionStyleCancel handler:^(MXCAlert *alert) {}];
[alert showInViewController:self];
}
#pragma mark - Keyboard handling
- (void)onKeyboardWillShow:(NSNotification *)notif { - (void)onKeyboardWillShow:(NSNotification *)notif {
NSValue *rectVal = notif.userInfo[UIKeyboardFrameEndUserInfoKey]; NSValue *rectVal = notif.userInfo[UIKeyboardFrameEndUserInfoKey];
CGRect endRect = rectVal.CGRectValue; CGRect endRect = rectVal.CGRectValue;
@ -114,13 +452,6 @@
// Handle portrait/landscape mode // Handle portrait/landscape mode
insets.bottom = (endRect.origin.y == 0) ? endRect.size.width : endRect.size.height; insets.bottom = (endRect.origin.y == 0) ? endRect.size.width : endRect.size.height;
self.scrollView.contentInset = insets; self.scrollView.contentInset = insets;
for (UITextField *tf in @[ self.userLoginTextField, self.passWordTextField, self.homeServerTextField]) {
if ([tf isFirstResponder]) {
CGRect tfFrame = tf.frame;
[self.scrollView scrollRectToVisible:tfFrame animated:YES];
}
}
} }
- (void)onKeyboardWillHide:(NSNotification *)notif { - (void)onKeyboardWillHide:(NSNotification *)notif {
@ -131,137 +462,59 @@
- (void)dismissKeyboard { - (void)dismissKeyboard {
// Hide the keyboard // Hide the keyboard
[_userLoginTextField resignFirstResponder]; [currentAuthInputsView dismissKeyboard];
[_passWordTextField resignFirstResponder];
[_homeServerTextField resignFirstResponder]; [_homeServerTextField resignFirstResponder];
[_identityServerTextField resignFirstResponder];
} }
#pragma mark - UITextField delegate #pragma mark - UITextField delegate
- (void)onTextFieldChange:(NSNotification *)notif { - (void)onTextFieldChange:(NSNotification *)notif {
NSString *user = _userLoginTextField.text;
NSString *pass = _passWordTextField.text;
NSString *homeServerURL = _homeServerTextField.text; NSString *homeServerURL = _homeServerTextField.text;
if (user.length && pass.length && homeServerURL.length) { if (currentAuthInputsView.areAllRequiredFieldsFilled && homeServerURL.length) {
_loginBtn.enabled = YES; _submitButton.enabled = YES;
_loginBtn.alpha = 1;
} else { } else {
_loginBtn.enabled = NO; _submitButton.enabled = NO;
_loginBtn.alpha = 0.5;
} }
} }
- (void)textFieldDidEndEditing:(UITextField *)textField { - (void)textFieldDidEndEditing:(UITextField *)textField {
MatrixSDKHandler *matrix = [MatrixSDKHandler sharedHandler]; MatrixSDKHandler *mxHandler = [MatrixSDKHandler sharedHandler];
if (textField == _homeServerTextField) {
if (textField == _userLoginTextField) { if (![[mxHandler homeServerURL] isEqualToString:textField.text]) {
[matrix setUserLogin: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 == _homeServerTextField) { else if (textField == _identityServerTextField) {
[matrix setHomeServerURL:textField.text]; [mxHandler setIdentityServerURL:textField.text];
if (!textField.text.length) { if (!textField.text.length) {
// Force refresh with default value // Force refresh with default value
textField.text = [[MatrixSDKHandler sharedHandler] homeServerURL]; textField.text = [mxHandler identityServerURL];
} }
} }
} }
- (BOOL)textFieldShouldReturn:(UITextField*) textField { - (BOOL)textFieldShouldReturn:(UITextField*)textField {
if (textField == _userLoginTextField) { if (textField.returnKeyType == UIReturnKeyDone) {
// "Next" key has been pressed
[_passWordTextField becomeFirstResponder];
}
else {
// "Done" key has been pressed // "Done" key has been pressed
[textField resignFirstResponder]; [textField resignFirstResponder];
if (_loginBtn.isEnabled) {
// Launch authentication now
[self onButtonPressed:_loginBtn];
}
} }
return YES; return YES;
} }
#pragma mark - #pragma mark - AuthInputsViewDelegate delegate
- (IBAction)onButtonPressed:(id)sender { - (void)authInputsDoneKeyHasBeenPressed:(AuthInputsView *)authInputsView {
[self dismissKeyboard]; if (_submitButton.isEnabled) {
// Launch authentication now
if (sender == _loginBtn) { [self onButtonPressed:_submitButton];
MatrixSDKHandler *matrix = [MatrixSDKHandler sharedHandler];
if (matrix.mxRestClient)
{
// Disable login button to prevent multiple requests
_loginBtn.enabled = NO;
[_activityIndicator startAnimating];
[matrix.mxRestClient loginWithUser:matrix.userLogin andPassword:_passWordTextField.text
success:^(MXCredentials *credentials){
[_activityIndicator 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(@"Unexpected error: the userId is not correctly formatted: %@", credentials.userId);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
failure:^(NSError *error){
[_activityIndicator stopAnimating];
_loginBtn.enabled = YES;
NSLog(@"Login 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 = [[MXCAlert alloc] initWithTitle:@"Login Failed" message:message style:MXCAlertStyleAlert];
[alert addActionWithTitle:@"Dismiss" style:MXCAlertActionStyleCancel handler:^(MXCAlert *alert) {}];
[alert showInViewController:self];
}];
}
} else if (sender == _createAccountBtn){
// TODO
} }
} }

View file

@ -2,8 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>identityserverurl</key>
<string>https://matrix.org</string>
<key>homeserverurl</key> <key>homeserverurl</key>
<string>http://matrix.org</string> <string>https://matrix.org</string>
<key>homeserver</key> <key>homeserver</key>
<string>matrix.org</string> <string>matrix.org</string>
<key>apnsIsActive</key> <key>apnsIsActive</key>