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 */; };
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 */; };
F0CA8D3D1A80DE08004320A4 /* EventDetailsView.m in Sources */ = {isa = PBXBuildFile; fileRef = F0CA8D3C1A80DE08004320A4 /* EventDetailsView.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 */; };
@ -189,6 +190,8 @@
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>"; };
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>"; };
F0CEA5AC19E6895E00E47915 /* logoHighRes.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logoHighRes.png; sourceTree = "<group>"; };
@ -328,6 +331,8 @@
F03EF5FC19F1762000A0EE52 /* View */ = {
isa = PBXGroup;
children = (
F0AC79311A8394510056D042 /* AuthInputsView.h */,
F0AC79321A8394510056D042 /* AuthInputsView.m */,
7176294D1A77FED800927125 /* ContactDetailsTableCell.h */,
7176294E1A77FED800927125 /* ContactDetailsTableCell.m */,
71193D2A1A6E433900E59A9E /* ContactTableCell.h */,
@ -649,6 +654,7 @@
F03EF5F719F171EB00A0EE52 /* AuthenticationViewController.m in Sources */,
F0D3C30F1A01330F0000D49E /* SettingsTableViewCell.m in Sources */,
71D2E4EC1A49814B000DE015 /* RoomMemberActionsCell.m in Sources */,
F0AC79331A8394510056D042 /* AuthInputsView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View file

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

View file

@ -52,8 +52,6 @@ static MatrixSDKHandler *sharedHandler = nil;
@implementation MatrixSDKHandler
@synthesize homeServerURL, homeServer, userLogin, userId, accessToken;
+ (MatrixSDKHandler *)sharedHandler {
@synchronized(self) {
if(sharedHandler == nil)
@ -79,6 +77,9 @@ static MatrixSDKHandler *sharedHandler = nil;
// 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];
@ -99,6 +100,11 @@ static MatrixSDKHandler *sharedHandler = nil;
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];
@ -204,6 +210,9 @@ static MatrixSDKHandler *sharedHandler = 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;
}
@ -386,10 +395,14 @@ static MatrixSDKHandler *sharedHandler = nil;
return [[NSUserDefaults standardUserDefaults] objectForKey:@"homeserverurl"];
}
- (void)setHomeServerURL:(NSString *)inHomeserverURL {
if (inHomeserverURL.length) {
[[NSUserDefaults standardUserDefaults] setObject:inHomeserverURL forKey:@"homeserverurl"];
self.mxRestClient = [[MXRestClient alloc] initWithHomeServer:inHomeserverURL];
- (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
@ -471,6 +484,24 @@ static MatrixSDKHandler *sharedHandler = nil;
[[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 {

View file

@ -1037,10 +1037,29 @@
<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="400"/>
<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" 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="164" y="185" width="272" height="30"/>
<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"/>
@ -1048,11 +1067,11 @@
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" returnKeyType="next"/>
<connections>
<outlet property="delegate" destination="ZlD-EU-ncw" id="q6h-TW-hTR"/>
<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="164" y="225" width="272" height="30"/>
<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"/>
@ -1060,13 +1079,200 @@
<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"/>
<outlet property="delegate" destination="O8N-hu-Ewh" id="enP-M1-s27"/>
</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"/>
<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="210" id="0Oj-hY-AuE"/>
<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"/>
</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" 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"/>
@ -1075,24 +1281,69 @@
<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">
<rect key="frame" x="180" y="68" width="240" height="102"/>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" constant="240" id="YfR-TZ-HGu"/>
<constraint firstAttribute="height" constant="102" id="qiX-ir-FRf"/>
<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>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fnl-LF-rEL">
<rect key="frame" x="281" y="265" width="38" height="30"/>
<state key="normal" title="Login">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
</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>
<action selector="onButtonPressed:" destination="ZlD-EU-ncw" eventType="touchUpInside" id="ER9-1w-3CL"/>
<outlet property="delegate" destination="ZlD-EU-ncw" id="7a0-Jw-Wg8"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bYl-BD-hfc">
<rect key="frame" x="248" y="345" width="105" height="30"/>
</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">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
@ -1100,45 +1351,46 @@
<action selector="onButtonPressed:" destination="ZlD-EU-ncw" eventType="touchUpInside" id="MWy-Uy-9Nc"/>
</connections>
</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>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="DkI-2d-TR5" firstAttribute="top" secondItem="9oD-IQ-d8J" secondAttribute="bottom" constant="15" id="00V-X5-82v"/>
<constraint firstAttribute="height" constant="400" id="42v-uQ-LsL"/>
<constraint firstItem="TW9-CJ-pH0" firstAttribute="top" secondItem="gDT-bj-MHE" secondAttribute="bottom" constant="54" id="49b-un-Aog"/>
<constraint firstItem="iBz-4w-0cv" firstAttribute="top" secondItem="fnl-LF-rEL" secondAttribute="bottom" constant="10" id="6Vs-3k-pOs"/>
<constraint firstItem="bYl-BD-hfc" firstAttribute="top" secondItem="iBz-4w-0cv" secondAttribute="bottom" constant="10" id="Gba-vG-eZb"/>
<constraint firstItem="fnl-LF-rEL" firstAttribute="top" secondItem="gDT-bj-MHE" secondAttribute="bottom" constant="10" id="Is5-SQ-bcL"/>
<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="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="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="centerX" secondItem="DkI-2d-TR5" secondAttribute="centerX" id="dS4-GI-IBQ"/>
<constraint firstItem="gDT-bj-MHE" firstAttribute="top" secondItem="DkI-2d-TR5" secondAttribute="bottom" constant="10" id="fE4-6M-Yc0"/>
<constraint firstAttribute="centerY" secondItem="DkI-2d-TR5" secondAttribute="centerY" id="mc2-E4-S5v"/>
<constraint firstAttribute="centerX" secondItem="gDT-bj-MHE" secondAttribute="centerX" id="oih-er-0Uz"/>
<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="iBz-4w-0cv" secondAttribute="centerX" constant="-52" id="umi-df-iLV"/>
<constraint firstAttribute="centerX" secondItem="J51-Cj-6mb" secondAttribute="centerX" id="xKM-Lu-0MM"/>
<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 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 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>
</subviews>
@ -1152,14 +1404,22 @@
</view>
<connections>
<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="contentViewBottomConstraint" destination="3LQ-0u-kCA" id="LuL-eo-fCe"/>
<outlet property="createAccountBtn" destination="bYl-BD-hfc" id="Kvu-Go-kEM"/>
<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="loginBtn" destination="fnl-LF-rEL" id="2aj-8W-Q6g"/>
<outlet property="passWordTextField" destination="gDT-bj-MHE" id="pP1-zd-BQn"/>
<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="retryButton" destination="70b-I3-Cuk" id="jHb-ZS-hrR"/>
<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>
</viewController>
<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"
@interface AuthenticationViewController : RageShakableViewController <UITextFieldDelegate>
#import "AuthInputsView.h"
@interface AuthenticationViewController : RageShakableViewController <UITextFieldDelegate, AuthInputsViewDelegate>
@end

View file

@ -20,24 +20,51 @@
#import "AppDelegate.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
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 (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 *userLoginTextField;
@property (weak, nonatomic) IBOutlet UITextField *passWordTextField;
@property (weak, nonatomic) IBOutlet UILabel *homeServerInfoLabel;
@property (weak, nonatomic) IBOutlet UITextField *identityServerTextField;
@property (weak, nonatomic) IBOutlet UILabel *identityServerInfoLabel;
@property (weak, nonatomic) IBOutlet UIButton *loginBtn;
@property (weak, nonatomic) IBOutlet UIButton *createAccountBtn;
@property (weak, nonatomic) IBOutlet UIButton *submitButton;
@property (weak, nonatomic) IBOutlet UIButton *authSwitchButton;
@property (strong, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
@property (weak, nonatomic) IBOutlet UILabel *noFlowLabel;
@property (weak, nonatomic) IBOutlet UIButton *retryButton;
@end
@ -47,9 +74,6 @@
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Finalize scrollView content size
_contentViewBottomConstraint.constant = 0;
// Force contentView in full width
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:self.contentView
attribute:NSLayoutAttributeLeading
@ -69,12 +93,30 @@
constant:0];
[self.view addConstraint:rightConstraint];
// Prefill text field
_userLoginTextField.text = [[MatrixSDKHandler sharedHandler] userLogin];
_scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_submitButton.enabled = NO;
_authSwitchButton.enabled = YES;
_authInputsPasswordBasedView.delegate = self;
_authInputsEmailCodeBasedView.delegate = self;
supportedFlows = [NSMutableArray array];
_homeServerTextField.text = [[MatrixSDKHandler sharedHandler] homeServerURL];
_passWordTextField.text = nil;
_loginBtn.enabled = NO;
_loginBtn.alpha = 0.5;
_identityServerTextField.text = [[MatrixSDKHandler sharedHandler] identityServerURL];
// 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 {
@ -85,6 +127,9 @@
- (void)viewWillAppear:(BOOL)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(onKeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
@ -95,111 +140,225 @@
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self dismissKeyboard];
// close any opened alert
if (alert) {
[alert dismiss:NO];
alert = nil;
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:AFNetworkingReachabilityDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil];
}
- (void)onKeyboardWillShow:(NSNotification *)notif {
NSValue *rectVal = notif.userInfo[UIKeyboardFrameEndUserInfoKey];
CGRect endRect = rectVal.CGRectValue;
UIEdgeInsets insets = self.scrollView.contentInset;
// Handle portrait/landscape mode
insets.bottom = (endRect.origin.y == 0) ? endRect.size.width : endRect.size.height;
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 {
UIEdgeInsets insets = self.scrollView.contentInset;
insets.bottom = 0;
self.scrollView.contentInset = insets;
}
- (void)dismissKeyboard {
// Hide the keyboard
[_userLoginTextField resignFirstResponder];
[_passWordTextField resignFirstResponder];
[_homeServerTextField resignFirstResponder];
}
#pragma mark - UITextField delegate
- (void)onTextFieldChange:(NSNotification *)notif {
NSString *user = _userLoginTextField.text;
NSString *pass = _passWordTextField.text;
NSString *homeServerURL = _homeServerTextField.text;
if (user.length && pass.length && homeServerURL.length) {
_loginBtn.enabled = YES;
_loginBtn.alpha = 1;
} else {
_loginBtn.enabled = NO;
_loginBtn.alpha = 0.5;
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
MatrixSDKHandler *matrix = [MatrixSDKHandler sharedHandler];
if (textField == _userLoginTextField) {
[matrix setUserLogin:textField.text];
}
else if (textField == _homeServerTextField) {
[matrix setHomeServerURL:textField.text];
if (!textField.text.length) {
// Force refresh with default value
textField.text = [[MatrixSDKHandler sharedHandler] homeServerURL];
}
}
}
- (BOOL)textFieldShouldReturn:(UITextField*) textField {
if (textField == _userLoginTextField) {
// "Next" key has been pressed
[_passWordTextField becomeFirstResponder];
}
else {
// "Done" key has been pressed
[textField resignFirstResponder];
if (_loginBtn.isEnabled) {
// Launch authentication now
[self onButtonPressed:_loginBtn];
}
}
return YES;
}
#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 == _loginBtn) {
if (sender == _submitButton) {
MatrixSDKHandler *matrix = [MatrixSDKHandler sharedHandler];
if (matrix.mxRestClient)
{
// Disable login button to prevent multiple requests
_loginBtn.enabled = NO;
if (matrix.mxRestClient) {
// Disable user interaction to prevent multiple requests
[self setUserInteractionEnabled:NO];
[_activityIndicator startAnimating];
[matrix.mxRestClient loginWithUser:matrix.userLogin andPassword:_passWordTextField.text
if (_authType == AuthenticationTypeLogin) {
if ([_selectedFlow.type isEqualToString:kMXLoginFlowTypePassword]) {
[matrix.mxRestClient loginWithUser:matrix.userLogin andPassword:_authInputsPasswordBasedView.passWordTextField.text
success:^(MXCredentials *credentials){
[_activityIndicator stopAnimating];
@ -217,10 +376,33 @@
[self dismissViewControllerAnimated:YES completion:nil];
}
failure:^(NSError *error){
[_activityIndicator stopAnimating];
_loginBtn.enabled = YES;
[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];
}
}
NSLog(@"Login failed: %@", error);
- (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;
@ -234,7 +416,7 @@
if (errCode) {
if ([errCode isEqualToString:@"M_FORBIDDEN"]) {
message = @"Invalid username/password";
} else if (localizedError .length > 0) {
} else if (localizedError.length > 0) {
message = localizedError;
} else if ([errCode isEqualToString:@"M_UNKNOWN_TOKEN"]) {
message = @"The access token specified was not recognised";
@ -258,10 +440,81 @@
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 {
NSValue *rectVal = notif.userInfo[UIKeyboardFrameEndUserInfoKey];
CGRect endRect = rectVal.CGRectValue;
UIEdgeInsets insets = self.scrollView.contentInset;
// Handle portrait/landscape mode
insets.bottom = (endRect.origin.y == 0) ? endRect.size.width : endRect.size.height;
self.scrollView.contentInset = insets;
}
- (void)onKeyboardWillHide:(NSNotification *)notif {
UIEdgeInsets insets = self.scrollView.contentInset;
insets.bottom = 0;
self.scrollView.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;
}
} else if (sender == _createAccountBtn){
// TODO
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
MatrixSDKHandler *mxHandler = [MatrixSDKHandler 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];
}
}

View file

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