Merge pull request #3890 from vector-im/element_3846
Implement social login
|
@ -2,10 +2,11 @@ Changes to be released in next version
|
|||
=================================================
|
||||
|
||||
✨ Features
|
||||
*
|
||||
* AuthVC: Add social login (#3846).
|
||||
|
||||
🙌 Improvements
|
||||
* Show user id in the room invite preview screen (#3839)
|
||||
* AuthVC: SSO authentication now use redirect URL instead of fallback page (#3846).
|
||||
|
||||
🐛 Bugfix
|
||||
* Crash report cannot be submitted (on small phones) #3819
|
||||
|
|
|
@ -52,6 +52,17 @@ final class BuildSettings: NSObject {
|
|||
return keychainAccessGroup
|
||||
}
|
||||
|
||||
static var applicationURLScheme: String? {
|
||||
guard let urlTypes = Bundle.app.object(forInfoDictionaryKey: "CFBundleURLTypes") as? [AnyObject],
|
||||
let urlTypeDictionary = urlTypes.first as? [String: AnyObject],
|
||||
let urlSchemes = urlTypeDictionary["CFBundleURLSchemes"] as? [AnyObject],
|
||||
let externalURLScheme = urlSchemes.first as? String else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return externalURLScheme
|
||||
}
|
||||
|
||||
static var pushKitAppIdProd: String {
|
||||
return baseBundleIdentifier + ".ios.voip.prod"
|
||||
}
|
||||
|
|
|
@ -27,3 +27,5 @@ KEYCHAIN_ACCESS_GROUP = $(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER).keychain
|
|||
|
||||
//Make Xcode 12 and fastlane(xcodebuild) happy while some pods are not updated
|
||||
EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64
|
||||
|
||||
APPLICATION_SCHEME = element
|
||||
|
|
|
@ -219,6 +219,8 @@
|
|||
B12676882523E4D100BE6B98 /* SecretsResetViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12676802523E4D100BE6B98 /* SecretsResetViewState.swift */; };
|
||||
B12676892523E4D100BE6B98 /* SecretsResetViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12676812523E4D100BE6B98 /* SecretsResetViewAction.swift */; };
|
||||
B126768A2523E4D100BE6B98 /* SecretsResetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12676822523E4D100BE6B98 /* SecretsResetViewModel.swift */; };
|
||||
B12742BA258C46DD00731DA6 /* SSOAuthentificationSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12742B9258C46DC00731DA6 /* SSOAuthentificationSessionProtocol.swift */; };
|
||||
B12742BC258C472800731DA6 /* LegacySSOAuthentificationSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12742BB258C472800731DA6 /* LegacySSOAuthentificationSession.swift */; };
|
||||
B1284E3E2535FEA6003529D7 /* SecureBackupSetupIntroViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1284E3D2535FEA6003529D7 /* SecureBackupSetupIntroViewModelType.swift */; };
|
||||
B1284E402535FEBA003529D7 /* SecureBackupSetupIntroViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1284E3F2535FEBA003529D7 /* SecureBackupSetupIntroViewModel.swift */; };
|
||||
B12C56EF2396CB5E00FAC6DE /* RoomMessageURLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12C56EE2396CB5E00FAC6DE /* RoomMessageURLParser.swift */; };
|
||||
|
@ -246,6 +248,9 @@
|
|||
B140B4A821F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B140B4A721F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift */; };
|
||||
B142317A22CCFA2000FFA96A /* EditHistoryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B142317822CCFA2000FFA96A /* EditHistoryCell.swift */; };
|
||||
B142317B22CCFA2000FFA96A /* EditHistoryCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B142317922CCFA2000FFA96A /* EditHistoryCell.xib */; };
|
||||
B14EED19257992D700448735 /* SocialLoginListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14EED18257992D600448735 /* SocialLoginListView.swift */; };
|
||||
B14EED1B2579933100448735 /* SocialLoginListView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B14EED1A2579933100448735 /* SocialLoginListView.xib */; };
|
||||
B14EED1D257D85E000448735 /* SocialLoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14EED1C257D85DF00448735 /* SocialLoginButton.swift */; };
|
||||
B14F142E22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B14F142622144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard */; };
|
||||
B14F142F22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142722144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift */; };
|
||||
B14F143022144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142822144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift */; };
|
||||
|
@ -313,6 +318,14 @@
|
|||
B169331520F3CAFC00746532 /* PublicRoomTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B169330F20F3CAFC00746532 /* PublicRoomTableViewCell.xib */; };
|
||||
B169331620F3CAFC00746532 /* PublicRoomsDirectoryDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = B169331220F3CAFC00746532 /* PublicRoomsDirectoryDataSource.m */; };
|
||||
B169331720F3CBE000746532 /* RecentCellData.m in Sources */ = {isa = PBXBuildFile; fileRef = B16932F920F3C51900746532 /* RecentCellData.m */; };
|
||||
B16C028D25A71CA3008CA7B1 /* CustomSchemeURLConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16C028C25A71CA3008CA7B1 /* CustomSchemeURLConstants.swift */; };
|
||||
B16D6354257D8FB0008BDC97 /* SocialLoginButtonFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16D6353257D8FB0008BDC97 /* SocialLoginButtonFactory.swift */; };
|
||||
B16DC22B2587DC35004DAB1A /* SSOAuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16DC22A2587DC35004DAB1A /* SSOAuthenticationService.swift */; };
|
||||
B16DC235258A723B004DAB1A /* SourceImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16DC234258A723B004DAB1A /* SourceImage.swift */; };
|
||||
B16DC238258A7595004DAB1A /* SocialLoginButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16DC237258A7595004DAB1A /* SocialLoginButtonStyle.swift */; };
|
||||
B16DC23A258A75B7004DAB1A /* SocialLoginButtonViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16DC239258A75B7004DAB1A /* SocialLoginButtonViewData.swift */; };
|
||||
B16DC23C258A7D13004DAB1A /* ThemeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16DC23B258A7D13004DAB1A /* ThemeService.swift */; };
|
||||
B16DC23E258A8025004DAB1A /* ThemeIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16DC23D258A8025004DAB1A /* ThemeIdentifier.swift */; };
|
||||
B17982FF2119FED2001FD722 /* GDPRConsentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17982FE2119FED2001FD722 /* GDPRConsentViewController.swift */; };
|
||||
B1798302211B13B3001FD722 /* OnBoardingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1798301211B13B3001FD722 /* OnBoardingManager.swift */; };
|
||||
B183226623F55D6B0035B2E8 /* CameraAccessManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B183226523F55D6B0035B2E8 /* CameraAccessManager.swift */; };
|
||||
|
@ -333,6 +346,10 @@
|
|||
B190F55922CE356800AEB493 /* EditHistoryHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B190F55822CE356800AEB493 /* EditHistoryHeaderView.swift */; };
|
||||
B190F55B22CE35FD00AEB493 /* EditHistoryHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B190F55A22CE35FD00AEB493 /* EditHistoryHeaderView.xib */; };
|
||||
B190F55D22CE5A9700AEB493 /* EditHistorySection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B190F55C22CE5A9600AEB493 /* EditHistorySection.swift */; };
|
||||
B1945CC4258A81FB0020D8D6 /* ThemeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16DC23B258A7D13004DAB1A /* ThemeService.swift */; };
|
||||
B1945CC5258A81FE0020D8D6 /* ThemeIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16DC23D258A8025004DAB1A /* ThemeIdentifier.swift */; };
|
||||
B1945CC9258C04F30020D8D6 /* SSOAuthenticationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1945CC8258C04F20020D8D6 /* SSOAuthenticationPresenter.swift */; };
|
||||
B1945CCB258C24500020D8D6 /* SSOAuthentificationSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1945CCA258C244F0020D8D6 /* SSOAuthentificationSession.swift */; };
|
||||
B1963B2B228F1C4900CBA17F /* BubbleReactionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1963B25228F1C4800CBA17F /* BubbleReactionsView.swift */; };
|
||||
B1963B2C228F1C4900CBA17F /* BubbleReactionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1963B26228F1C4800CBA17F /* BubbleReactionViewCell.xib */; };
|
||||
B1963B2D228F1C4900CBA17F /* BubbleReactionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1963B27228F1C4800CBA17F /* BubbleReactionsViewModel.swift */; };
|
||||
|
@ -344,6 +361,10 @@
|
|||
B197B7C6243DE947005ABBF3 /* EncryptionTrustLevelBadgeImageHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B197B7C5243DE947005ABBF3 /* EncryptionTrustLevelBadgeImageHelper.swift */; };
|
||||
B19EFA3921F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */; };
|
||||
B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */; };
|
||||
B1A15BD425A4BAA800BDCA36 /* CustomSchemeURLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A15BD325A4BAA800BDCA36 /* CustomSchemeURLParser.swift */; };
|
||||
B1A15BD625A526A200BDCA36 /* URLComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A15BD525A526A200BDCA36 /* URLComponents.swift */; };
|
||||
B1A15BD925A6652800BDCA36 /* DeepLinkOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A15BD825A6652800BDCA36 /* DeepLinkOption.swift */; };
|
||||
B1A15BDB25A6680E00BDCA36 /* SSOURLConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A15BDA25A6680E00BDCA36 /* SSOURLConstants.swift */; };
|
||||
B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A5B33D227ADF2A004CBA85 /* UIImage.swift */; };
|
||||
B1A67946257559CF00BB0C69 /* RootTabEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B11F4D2025681500009F1586 /* RootTabEmptyView.swift */; };
|
||||
B1A67947257559D500BB0C69 /* RootTabEmptyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B11F4D1E256814E5009F1586 /* RootTabEmptyView.xib */; };
|
||||
|
@ -1262,6 +1283,8 @@
|
|||
B12676802523E4D100BE6B98 /* SecretsResetViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsResetViewState.swift; sourceTree = "<group>"; };
|
||||
B12676812523E4D100BE6B98 /* SecretsResetViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsResetViewAction.swift; sourceTree = "<group>"; };
|
||||
B12676822523E4D100BE6B98 /* SecretsResetViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsResetViewModel.swift; sourceTree = "<group>"; };
|
||||
B12742B9258C46DC00731DA6 /* SSOAuthentificationSessionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSOAuthentificationSessionProtocol.swift; sourceTree = "<group>"; };
|
||||
B12742BB258C472800731DA6 /* LegacySSOAuthentificationSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacySSOAuthentificationSession.swift; sourceTree = "<group>"; };
|
||||
B1284E3D2535FEA6003529D7 /* SecureBackupSetupIntroViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupSetupIntroViewModelType.swift; sourceTree = "<group>"; };
|
||||
B1284E3F2535FEBA003529D7 /* SecureBackupSetupIntroViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupSetupIntroViewModel.swift; sourceTree = "<group>"; };
|
||||
B12C56EE2396CB5E00FAC6DE /* RoomMessageURLParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMessageURLParser.swift; sourceTree = "<group>"; };
|
||||
|
@ -1289,6 +1312,9 @@
|
|||
B140B4A721F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorBridgePresenter.swift; sourceTree = "<group>"; };
|
||||
B142317822CCFA2000FFA96A /* EditHistoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHistoryCell.swift; sourceTree = "<group>"; };
|
||||
B142317922CCFA2000FFA96A /* EditHistoryCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EditHistoryCell.xib; sourceTree = "<group>"; };
|
||||
B14EED18257992D600448735 /* SocialLoginListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginListView.swift; sourceTree = "<group>"; };
|
||||
B14EED1A2579933100448735 /* SocialLoginListView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SocialLoginListView.xib; sourceTree = "<group>"; };
|
||||
B14EED1C257D85DF00448735 /* SocialLoginButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginButton.swift; sourceTree = "<group>"; };
|
||||
B14F142622144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = KeyBackupRecoverFromRecoveryKeyViewController.storyboard; sourceTree = "<group>"; };
|
||||
B14F142722144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyViewModelType.swift; sourceTree = "<group>"; };
|
||||
B14F142822144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
|
@ -1406,6 +1432,14 @@
|
|||
B169331020F3CAFC00746532 /* PublicRoomTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PublicRoomTableViewCell.h; sourceTree = "<group>"; };
|
||||
B169331220F3CAFC00746532 /* PublicRoomsDirectoryDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PublicRoomsDirectoryDataSource.m; sourceTree = "<group>"; };
|
||||
B169331320F3CAFC00746532 /* PublicRoomsDirectoryDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PublicRoomsDirectoryDataSource.h; sourceTree = "<group>"; };
|
||||
B16C028C25A71CA3008CA7B1 /* CustomSchemeURLConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSchemeURLConstants.swift; sourceTree = "<group>"; };
|
||||
B16D6353257D8FB0008BDC97 /* SocialLoginButtonFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginButtonFactory.swift; sourceTree = "<group>"; };
|
||||
B16DC22A2587DC35004DAB1A /* SSOAuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSOAuthenticationService.swift; sourceTree = "<group>"; };
|
||||
B16DC234258A723B004DAB1A /* SourceImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceImage.swift; sourceTree = "<group>"; };
|
||||
B16DC237258A7595004DAB1A /* SocialLoginButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginButtonStyle.swift; sourceTree = "<group>"; };
|
||||
B16DC239258A75B7004DAB1A /* SocialLoginButtonViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginButtonViewData.swift; sourceTree = "<group>"; };
|
||||
B16DC23B258A7D13004DAB1A /* ThemeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeService.swift; sourceTree = "<group>"; };
|
||||
B16DC23D258A8025004DAB1A /* ThemeIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeIdentifier.swift; sourceTree = "<group>"; };
|
||||
B17982FE2119FED2001FD722 /* GDPRConsentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GDPRConsentViewController.swift; sourceTree = "<group>"; };
|
||||
B1798301211B13B3001FD722 /* OnBoardingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnBoardingManager.swift; sourceTree = "<group>"; };
|
||||
B183226523F55D6B0035B2E8 /* CameraAccessManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraAccessManager.swift; sourceTree = "<group>"; };
|
||||
|
@ -1426,6 +1460,9 @@
|
|||
B190F55822CE356800AEB493 /* EditHistoryHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHistoryHeaderView.swift; sourceTree = "<group>"; };
|
||||
B190F55A22CE35FD00AEB493 /* EditHistoryHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EditHistoryHeaderView.xib; sourceTree = "<group>"; };
|
||||
B190F55C22CE5A9600AEB493 /* EditHistorySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHistorySection.swift; sourceTree = "<group>"; };
|
||||
B1945CC6258B77520020D8D6 /* SocialLoginDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginDataSource.swift; sourceTree = "<group>"; };
|
||||
B1945CC8258C04F20020D8D6 /* SSOAuthenticationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSOAuthenticationPresenter.swift; sourceTree = "<group>"; };
|
||||
B1945CCA258C244F0020D8D6 /* SSOAuthentificationSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSOAuthentificationSession.swift; sourceTree = "<group>"; };
|
||||
B1963B25228F1C4800CBA17F /* BubbleReactionsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleReactionsView.swift; sourceTree = "<group>"; };
|
||||
B1963B26228F1C4800CBA17F /* BubbleReactionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BubbleReactionViewCell.xib; sourceTree = "<group>"; };
|
||||
B1963B27228F1C4800CBA17F /* BubbleReactionsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleReactionsViewModel.swift; sourceTree = "<group>"; };
|
||||
|
@ -1437,6 +1474,10 @@
|
|||
B197B7C5243DE947005ABBF3 /* EncryptionTrustLevelBadgeImageHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionTrustLevelBadgeImageHelper.swift; sourceTree = "<group>"; };
|
||||
B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinator.swift; sourceTree = "<group>"; };
|
||||
B1A15BD325A4BAA800BDCA36 /* CustomSchemeURLParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSchemeURLParser.swift; sourceTree = "<group>"; };
|
||||
B1A15BD525A526A200BDCA36 /* URLComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLComponents.swift; sourceTree = "<group>"; };
|
||||
B1A15BD825A6652800BDCA36 /* DeepLinkOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinkOption.swift; sourceTree = "<group>"; };
|
||||
B1A15BDA25A6680E00BDCA36 /* SSOURLConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSOURLConstants.swift; sourceTree = "<group>"; };
|
||||
B1A5B33D227ADF2A004CBA85 /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = "<group>"; };
|
||||
B1A6805324B7C65200E312CC /* MajorUpdateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MajorUpdateManager.swift; sourceTree = "<group>"; };
|
||||
B1A68592229E807800D6C09A /* RoomBubbleCellLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomBubbleCellLayout.swift; sourceTree = "<group>"; };
|
||||
|
@ -2307,9 +2348,11 @@
|
|||
32242F0B21E8FBA900725742 /* Theme */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
32242F0C21E8FBA900725742 /* ThemeService.m */,
|
||||
32242F0D21E8FBA900725742 /* Theme.swift */,
|
||||
32242F1121E8FBA900725742 /* ThemeService.h */,
|
||||
32242F0C21E8FBA900725742 /* ThemeService.m */,
|
||||
B16DC23B258A7D13004DAB1A /* ThemeService.swift */,
|
||||
B16DC23D258A8025004DAB1A /* ThemeIdentifier.swift */,
|
||||
32242F0D21E8FBA900725742 /* Theme.swift */,
|
||||
32242F0E21E8FBA900725742 /* Themes */,
|
||||
3232ABC1225B996100AD6A5C /* Themable.swift */,
|
||||
);
|
||||
|
@ -3222,6 +3265,33 @@
|
|||
path = DataSources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B16DC2192587AED2004DAB1A /* SSO */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1945CC8258C04F20020D8D6 /* SSOAuthenticationPresenter.swift */,
|
||||
B1A15BDA25A6680E00BDCA36 /* SSOURLConstants.swift */,
|
||||
B16DC22A2587DC35004DAB1A /* SSOAuthenticationService.swift */,
|
||||
B12742B9258C46DC00731DA6 /* SSOAuthentificationSessionProtocol.swift */,
|
||||
B1945CCA258C244F0020D8D6 /* SSOAuthentificationSession.swift */,
|
||||
B12742BB258C472800731DA6 /* LegacySSOAuthentificationSession.swift */,
|
||||
);
|
||||
path = SSO;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B16DC236258A7516004DAB1A /* SocialLogin */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B16DC237258A7595004DAB1A /* SocialLoginButtonStyle.swift */,
|
||||
B16DC239258A75B7004DAB1A /* SocialLoginButtonViewData.swift */,
|
||||
B14EED1C257D85DF00448735 /* SocialLoginButton.swift */,
|
||||
B16D6353257D8FB0008BDC97 /* SocialLoginButtonFactory.swift */,
|
||||
B1945CC6258B77520020D8D6 /* SocialLoginDataSource.swift */,
|
||||
B14EED18257992D600448735 /* SocialLoginListView.swift */,
|
||||
B14EED1A2579933100448735 /* SocialLoginListView.xib */,
|
||||
);
|
||||
path = SocialLogin;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B17982FD2119FEA7001FD722 /* GDPR */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -3340,6 +3410,16 @@
|
|||
path = KeyVerification;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1A15BD725A664F400BDCA36 /* DeepLink */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1A15BD825A6652800BDCA36 /* DeepLinkOption.swift */,
|
||||
B16C028C25A71CA3008CA7B1 /* CustomSchemeURLConstants.swift */,
|
||||
B1A15BD325A4BAA800BDCA36 /* CustomSchemeURLParser.swift */,
|
||||
);
|
||||
path = DeepLink;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1A6805224B7C60900E312CC /* MajorUpdate */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -3458,6 +3538,7 @@
|
|||
B1A6C10523881ECB002882FD /* SlidingModal */,
|
||||
32DB556722FDADE50016329E /* ServiceTerms */,
|
||||
B1550FC52420E8F400CE097B /* QRCode */,
|
||||
B1A15BD725A664F400BDCA36 /* DeepLink */,
|
||||
B1B556CD20EE6C4C00210D55 /* Common */,
|
||||
);
|
||||
path = Modules;
|
||||
|
@ -3875,6 +3956,8 @@
|
|||
B1B556FA20EE6C4C00210D55 /* AuthenticationViewController.h */,
|
||||
B1B556FC20EE6C4C00210D55 /* AuthenticationViewController.m */,
|
||||
B1B556FB20EE6C4C00210D55 /* AuthenticationViewController.xib */,
|
||||
B16DC236258A7516004DAB1A /* SocialLogin */,
|
||||
B16DC2192587AED2004DAB1A /* SSO */,
|
||||
B1B5579220EF575A00210D55 /* Views */,
|
||||
);
|
||||
path = Authentication;
|
||||
|
@ -5199,6 +5282,7 @@
|
|||
ECAE7AEA24EC223D002FA813 /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B16DC234258A723B004DAB1A /* SourceImage.swift */,
|
||||
ECAE7AE424EC0E01002FA813 /* TableViewSections.swift */,
|
||||
ECAE7AE624EC15F7002FA813 /* Section.swift */,
|
||||
ECAE7AE824EC1888002FA813 /* Row.swift */,
|
||||
|
@ -5460,6 +5544,7 @@
|
|||
ECAE7AED24EFDD1F002FA813 /* MXSessionState.swift */,
|
||||
ECF57A4F250913E4004BBF9D /* MXKTableViewCellWithLabelAndSwitch.swift */,
|
||||
ECF57A5625091ECC004BBF9D /* MXKTableViewCellWithTextView.swift */,
|
||||
B1A15BD525A526A200BDCA36 /* URLComponents.swift */,
|
||||
);
|
||||
path = Categories;
|
||||
sourceTree = "<group>";
|
||||
|
@ -5976,6 +6061,7 @@
|
|||
B1B558DB20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.xib in Resources */,
|
||||
B185145D24B8C9A400EE19EA /* MajorUpdateViewController.storyboard in Resources */,
|
||||
B1CE83E42422817200D07506 /* KeyVerificationVerifyBySASViewController.storyboard in Resources */,
|
||||
B14EED1B2579933100448735 /* SocialLoginListView.xib in Resources */,
|
||||
B1B5572B20EE6C4D00210D55 /* RoomMemberDetailsViewController.xib in Resources */,
|
||||
B1D4752C21EE52C30067973F /* KeyBackupSetupIntroViewController.storyboard in Resources */,
|
||||
B1B558C620EF768F00210D55 /* RoomIncomingEncryptedAttachmentBubbleCell.xib in Resources */,
|
||||
|
@ -6166,6 +6252,8 @@
|
|||
B169328420F38BE300746532 /* SegmentedViewController.m in Sources */,
|
||||
32FD757824D2C9BA00BA7B37 /* Bundle.swift in Sources */,
|
||||
32242F1821E8FBF800725742 /* DefaultTheme.swift in Sources */,
|
||||
B1945CC4258A81FB0020D8D6 /* ThemeService.swift in Sources */,
|
||||
B1945CC5258A81FE0020D8D6 /* ThemeIdentifier.swift in Sources */,
|
||||
B1664BCA20F4E67600808783 /* ShareViewController.m in Sources */,
|
||||
B1664BC620F4E67600808783 /* FallbackViewController.m in Sources */,
|
||||
32242F1621E8FBCC00725742 /* ThemeService.m in Sources */,
|
||||
|
@ -6249,6 +6337,7 @@
|
|||
B1CE83DE2422817200D07506 /* KeyVerificationVerifyBySASViewModelType.swift in Sources */,
|
||||
32FD757024D2BEF700BA7B37 /* InfoPlist.swift in Sources */,
|
||||
F083BE021E7009ED00A9B29C /* AvatarGenerator.m in Sources */,
|
||||
B16DC23E258A8025004DAB1A /* ThemeIdentifier.swift in Sources */,
|
||||
B157FAA023264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewModelType.swift in Sources */,
|
||||
B1B5573A20EE6C4D00210D55 /* GroupRoomsViewController.m in Sources */,
|
||||
B1B558F920EF768F00210D55 /* RoomOutgoingTextMsgWithoutSenderNameBubbleCell.m in Sources */,
|
||||
|
@ -6260,6 +6349,7 @@
|
|||
B1B4E9BF24D4703E004D5C33 /* BaseBubbleCell.swift in Sources */,
|
||||
ECFBD5FD250FA59B00DD5F5A /* RoomCreationEventsModalViewController.swift in Sources */,
|
||||
B16932A520F3A21C00746532 /* empty.mm in Sources */,
|
||||
B1945CCB258C24500020D8D6 /* SSOAuthentificationSession.swift in Sources */,
|
||||
3232AB4A2256558300AD6A5C /* FlowTemplateCoordinator.swift in Sources */,
|
||||
ECF57A4825090C23004BBF9D /* EnterNewRoomDetailsCoordinatorType.swift in Sources */,
|
||||
ECFBD5D7250A7AAF00DD5F5A /* RoomsDirectoryCoordinatorBridgePresenter.swift in Sources */,
|
||||
|
@ -6341,6 +6431,7 @@
|
|||
3232AB4D2256558300AD6A5C /* TemplateScreenCoordinatorType.swift in Sources */,
|
||||
B1B5581720EF625800210D55 /* PreviewRoomTitleView.m in Sources */,
|
||||
B1DCC63F22E9A3AE00625807 /* EmojiItem+EmojiMart.swift in Sources */,
|
||||
B16DC235258A723B004DAB1A /* SourceImage.swift in Sources */,
|
||||
B1DCC61C22E5E17100625807 /* EmojiPickerViewAction.swift in Sources */,
|
||||
B1098BDF21ECE09F000DDA48 /* Strings.swift in Sources */,
|
||||
EC3B066924AC6ADE000DF9BF /* CrossSigningService.swift in Sources */,
|
||||
|
@ -6371,10 +6462,13 @@
|
|||
B1B558C320EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.m in Sources */,
|
||||
B10A3E9D24FE88CB007C380F /* RootRouterType.swift in Sources */,
|
||||
B1B9DEDC22E9B7440065E677 /* SerializationServiceType.swift in Sources */,
|
||||
B16DC23C258A7D13004DAB1A /* ThemeService.swift in Sources */,
|
||||
B110872521F098F0003554A5 /* ActivityIndicatorPresenter.swift in Sources */,
|
||||
32242F1521E8FBA900725742 /* DarkTheme.swift in Sources */,
|
||||
B1A15BD625A526A200BDCA36 /* URLComponents.swift in Sources */,
|
||||
B1D211E222BD193C00D939BD /* ReactionsMenuViewModel.swift in Sources */,
|
||||
EC2B4EF124A1EEBD005EB739 /* DataProtectionHelper.swift in Sources */,
|
||||
B16DC23A258A75B7004DAB1A /* SocialLoginButtonViewData.swift in Sources */,
|
||||
B140B4A621F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift in Sources */,
|
||||
B1B5577420EE702900210D55 /* WidgetViewController.m in Sources */,
|
||||
B1DCC63122E7026F00625807 /* EmojiPickerHeaderView.swift in Sources */,
|
||||
|
@ -6434,6 +6528,7 @@
|
|||
B1B9DEE822EB34EF0065E677 /* ReactionHistoryCoordinatorType.swift in Sources */,
|
||||
EC711B7424A63B37008F830C /* SecretsSetupRecoveryKeyViewModelType.swift in Sources */,
|
||||
B14F143122144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewState.swift in Sources */,
|
||||
B16C028D25A71CA3008CA7B1 /* CustomSchemeURLConstants.swift in Sources */,
|
||||
32DB557F22FDADE50016329E /* ServiceTermsModalScreenCoordinator.swift in Sources */,
|
||||
EC31F014251B53AD00D407DA /* RoomInfoBasicView.swift in Sources */,
|
||||
EC1CA85F24C1DEC400DE9EBF /* EnterPinCodeViewModelType.swift in Sources */,
|
||||
|
@ -6531,6 +6626,7 @@
|
|||
B12C56EF2396CB5E00FAC6DE /* RoomMessageURLParser.swift in Sources */,
|
||||
EC711B9924A63B37008F830C /* SecretsRecoveryCoordinator.swift in Sources */,
|
||||
B1BEE73623DF44A60003A4CB /* UserVerificationSessionsStatusCoordinatorType.swift in Sources */,
|
||||
B16DC238258A7595004DAB1A /* SocialLoginButtonStyle.swift in Sources */,
|
||||
B1C45A86232A8C2600165425 /* SettingsIdentityServerViewModelType.swift in Sources */,
|
||||
F083BE031E7009ED00A9B29C /* EventFormatter.m in Sources */,
|
||||
B157FAA623264AE900EBFBD4 /* SettingsDiscoveryThreePidDetailsViewController.swift in Sources */,
|
||||
|
@ -6543,6 +6639,7 @@
|
|||
B1DCC63B22E85EF800625807 /* EmojiMartCategory.swift in Sources */,
|
||||
3232AB4F2256558300AD6A5C /* TemplateScreenViewController.swift in Sources */,
|
||||
B1B558FC20EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.m in Sources */,
|
||||
B14EED1D257D85E000448735 /* SocialLoginButton.swift in Sources */,
|
||||
B1B5572920EE6C4D00210D55 /* RoomFilesViewController.m in Sources */,
|
||||
B1BEE74B23E093260003A4CB /* UserVerificationSessionStatusViewAction.swift in Sources */,
|
||||
B1098C1021ED07E4000DDA48 /* Presentable.swift in Sources */,
|
||||
|
@ -6601,6 +6698,7 @@
|
|||
B1DCC63722E8541700625807 /* EmojiStore.swift in Sources */,
|
||||
3232ABA6225730E100AD6A5C /* DeviceVerificationStartViewController.swift in Sources */,
|
||||
B16932EA20F3C39000746532 /* UnifiedSearchRecentsDataSource.m in Sources */,
|
||||
B16D6354257D8FB0008BDC97 /* SocialLoginButtonFactory.swift in Sources */,
|
||||
B10A3E9924FE86AF007C380F /* SplitViewCoordinator.swift in Sources */,
|
||||
B1BEE72A23DF38B20003A4CB /* UserVerificationSessionStatusCell.swift in Sources */,
|
||||
B1C45A8A232A8C2600165425 /* SettingsIdentityServerCoordinatorBridgePresenter.swift in Sources */,
|
||||
|
@ -6692,6 +6790,7 @@
|
|||
B12D7A0223E2462200FACEDC /* UserVerificationStartViewAction.swift in Sources */,
|
||||
EC85D7372477DD97002C44C9 /* LocalContactsSectionHeaderContainerView.m in Sources */,
|
||||
B1DCC61A22E5E17100625807 /* EmojiPickerViewController.swift in Sources */,
|
||||
B1A15BD425A4BAA800BDCA36 /* CustomSchemeURLParser.swift in Sources */,
|
||||
ECFBD5C9250A7AAF00DD5F5A /* RoomsDirectoryCoordinator.swift in Sources */,
|
||||
B1963B32228F1C6B00CBA17F /* BubbleReactionsViewModelType.swift in Sources */,
|
||||
EC1CA89A24C9C9A200DE9EBF /* SetupBiometricsCoordinatorType.swift in Sources */,
|
||||
|
@ -6701,12 +6800,14 @@
|
|||
EC1CA87524C8259700DE9EBF /* KeychainStore.swift in Sources */,
|
||||
B1B5575220EE6C4D00210D55 /* RoomKeyRequestViewController.m in Sources */,
|
||||
32A6001A22C661100042C1D9 /* EditHistoryCoordinator.swift in Sources */,
|
||||
B1A15BDB25A6680E00BDCA36 /* SSOURLConstants.swift in Sources */,
|
||||
F083BD1E1E7009ED00A9B29C /* LegacyAppDelegate.m in Sources */,
|
||||
B1B558E620EF768F00210D55 /* RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m in Sources */,
|
||||
B1B4E9BD24D4701F004D5C33 /* BubbleCellReactionsDisplayable.swift in Sources */,
|
||||
329E746722CD02EA006F9797 /* BubbleReactionActionViewCell.swift in Sources */,
|
||||
B1098BFB21ECFE65000DDA48 /* KeyBackupSetupCoordinatorType.swift in Sources */,
|
||||
B1098BF721ECFE65000DDA48 /* PasswordStrength.swift in Sources */,
|
||||
B14EED19257992D700448735 /* SocialLoginListView.swift in Sources */,
|
||||
EC711B8424A63B37008F830C /* SecretsSetupRecoveryPassphraseInputMode.swift in Sources */,
|
||||
B1B336BE242B933700F95EC4 /* KeyVerificationSelfVerifyStartViewState.swift in Sources */,
|
||||
B1BEE73423DF44A60003A4CB /* UserVerificationSessionsStatusViewModelType.swift in Sources */,
|
||||
|
@ -6738,6 +6839,7 @@
|
|||
EC711B7F24A63B37008F830C /* SecretsSetupRecoveryPassphraseViewController.swift in Sources */,
|
||||
B1B5575120EE6C4D00210D55 /* AuthenticationViewController.m in Sources */,
|
||||
B1CE83BA2422815C00D07506 /* KeyVerificationService.swift in Sources */,
|
||||
B16DC22B2587DC35004DAB1A /* SSOAuthenticationService.swift in Sources */,
|
||||
B1B5571820EE6C4D00210D55 /* CountryPickerViewController.m in Sources */,
|
||||
B1D211E622C194A200D939BD /* ReactionsMenuViewState.swift in Sources */,
|
||||
ECF57A4925090C23004BBF9D /* EnterNewRoomDetailsViewState.swift in Sources */,
|
||||
|
@ -6746,6 +6848,7 @@
|
|||
ECF57A85250A64F0004BBF9D /* PlaceholderedTextView.swift in Sources */,
|
||||
B1B4E9C424D47207004D5C33 /* BubbleReactionsViewModelBuilder.swift in Sources */,
|
||||
EC85D72A2477DCF2002C44C9 /* KeyVerificationManuallyVerifyViewModel.swift in Sources */,
|
||||
B12742BC258C472800731DA6 /* LegacySSOAuthentificationSession.swift in Sources */,
|
||||
B1BEE74A23E093260003A4CB /* UserVerificationSessionStatusCoordinatorType.swift in Sources */,
|
||||
3232ABA4225730E100AD6A5C /* DeviceVerificationStartViewAction.swift in Sources */,
|
||||
B1550FCC2420E8F500CE097B /* QRCodeGenerator.swift in Sources */,
|
||||
|
@ -6836,6 +6939,7 @@
|
|||
ECB101322477CFDB00CF8C11 /* UIDevice.swift in Sources */,
|
||||
B169330320F3C98900746532 /* RoomBubbleCellData.m in Sources */,
|
||||
EC1CA86224C1DEC400DE9EBF /* EnterPinCodeViewState.swift in Sources */,
|
||||
B12742BA258C46DD00731DA6 /* SSOAuthentificationSessionProtocol.swift in Sources */,
|
||||
EC711B8A24A63B37008F830C /* SecretsRecoveryWithPassphraseViewController.swift in Sources */,
|
||||
B1B336C3242B933700F95EC4 /* KeyVerificationSelfVerifyStartCoordinator.swift in Sources */,
|
||||
B1B557CC20EF5D8000210D55 /* DirectoryServerTableViewCell.m in Sources */,
|
||||
|
@ -6856,7 +6960,9 @@
|
|||
EC85D73E2477DDD0002C44C9 /* PushNotificationService.m in Sources */,
|
||||
32BF995121FA29DC00698084 /* SettingsKeyBackupViewModelType.swift in Sources */,
|
||||
B190F55922CE356800AEB493 /* EditHistoryHeaderView.swift in Sources */,
|
||||
B1A15BD925A6652800BDCA36 /* DeepLinkOption.swift in Sources */,
|
||||
32F6B96A2270623100BBA352 /* KeyVerificationDataLoadingViewState.swift in Sources */,
|
||||
B1945CC9258C04F30020D8D6 /* SSOAuthenticationPresenter.swift in Sources */,
|
||||
32BF995321FA2A1300698084 /* SettingsKeyBackupViewState.swift in Sources */,
|
||||
ECFBD5FB250FA59B00DD5F5A /* RoomCreationEventsModalViewAction.swift in Sources */,
|
||||
B18DEDDB243377C10075FEF7 /* KeyVerificationSelfVerifyWaitViewAction.swift in Sources */,
|
||||
|
|
6
Riot/Assets/Images.xcassets/Authentication/Contents.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "social_login_button_apple.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "social_login_button_apple@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "social_login_button_apple@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 555 B |
After Width: | Height: | Size: 983 B |
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "social_login_button_facebook.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "social_login_button_facebook@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "social_login_button_facebook@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "social_login_button_github.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "social_login_button_github@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "social_login_button_github@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 399 B |
After Width: | Height: | Size: 698 B |
After Width: | Height: | Size: 1,015 B |
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "social_login_button_google.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "social_login_button_google@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "social_login_button_google@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 5.4 KiB |
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "social_login_button_twitter.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "social_login_button_twitter@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "social_login_button_twitter@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 368 B |
After Width: | Height: | Size: 686 B |
After Width: | Height: | Size: 1 KiB |
|
@ -139,6 +139,16 @@
|
|||
"auth_softlogout_clear_data_sign_out_msg" = "Are you sure you want to clear all data currently stored on this device? Sign in again to access your account data and messages.";
|
||||
"auth_softlogout_clear_data_sign_out" = "Sign out";
|
||||
|
||||
// Social login
|
||||
|
||||
"social_login_list_title_continue" = "Continue with";
|
||||
"social_login_list_title_sign_in" = "Or login with";
|
||||
"social_login_list_title_sign_up" = "Or register with";
|
||||
|
||||
"social_login_button_title_continue" = "Continue with %@";
|
||||
"social_login_button_title_sign_in" = "Sign In with %@";
|
||||
"social_login_button_title_sign_up" = "Sign Up with %@";
|
||||
|
||||
// Errors
|
||||
"error_user_already_logged_in" = "It looks like you’re trying to connect to another homeserver. Do you want to sign out?";
|
||||
|
||||
|
|
|
@ -57,6 +57,31 @@ extension UIImage {
|
|||
return newImage
|
||||
}
|
||||
|
||||
// Based on https://stackoverflow.com/a/31314494
|
||||
@objc func vc_resized(with targetSize: CGSize) -> UIImage? {
|
||||
let size = self.size
|
||||
|
||||
let widthRatio = targetSize.width/size.width
|
||||
let heightRatio = targetSize.height/size.height
|
||||
|
||||
// Figure out what our orientation is, and use that to form the rectangle
|
||||
let newSize: CGSize
|
||||
if widthRatio > heightRatio {
|
||||
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
|
||||
} else {
|
||||
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
|
||||
}
|
||||
|
||||
let rect = CGRect(origin: .zero, size: newSize)
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
|
||||
draw(in: rect)
|
||||
let newImage = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
return newImage
|
||||
}
|
||||
|
||||
@objc func vc_notRenderedImage() -> UIImage {
|
||||
if let cgImage = cgImage {
|
||||
return NotRenderedImage(cgImage: cgImage, scale: UIScreen.main.scale, orientation: .up)
|
||||
|
|
28
Riot/Categories/URLComponents.swift
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
|
||||
extension URLComponents {
|
||||
|
||||
func vc_getQueryItem(with name: String) -> URLQueryItem? {
|
||||
return self.queryItems?.first(where: { $0.name == name })
|
||||
}
|
||||
|
||||
func vc_getQueryItemValue(for name: String) -> String? {
|
||||
return self.vc_getQueryItem(with: name)?.value
|
||||
}
|
||||
}
|
|
@ -20,6 +20,11 @@ internal typealias AssetImageTypeAlias = ImageAsset.Image
|
|||
// swiftlint:disable identifier_name line_length nesting type_body_length type_name
|
||||
internal enum Asset {
|
||||
internal enum Images {
|
||||
internal static let socialLoginButtonApple = ImageAsset(name: "social_login_button_apple")
|
||||
internal static let socialLoginButtonFacebook = ImageAsset(name: "social_login_button_facebook")
|
||||
internal static let socialLoginButtonGithub = ImageAsset(name: "social_login_button_github")
|
||||
internal static let socialLoginButtonGoogle = ImageAsset(name: "social_login_button_google")
|
||||
internal static let socialLoginButtonTwitter = ImageAsset(name: "social_login_button_twitter")
|
||||
internal static let callAudioMuteOffIcon = ImageAsset(name: "call_audio_mute_off_icon")
|
||||
internal static let callAudioMuteOnIcon = ImageAsset(name: "call_audio_mute_on_icon")
|
||||
internal static let callChatIcon = ImageAsset(name: "call_chat_icon")
|
||||
|
|
|
@ -4266,6 +4266,30 @@ internal enum VectorL10n {
|
|||
internal static var skip: String {
|
||||
return VectorL10n.tr("Vector", "skip")
|
||||
}
|
||||
/// Continue with %@
|
||||
internal static func socialLoginButtonTitleContinue(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "social_login_button_title_continue", p1)
|
||||
}
|
||||
/// Sign In with %@
|
||||
internal static func socialLoginButtonTitleSignIn(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "social_login_button_title_sign_in", p1)
|
||||
}
|
||||
/// Sign Up with %@
|
||||
internal static func socialLoginButtonTitleSignUp(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "social_login_button_title_sign_up", p1)
|
||||
}
|
||||
/// Continue with
|
||||
internal static var socialLoginListTitleContinue: String {
|
||||
return VectorL10n.tr("Vector", "social_login_list_title_continue")
|
||||
}
|
||||
/// Or login with
|
||||
internal static var socialLoginListTitleSignIn: String {
|
||||
return VectorL10n.tr("Vector", "social_login_list_title_sign_in")
|
||||
}
|
||||
/// Or register with
|
||||
internal static var socialLoginListTitleSignUp: String {
|
||||
return VectorL10n.tr("Vector", "social_login_list_title_sign_up")
|
||||
}
|
||||
/// Start
|
||||
internal static var start: String {
|
||||
return VectorL10n.tr("Vector", "start")
|
||||
|
|
36
Riot/Managers/Theme/ThemeIdentifier.swift
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
|
||||
enum ThemeIdentifier: String, RawRepresentable {
|
||||
case light = "default"
|
||||
case dark = "dark"
|
||||
case black = "black"
|
||||
|
||||
init?(rawValue: String) {
|
||||
switch rawValue {
|
||||
case "default":
|
||||
self = .light
|
||||
case "dark":
|
||||
self = .dark
|
||||
case "black":
|
||||
self = .black
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
27
Riot/Managers/Theme/ThemeService.swift
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
|
||||
extension ThemeService {
|
||||
|
||||
var themeIdentifier: ThemeIdentifier? {
|
||||
guard let themeId = self.themeId else {
|
||||
return nil
|
||||
}
|
||||
return ThemeIdentifier(rawValue: themeId)
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ class BlackTheme: DarkTheme {
|
|||
|
||||
override init() {
|
||||
super.init()
|
||||
self.identifier = "black"
|
||||
self.identifier = ThemeIdentifier.black.rawValue
|
||||
self.backgroundColor = UIColor(rgb: 0x000000)
|
||||
self.baseColor = UIColor(rgb: 0x000000)
|
||||
self.headerBackgroundColor = UIColor(rgb: 0x000000)
|
||||
|
|
|
@ -21,7 +21,7 @@ import UIKit
|
|||
@objcMembers
|
||||
class DarkTheme: NSObject, Theme {
|
||||
|
||||
var identifier: String = "dark"
|
||||
var identifier: String = ThemeIdentifier.dark.rawValue
|
||||
|
||||
var backgroundColor: UIColor = UIColor(rgb: 0x15191E)
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import UIKit
|
|||
@objcMembers
|
||||
class DefaultTheme: NSObject, Theme {
|
||||
|
||||
var identifier: String = "default"
|
||||
var identifier: String = ThemeIdentifier.light.rawValue
|
||||
|
||||
var backgroundColor: UIColor = UIColor(rgb: 0xFFFFFF)
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ final class AppCoordinator: NSObject, AppCoordinatorType {
|
|||
// MARK: - Constants
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private let customSchemeURLParser: CustomSchemeURLParser
|
||||
|
||||
// MARK: Private
|
||||
|
||||
|
@ -48,6 +50,7 @@ final class AppCoordinator: NSObject, AppCoordinatorType {
|
|||
|
||||
init(router: RootRouterType) {
|
||||
self.rootRouter = router
|
||||
self.customSchemeURLParser = CustomSchemeURLParser()
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
@ -55,6 +58,18 @@ final class AppCoordinator: NSObject, AppCoordinatorType {
|
|||
func start() {
|
||||
self.showSplitView(session: self.mainSession)
|
||||
}
|
||||
|
||||
func open(url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
|
||||
// NOTE: As said in the Apple documentation be careful on security issues with Custom Scheme URL (see https://developer.apple.com/documentation/xcode/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app)
|
||||
|
||||
do {
|
||||
let deepLinkOption = try self.customSchemeURLParser.parse(url: url, options: options)
|
||||
return self.handleDeepLinkOption(deepLinkOption)
|
||||
} catch {
|
||||
NSLog("[AppCoordinator] Custom scheme URL parsing failed with error: \(error)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
|
@ -86,6 +101,18 @@ final class AppCoordinator: NSObject, AppCoordinatorType {
|
|||
// FIXME: Present an error on coordinator.toPresentable()
|
||||
self.legacyAppDelegate.showError(asAlert: error)
|
||||
}
|
||||
|
||||
private func handleDeepLinkOption(_ deepLinkOption: DeepLinkOption) -> Bool {
|
||||
|
||||
let canOpenLink: Bool
|
||||
|
||||
switch deepLinkOption {
|
||||
case .connect(let loginToken, let transactionId):
|
||||
canOpenLink = self.legacyAppDelegate.continueSSOLogin(withToken: loginToken, txnId: transactionId)
|
||||
}
|
||||
|
||||
return canOpenLink
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - LegacyAppDelegateDelegate
|
||||
|
|
|
@ -18,4 +18,6 @@ import Foundation
|
|||
|
||||
/// `AppCoordinatorType` is a protocol describing a Coordinator that handles application navigation flow.
|
||||
protocol AppCoordinatorType: Coordinator {
|
||||
|
||||
func open(url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool
|
||||
}
|
||||
|
|
|
@ -101,6 +101,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
self.legacyAppDelegate.applicationDidReceiveMemoryWarning(application)
|
||||
}
|
||||
|
||||
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
|
||||
return self.appCoordinator.open(url: url, options: options)
|
||||
}
|
||||
|
||||
// MARK: User Activity Continuation
|
||||
|
||||
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
|
||||
|
|
|
@ -243,6 +243,14 @@ extern NSString *const AppDelegateUniversalLinkDidChangeNotification;
|
|||
*/
|
||||
- (void)checkAppVersion;
|
||||
|
||||
#pragma mark - Authentication
|
||||
|
||||
/// When SSO login succeeded, when SFSafariViewController is used, continue login with success parameters.
|
||||
/// @param loginToken The login token provided when SSO succeeded.
|
||||
/// @param txnId transaction id generated during SSO page presentation.
|
||||
/// returns YES if the SSO login can be continued.
|
||||
- (BOOL)continueSSOLoginWithToken:(NSString*)loginToken txnId:(NSString*)txnId;
|
||||
|
||||
@end
|
||||
|
||||
@protocol LegacyAppDelegateDelegate <NSObject>
|
||||
|
|
|
@ -4649,4 +4649,19 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark - Authentication
|
||||
|
||||
- (BOOL)continueSSOLoginWithToken:(NSString*)loginToken txnId:(NSString*)txnId
|
||||
{
|
||||
AuthenticationViewController *authVC = self.masterTabBarController.authViewController;
|
||||
|
||||
if (!authVC)
|
||||
{
|
||||
NSLog(@"[AppDelegate] Fail to continue SSO login");
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [authVC continueSSOLoginWithToken:loginToken txnId:txnId];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -55,6 +55,12 @@
|
|||
|
||||
- (void)showCustomHomeserver:(NSString*)homeserver andIdentityServer:(NSString*)identityServer;
|
||||
|
||||
/// When SSO login succeeded, when SFSafariViewController is used, continue login with success parameters.
|
||||
/// @param loginToken The login token provided when SSO succeeded.
|
||||
/// @param txnId transaction id generated during SSO page presentation.
|
||||
/// returns YES if the SSO login can be continued.
|
||||
- (BOOL)continueSSOLoginWithToken:(NSString*)loginToken txnId:(NSString*)txnId;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
|
|
@ -27,7 +27,12 @@
|
|||
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
@interface AuthenticationViewController () <AuthFallBackViewControllerDelegate, KeyVerificationCoordinatorBridgePresenterDelegate, SetPinCoordinatorBridgePresenterDelegate>
|
||||
static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
|
||||
|
||||
@interface AuthenticationViewController () <AuthFallBackViewControllerDelegate, KeyVerificationCoordinatorBridgePresenterDelegate, SetPinCoordinatorBridgePresenterDelegate,
|
||||
SocialLoginListViewDelegate,
|
||||
SSOAuthenticationPresenterDelegate
|
||||
>
|
||||
{
|
||||
/**
|
||||
The default country code used to initialize the mobile phone number input.
|
||||
|
@ -63,6 +68,17 @@
|
|||
@property (nonatomic, strong) SetPinCoordinatorBridgePresenter *setPinCoordinatorBridgePresenter;
|
||||
@property (nonatomic, strong) KeyboardAvoider *keyboardAvoider;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *socialLoginContainerView;
|
||||
@property (nonatomic, weak) SocialLoginListView *socialLoginListView;
|
||||
|
||||
@property (nonatomic, strong) SSOAuthenticationPresenter *ssoAuthenticationPresenter;
|
||||
|
||||
// Current SSO flow containing Identity Providers. Used for `socialLoginListView`
|
||||
@property (nonatomic, strong) MXLoginSSOFlow *currentLoginSSOFlow;
|
||||
|
||||
// Current SSO transaction id used to identify and validate the SSO authentication callback
|
||||
@property (nonatomic, strong) NSString *ssoCallbackTxnId;
|
||||
|
||||
@end
|
||||
|
||||
@implementation AuthenticationViewController
|
||||
|
@ -377,8 +393,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
[self updateAuthInputViewVisibility];
|
||||
[self updateForgotPwdButtonVisibility];
|
||||
[self updateSoftLogoutClearDataContainerVisibility];
|
||||
[self updateSocialLoginViewVisibility];
|
||||
}
|
||||
|
||||
- (void)setAuthInputsView:(MXKAuthInputsView *)authInputsView
|
||||
|
@ -422,6 +440,21 @@
|
|||
[self refreshContentViewHeightConstraint];
|
||||
}
|
||||
|
||||
- (void)updateAuthInputViewVisibility
|
||||
{
|
||||
BOOL hideAuthInputView = NO;
|
||||
|
||||
// Hide input view when there is only social login actions to present
|
||||
if ((self.authType == MXKAuthenticationTypeLogin || self.authType == MXKAuthenticationTypeRegister)
|
||||
&& self.currentLoginSSOFlow
|
||||
&& !self.isAuthSessionContainsPasswordFlow)
|
||||
{
|
||||
hideAuthInputView = YES;
|
||||
}
|
||||
|
||||
self.authInputsView.hidden = hideAuthInputView;
|
||||
}
|
||||
|
||||
- (void)setUserInteractionEnabled:(BOOL)userInteractionEnabled
|
||||
{
|
||||
super.userInteractionEnabled = userInteractionEnabled;
|
||||
|
@ -528,6 +561,21 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (BOOL)continueSSOLoginWithToken:(NSString*)loginToken txnId:(NSString*)txnId
|
||||
{
|
||||
// Check if transaction id is the same as expected
|
||||
if (loginToken &&
|
||||
txnId && self.ssoCallbackTxnId
|
||||
&& [txnId isEqualToString:self.ssoCallbackTxnId])
|
||||
{
|
||||
[self loginWithToken:loginToken];
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSLog(@"[AuthenticationVC] Fail to continue SSO login");
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - Fallback URL display
|
||||
|
||||
- (void)showAuthenticationFallBackView:(NSString*)fallbackPage
|
||||
|
@ -692,7 +740,8 @@
|
|||
*/
|
||||
- (MXAuthenticationSession*)handleSupportedFlowsInAuthenticationSession:(MXAuthenticationSession *)authSession
|
||||
{
|
||||
MXLoginFlow *ssoFlow;
|
||||
MXLoginSSOFlow *ssoFlow;
|
||||
MXLoginFlow *passwordFlow;
|
||||
NSMutableArray *supportedFlows = [NSMutableArray array];
|
||||
|
||||
for (MXLoginFlow *flow in authSession.flows)
|
||||
|
@ -704,20 +753,30 @@
|
|||
[supportedFlows addObject:flow];
|
||||
}
|
||||
|
||||
// Prioritise SSO over other flows
|
||||
if ([flow.type isEqualToString:kMXLoginFlowTypeSSO]
|
||||
|| [flow.type isEqualToString:kMXLoginFlowTypeCAS])
|
||||
if ([flow.type isEqualToString:kMXLoginFlowTypePassword])
|
||||
{
|
||||
passwordFlow = flow;
|
||||
}
|
||||
|
||||
if ([flow isKindOfClass:MXLoginSSOFlow.class])
|
||||
{
|
||||
NSLog(@"[AuthenticationVC] handleSupportedFlowsInAuthenticationSession: Prioritise flow %@", flow.type);
|
||||
ssoFlow = flow;
|
||||
break;
|
||||
ssoFlow = (MXLoginSSOFlow *)flow;
|
||||
}
|
||||
}
|
||||
|
||||
// Prioritise SSO over other flows
|
||||
if (ssoFlow)
|
||||
{
|
||||
[supportedFlows removeAllObjects];
|
||||
[supportedFlows addObject:ssoFlow];
|
||||
|
||||
// If the SSO contains Identity Providers list and password
|
||||
// Display both social login and password input
|
||||
if (ssoFlow.identityProviders.count && passwordFlow)
|
||||
{
|
||||
[supportedFlows addObject:passwordFlow];
|
||||
}
|
||||
}
|
||||
|
||||
if (supportedFlows.count != authSession.flows.count)
|
||||
|
@ -740,7 +799,12 @@
|
|||
authSession = [self handleSupportedFlowsInAuthenticationSession:authSession];
|
||||
|
||||
[super handleAuthenticationSession:authSession];
|
||||
|
||||
|
||||
self.currentLoginSSOFlow = [self logginSSOFlowWithProvidersFromFlows:authSession.flows];
|
||||
|
||||
[self updateAuthInputViewVisibility];
|
||||
[self updateSocialLoginViewVisibility];
|
||||
|
||||
AuthInputsView *authInputsview;
|
||||
if ([self.authInputsView isKindOfClass:AuthInputsView.class])
|
||||
{
|
||||
|
@ -752,7 +816,7 @@
|
|||
[self updateForgotPwdButtonVisibility];
|
||||
[self updateSoftLogoutClearDataContainerVisibility];
|
||||
|
||||
self.submitButton.hidden = authInputsview.isSingleSignOnRequired;
|
||||
self.submitButton.hidden = authInputsview.isSingleSignOnRequired || authInputsview.isHidden;
|
||||
|
||||
// Bind ssoButton again if self.authInputsView has changed
|
||||
[authInputsview.ssoButton addTarget:self action:@selector(onButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
@ -763,6 +827,54 @@
|
|||
// That makes softLogoutClearDataContainer appear upper in the screen
|
||||
[self.submitButton removeFromSuperview];
|
||||
}
|
||||
|
||||
[self refreshContentViewHeightConstraint];
|
||||
}
|
||||
|
||||
- (BOOL)isAuthSessionContainsPasswordFlow
|
||||
{
|
||||
BOOL containsPassword = NO;
|
||||
|
||||
if (self.authInputsView.authSession)
|
||||
{
|
||||
containsPassword = [self containsPasswordFlowInFlows:self.authInputsView.authSession.flows];
|
||||
}
|
||||
|
||||
return containsPassword;
|
||||
}
|
||||
|
||||
- (BOOL)containsPasswordFlowInFlows:(NSArray<MXLoginFlow*>*)loginFlows
|
||||
{
|
||||
for (MXLoginFlow *loginFlow in loginFlows)
|
||||
{
|
||||
if ([loginFlow.type isEqualToString:kMXLoginFlowTypePassword])
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (MXLoginSSOFlow*)logginSSOFlowWithProvidersFromFlows:(NSArray<MXLoginFlow*>*)loginFlows
|
||||
{
|
||||
MXLoginSSOFlow *ssoFlowWithProviders;
|
||||
|
||||
for (MXLoginFlow *loginFlow in loginFlows)
|
||||
{
|
||||
if ([loginFlow isKindOfClass:MXLoginSSOFlow.class])
|
||||
{
|
||||
MXLoginSSOFlow *ssoFlow = (MXLoginSSOFlow *)loginFlow;
|
||||
|
||||
if (ssoFlow.identityProviders.count)
|
||||
{
|
||||
ssoFlowWithProviders = ssoFlow;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ssoFlowWithProviders;
|
||||
}
|
||||
|
||||
- (IBAction)onButtonPressed:(id)sender
|
||||
|
@ -890,9 +1002,8 @@
|
|||
[super onButtonPressed:self.submitButton];
|
||||
}
|
||||
else if (sender == ((AuthInputsView*)self.authInputsView).ssoButton)
|
||||
{
|
||||
// Do SSO using the fallback URL
|
||||
[self showAuthenticationFallBackView];
|
||||
{
|
||||
[self presentDefaultSSOAuthentication];
|
||||
}
|
||||
else if (sender == self.softLogoutClearDataButton)
|
||||
{
|
||||
|
@ -958,7 +1069,7 @@
|
|||
|
||||
BOOL showForgotPasswordButton = NO;
|
||||
|
||||
if (BuildSettings.authScreenShowForgotPassword)
|
||||
if (BuildSettings.authScreenShowForgotPassword && authInputsview.isHidden == NO)
|
||||
{
|
||||
showForgotPasswordButton = (self.authType == MXKAuthenticationTypeLogin) && !authInputsview.isSingleSignOnRequired;
|
||||
}
|
||||
|
@ -1054,6 +1165,17 @@
|
|||
// Refresh content view height by considering the options container display.
|
||||
CGFloat constant = self.optionsContainer.frame.origin.y + 10;
|
||||
|
||||
if (self.authInputsView.isHidden == NO)
|
||||
{
|
||||
self.authInputContainerViewMinHeightConstraint.constant = kAuthInputContainerViewMinHeightConstraintConstant;
|
||||
self.authInputContainerViewHeightConstraint.constant = self.authInputsView.viewHeightConstraint.constant;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.authInputContainerViewMinHeightConstraint.constant = 0;
|
||||
self.authInputContainerViewHeightConstraint.constant = 0;
|
||||
}
|
||||
|
||||
if (!self.optionsContainer.isHidden)
|
||||
{
|
||||
constant += self.serverOptionsContainer.frame.origin.y;
|
||||
|
@ -1079,8 +1201,15 @@
|
|||
// The soft logout clear data section adds more height
|
||||
constant += self.softLogoutClearDataContainer.frame.size.height;
|
||||
}
|
||||
|
||||
if (self.isSocialLoginViewShown)
|
||||
{
|
||||
constant += [self socialLoginViewHeightFittingWidth:self.contentView.frame.size.width];
|
||||
}
|
||||
|
||||
self.contentViewHeightConstraint.constant = constant;
|
||||
|
||||
[self.view layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)hideCustomServers:(BOOL)hidden
|
||||
|
@ -1148,7 +1277,7 @@
|
|||
self.customServersContainer.hidden = NO;
|
||||
|
||||
// Refresh content view height
|
||||
self.contentViewHeightConstraint.constant += self.customServersContainer.frame.size.height;
|
||||
[self refreshContentViewHeightConstraint];
|
||||
|
||||
// Scroll to display server options
|
||||
CGPoint offset = self.authenticationScrollView.contentOffset;
|
||||
|
@ -1186,11 +1315,6 @@
|
|||
// Override here the handling of the authInputsView height change.
|
||||
if ([@"viewHeightConstraint.constant" isEqualToString:keyPath])
|
||||
{
|
||||
self.authInputContainerViewHeightConstraint.constant = self.authInputsView.viewHeightConstraint.constant;
|
||||
|
||||
// Force to render the view
|
||||
[self.view layoutIfNeeded];
|
||||
|
||||
// Refresh content view height by considering the updated frame of the options container.
|
||||
[self refreshContentViewHeightConstraint];
|
||||
}
|
||||
|
@ -1496,4 +1620,159 @@
|
|||
self.setPinCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Social login view management
|
||||
|
||||
- (BOOL)isSocialLoginViewShown
|
||||
{
|
||||
return self.socialLoginListView.superview
|
||||
&& !self.socialLoginListView.isHidden
|
||||
&& self.currentLoginSSOFlow.identityProviders.count;
|
||||
}
|
||||
|
||||
- (CGFloat)socialLoginViewHeightFittingWidth:(CGFloat)width
|
||||
{
|
||||
NSArray<MXLoginSSOIdentityProvider*> *identityProviders = self.currentLoginSSOFlow.identityProviders;
|
||||
|
||||
if (!identityProviders.count && self.socialLoginListView)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return [SocialLoginListView contentViewHeightWithIdentityProviders:identityProviders mode:self.socialLoginListView.mode fitting:self.contentView.frame.size.width];
|
||||
}
|
||||
|
||||
- (void)showSocialLoginViewWithLoginSSOFlow:(MXLoginSSOFlow*)loginSSOFlow andMode:(SocialLoginButtonMode)mode
|
||||
{
|
||||
SocialLoginListView *listView = self.socialLoginListView;
|
||||
|
||||
if (!listView)
|
||||
{
|
||||
listView = [SocialLoginListView instantiate];
|
||||
[self.socialLoginContainerView vc_addSubViewMatchingParent:listView];
|
||||
self.socialLoginListView = listView;
|
||||
listView.delegate = self;
|
||||
}
|
||||
|
||||
[listView updateWith:loginSSOFlow.identityProviders mode:mode];
|
||||
|
||||
[self refreshContentViewHeightConstraint];
|
||||
}
|
||||
|
||||
- (void)hideSocialLoginView
|
||||
{
|
||||
[self.socialLoginListView removeFromSuperview];
|
||||
[self refreshContentViewHeightConstraint];
|
||||
}
|
||||
|
||||
- (void)updateSocialLoginViewVisibility
|
||||
{
|
||||
SocialLoginButtonMode socialLoginButtonMode = SocialLoginButtonModeContinue;
|
||||
|
||||
BOOL showSocialLoginView = self.currentLoginSSOFlow ? YES : NO;
|
||||
|
||||
switch (self.authType)
|
||||
{
|
||||
case MXKAuthenticationTypeForgotPassword:
|
||||
showSocialLoginView = NO;
|
||||
break;
|
||||
case MXKAuthenticationTypeRegister:
|
||||
socialLoginButtonMode = SocialLoginButtonModeSignUp;
|
||||
break;
|
||||
case MXKAuthenticationTypeLogin:
|
||||
if (((AuthInputsView*)self.authInputsView).isSingleSignOnRequired)
|
||||
{
|
||||
socialLoginButtonMode = SocialLoginButtonModeContinue;
|
||||
}
|
||||
else
|
||||
{
|
||||
socialLoginButtonMode = SocialLoginButtonModeSignIn;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (showSocialLoginView)
|
||||
{
|
||||
[self showSocialLoginViewWithLoginSSOFlow:self.currentLoginSSOFlow andMode:socialLoginButtonMode];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self hideSocialLoginView];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - SocialLoginListViewDelegate
|
||||
|
||||
- (void)socialLoginListView:(SocialLoginListView *)socialLoginListView didTapSocialButtonWithIdentifier:(NSString *)identifier
|
||||
{
|
||||
[self presentSSOAuthenticationForIdentityProviderIdentifier:identifier];
|
||||
}
|
||||
|
||||
#pragma mark - SSOIdentityProviderAuthenticationPresenter
|
||||
|
||||
- (void)presentSSOAuthenticationForIdentityProviderIdentifier:(NSString*)identityProviderIdentifier
|
||||
{
|
||||
NSString *homeServerStringURL = self.homeServerTextField.text;
|
||||
|
||||
if (!homeServerStringURL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SSOAuthenticationService *ssoAuthenticationService = [[SSOAuthenticationService alloc] initWithHomeserverStringURL:homeServerStringURL];
|
||||
|
||||
SSOAuthenticationPresenter *presenter = [[SSOAuthenticationPresenter alloc] initWithSsoAuthenticationService:ssoAuthenticationService];
|
||||
|
||||
presenter.delegate = self;
|
||||
|
||||
// Generate a unique identifier that will identify the success callback URL
|
||||
NSString *transactionId = [MXTools generateTransactionId];
|
||||
|
||||
[presenter presentForIdentityProviderIdentifier:identityProviderIdentifier with: transactionId from:self animated:YES];
|
||||
|
||||
self.ssoCallbackTxnId = transactionId;
|
||||
self.ssoAuthenticationPresenter = presenter;
|
||||
}
|
||||
|
||||
- (void)presentDefaultSSOAuthentication
|
||||
{
|
||||
[self presentSSOAuthenticationForIdentityProviderIdentifier:nil];
|
||||
}
|
||||
|
||||
- (void)dismissSSOAuthenticationPresenter
|
||||
{
|
||||
[self.ssoAuthenticationPresenter dismissWithAnimated:YES completion:nil];
|
||||
self.ssoAuthenticationPresenter = nil;
|
||||
}
|
||||
|
||||
// TODO: Move to SDK
|
||||
- (void)loginWithToken:(NSString*)loginToken
|
||||
{
|
||||
NSDictionary *parameters = @{
|
||||
@"type" : kMXLoginFlowTypeToken,
|
||||
@"token": loginToken
|
||||
};
|
||||
|
||||
[self loginWithParameters:parameters];
|
||||
}
|
||||
|
||||
#pragma mark - SSOAuthenticationPresenterDelegate
|
||||
|
||||
- (void)ssoAuthenticationPresenterDidCancel:(SSOAuthenticationPresenter *)presenter
|
||||
{
|
||||
[self dismissSSOAuthenticationPresenter];
|
||||
}
|
||||
|
||||
- (void)ssoAuthenticationPresenter:(SSOAuthenticationPresenter *)presenter authenticationDidFailWithError:(NSError *)error
|
||||
{
|
||||
[self dismissSSOAuthenticationPresenter];
|
||||
}
|
||||
|
||||
- (void)ssoAuthenticationPresenter:(SSOAuthenticationPresenter *)presenter authenticationSucceededWithToken:(NSString *)token
|
||||
{
|
||||
[self dismissSSOAuthenticationPresenter];
|
||||
[self loginWithToken:token];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina5_9" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17126"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
|
@ -42,6 +42,7 @@
|
|||
<outlet property="rightBarButtonItem" destination="Kwt-KN-aVL" id="Y3F-wA-tf8"/>
|
||||
<outlet property="serverOptionsContainer" destination="FIn-2w-e6H" id="Z0e-NN-3LY"/>
|
||||
<outlet property="skipButton" destination="wEJ-AF-rdH" id="smu-MS-2IY"/>
|
||||
<outlet property="socialLoginContainerView" destination="TjK-XL-dQS" id="Rte-h1-blp"/>
|
||||
<outlet property="softLogoutClearDataButton" destination="ZRO-C2-hH1" id="PDa-aM-Hso"/>
|
||||
<outlet property="softLogoutClearDataContainer" destination="vX2-5Y-rQc" id="mgK-41-cnX"/>
|
||||
<outlet property="softLogoutClearDataLabel" destination="QYL-Lo-tmH" id="ks9-5X-xfs"/>
|
||||
|
@ -111,7 +112,7 @@
|
|||
<color key="textColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wIH-Kd-r7q" userLabel="retryButton">
|
||||
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wIH-Kd-r7q" userLabel="retryButton">
|
||||
<rect key="frame" x="165" y="46.666666666666686" width="45" height="30"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCRetryButton"/>
|
||||
<constraints>
|
||||
|
@ -144,7 +145,7 @@
|
|||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Gg0-TE-OGb">
|
||||
<rect key="frame" x="0.0" y="360" width="375" height="300"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" hasAttributedTitle="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AJ2-lJ-NUq">
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" hasAttributedTitle="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AJ2-lJ-NUq">
|
||||
<rect key="frame" x="19" y="33" width="122" height="30"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCForgotPasswordButton"/>
|
||||
<constraints>
|
||||
|
@ -164,7 +165,7 @@
|
|||
<action selector="onButtonPressed:" destination="-1" eventType="touchUpInside" id="UVJ-Re-xe2"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wEJ-AF-rdH">
|
||||
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wEJ-AF-rdH">
|
||||
<rect key="frame" x="11" y="33" width="106" height="30"/>
|
||||
<color key="backgroundColor" red="0.028153735480000001" green="0.82494870580000002" blue="0.051896891280000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCSkipButton"/>
|
||||
|
@ -181,7 +182,7 @@
|
|||
<action selector="onButtonPressed:" destination="-1" eventType="touchUpInside" id="iEr-Vf-f6P"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="k3J-Eg-itz" userLabel="SubmitBtn">
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="k3J-Eg-itz" userLabel="SubmitBtn">
|
||||
<rect key="frame" x="258" y="33" width="106" height="30"/>
|
||||
<color key="backgroundColor" red="0.028153735480000001" green="0.82494870580000002" blue="0.051896891280000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCLoginButton"/>
|
||||
|
@ -366,7 +367,7 @@ Clear it if you're finished using this device, or want to sign in to another acc
|
|||
<nil key="highlightedColor"/>
|
||||
<string key="userLabel">Clear personal data Warning: Your personal data (including encryption keys) is still stored on this device. Clear it if you're finished using this device, or want to sign in to another account.</string>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZRO-C2-hH1">
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZRO-C2-hH1">
|
||||
<rect key="frame" x="245" y="129" width="119" height="30"/>
|
||||
<color key="backgroundColor" red="0.4624713659286499" green="0.81734329462051392" blue="0.47220504283905029" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
|
@ -416,20 +417,32 @@ Clear it if you're finished using this device, or want to sign in to another acc
|
|||
<constraint firstItem="AJ2-lJ-NUq" firstAttribute="leading" secondItem="Gg0-TE-OGb" secondAttribute="leading" constant="19" id="xFm-Bs-yzw"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TjK-XL-dQS">
|
||||
<rect key="frame" x="0.0" y="660" width="375" height="0.0"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" priority="750" id="mIX-QF-Tmn"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthContentView"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="485" id="6v6-fz-e8o"/>
|
||||
<constraint firstItem="Gg0-TE-OGb" firstAttribute="width" secondItem="rhx-dD-4EJ" secondAttribute="width" id="EBX-KN-pRT"/>
|
||||
<constraint firstItem="TjK-XL-dQS" firstAttribute="leading" secondItem="rhx-dD-4EJ" secondAttribute="leading" id="MId-fr-A7b"/>
|
||||
<constraint firstAttribute="bottom" secondItem="TjK-XL-dQS" secondAttribute="bottom" id="PKr-aT-3Qe"/>
|
||||
<constraint firstItem="TjK-XL-dQS" firstAttribute="top" secondItem="Gg0-TE-OGb" secondAttribute="bottom" id="Sfy-Zn-JkS"/>
|
||||
<constraint firstItem="Gg0-TE-OGb" firstAttribute="top" secondItem="rhx-dD-4EJ" secondAttribute="top" priority="250" constant="384" id="UEM-Mh-0H9"/>
|
||||
<constraint firstItem="xWb-IJ-v7F" firstAttribute="leading" secondItem="rhx-dD-4EJ" secondAttribute="leading" id="YnP-Nk-QxR"/>
|
||||
<constraint firstItem="Gg0-TE-OGb" firstAttribute="top" secondItem="xWb-IJ-v7F" secondAttribute="bottom" id="aGH-m3-xL3"/>
|
||||
<constraint firstAttribute="trailing" secondItem="TjK-XL-dQS" secondAttribute="trailing" id="d1j-OA-Wwm"/>
|
||||
<constraint firstAttribute="trailing" secondItem="xWb-IJ-v7F" secondAttribute="trailing" id="hko-ol-XDd"/>
|
||||
<constraint firstItem="xWb-IJ-v7F" firstAttribute="top" secondItem="rhx-dD-4EJ" secondAttribute="top" constant="160" id="khR-Uj-OTH"/>
|
||||
<constraint firstItem="d8r-TX-pwX" firstAttribute="top" secondItem="rhx-dD-4EJ" secondAttribute="top" constant="35" id="l68-Ta-YKg"/>
|
||||
<constraint firstAttribute="centerX" secondItem="d8r-TX-pwX" secondAttribute="centerX" id="l6k-EH-Yb8"/>
|
||||
<constraint firstItem="Gg0-TE-OGb" firstAttribute="leading" secondItem="rhx-dD-4EJ" secondAttribute="leading" id="rS3-go-zbf"/>
|
||||
<constraint firstItem="TjK-XL-dQS" firstAttribute="width" secondItem="rhx-dD-4EJ" secondAttribute="width" id="zIZ-fl-i3i"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
|
@ -449,7 +462,7 @@ Clear it if you're finished using this device, or want to sign in to another acc
|
|||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="q1e-Wg-6t7" userLabel="Authentication Fallback ContentView">
|
||||
<rect key="frame" x="0.0" y="44" width="375" height="734"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9qj-5c-Sfb">
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9qj-5c-Sfb">
|
||||
<rect key="frame" x="317" y="5" width="50" height="35"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCCancelAuthFallbackButton"/>
|
||||
<constraints>
|
||||
|
@ -480,6 +493,7 @@ Clear it if you're finished using this device, or want to sign in to another acc
|
|||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="3h8-75-kcp"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCView"/>
|
||||
<constraints>
|
||||
|
@ -502,7 +516,6 @@ Clear it if you're finished using this device, or want to sign in to another acc
|
|||
<constraint firstItem="izX-ya-hXh" firstAttribute="top" secondItem="k7D-Gy-yBR" secondAttribute="bottom" id="y2h-1T-TZ4"/>
|
||||
<constraint firstItem="k7D-Gy-yBR" firstAttribute="leading" secondItem="5rn-KE-plm" secondAttribute="leading" id="z6D-6L-5Ud"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="3h8-75-kcp"/>
|
||||
<point key="canvasLocation" x="138.40000000000001" y="153.69458128078819"/>
|
||||
</view>
|
||||
</objects>
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
import SafariServices
|
||||
|
||||
/// LegacySSOAuthentificationSession is session used to authenticate a user through a web service on iOS 11 and earlier. It uses SFAuthenticationSession.
|
||||
final class LegacySSOAuthentificationSession: SSOAuthentificationSessionProtocol {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private var authentificationSession: SFAuthenticationSession?
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func setContextProvider(_ contextProvider: SSOAuthenticationSessionContextProviding) {
|
||||
}
|
||||
|
||||
func authenticate(with url: URL, callbackURLScheme: String?, completionHandler: @escaping SSOAuthenticationSessionCompletionHandler) {
|
||||
|
||||
let authentificationSession = SFAuthenticationSession(url: url, callbackURLScheme: callbackURLScheme) { (callbackURL, error) in
|
||||
|
||||
var finalError: Error?
|
||||
|
||||
if let error = error as? SFAuthenticationError {
|
||||
switch error.code {
|
||||
case .canceledLogin:
|
||||
finalError = SSOAuthentificationSessionError.userCanceled
|
||||
default:
|
||||
finalError = error
|
||||
}
|
||||
}
|
||||
|
||||
completionHandler(callbackURL, finalError)
|
||||
}
|
||||
|
||||
self.authentificationSession = authentificationSession
|
||||
authentificationSession.start()
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
self.authentificationSession?.cancel()
|
||||
}
|
||||
}
|
160
Riot/Modules/Authentication/SSO/SSOAuthenticationPresenter.swift
Normal file
|
@ -0,0 +1,160 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
import SafariServices
|
||||
|
||||
@objc protocol SSOAuthenticationPresenterDelegate {
|
||||
func ssoAuthenticationPresenterDidCancel(_ presenter: SSOAuthenticationPresenter)
|
||||
func ssoAuthenticationPresenter(_ presenter: SSOAuthenticationPresenter, authenticationDidFailWithError error: Error)
|
||||
func ssoAuthenticationPresenter(_ presenter: SSOAuthenticationPresenter, authenticationSucceededWithToken token: String)
|
||||
}
|
||||
|
||||
enum SSOAuthenticationPresenterError: Error {
|
||||
case failToLoadAuthenticationURL
|
||||
}
|
||||
|
||||
/// SSOAuthenticationPresenter enables to present single sign-on authentication
|
||||
@objcMembers
|
||||
final class SSOAuthenticationPresenter: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private let ssoAuthenticationService: SSOAuthenticationService
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private weak var presentingViewController: UIViewController?
|
||||
|
||||
private var authenticationSession: SSOAuthentificationSessionProtocol?
|
||||
private weak var safariViewController: SFSafariViewController?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: SSOAuthenticationPresenterDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(ssoAuthenticationService: SSOAuthenticationService) {
|
||||
self.ssoAuthenticationService = ssoAuthenticationService
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func present(forIdentityProviderIdentifier identityProviderIdentifier: String?,
|
||||
with transactionId: String,
|
||||
from presentingViewController: UIViewController,
|
||||
animated: Bool) {
|
||||
guard let authenticationURL = self.ssoAuthenticationService.authenticationURL(for: identityProviderIdentifier, transactionId: transactionId) else {
|
||||
self.delegate?.ssoAuthenticationPresenter(self, authenticationDidFailWithError: SSOAuthenticationPresenterError.failToLoadAuthenticationURL)
|
||||
return
|
||||
}
|
||||
|
||||
self.presentingViewController = presentingViewController
|
||||
|
||||
// SFAuthenticationSession and ASWebAuthenticationSession doesn't work with guided access (rdar://48376122)
|
||||
if UIAccessibility.isGuidedAccessEnabled {
|
||||
self.presentSafariViewController(with: authenticationURL, animated: animated)
|
||||
} else {
|
||||
self.startAuthenticationSession(with: authenticationURL)
|
||||
}
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
if let safariViewController = self.safariViewController {
|
||||
safariViewController.dismiss(animated: animated, completion: completion)
|
||||
}
|
||||
|
||||
self.authenticationSession?.cancel()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func presentSafariViewController(with authenticationURL: URL, animated: Bool) {
|
||||
guard let presentingViewController = self.presentingViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
let safariViewController = SFSafariViewController(url: authenticationURL)
|
||||
safariViewController.dismissButtonStyle = .cancel
|
||||
safariViewController.delegate = self
|
||||
|
||||
presentingViewController.present(safariViewController, animated: animated, completion: nil)
|
||||
self.safariViewController = safariViewController
|
||||
}
|
||||
|
||||
private func startAuthenticationSession(with authenticationURL: URL) {
|
||||
guard let presentingViewController = self.presentingViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
let authenticationSession: SSOAuthentificationSessionProtocol
|
||||
|
||||
if #available(iOS 12.0, *) {
|
||||
authenticationSession = SSOAuthentificationSession()
|
||||
} else {
|
||||
authenticationSession = LegacySSOAuthentificationSession()
|
||||
}
|
||||
|
||||
if #available(iOS 12.0, *) {
|
||||
if let presentingWindow = presentingViewController.view.window {
|
||||
let contextProvider = SSOAuthenticationSessionContextProvider(window: presentingWindow)
|
||||
authenticationSession.setContextProvider(contextProvider)
|
||||
}
|
||||
}
|
||||
|
||||
authenticationSession.authenticate(with: authenticationURL, callbackURLScheme: self.ssoAuthenticationService.callBackURLScheme) { [weak self] (callBackURL, error) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let error = error {
|
||||
if case SSOAuthentificationSessionError.userCanceled = error {
|
||||
self.delegate?.ssoAuthenticationPresenterDidCancel(self)
|
||||
} else {
|
||||
self.delegate?.ssoAuthenticationPresenter(self, authenticationDidFailWithError: error)
|
||||
}
|
||||
} else if let successURL = callBackURL {
|
||||
if let loginToken = self.ssoAuthenticationService.loginToken(from: successURL) {
|
||||
self.delegate?.ssoAuthenticationPresenter(self, authenticationSucceededWithToken: loginToken)
|
||||
} else {
|
||||
NSLog("SSOAuthenticationPresenter: Login token not found")
|
||||
self.delegate?.ssoAuthenticationPresenter(self, authenticationDidFailWithError: SSOAuthenticationServiceError.tokenNotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.authenticationSession = authenticationSession
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SFSafariViewControllerDelegate
|
||||
extension SSOAuthenticationPresenter: SFSafariViewControllerDelegate {
|
||||
|
||||
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
|
||||
self.delegate?.ssoAuthenticationPresenterDidCancel(self)
|
||||
}
|
||||
|
||||
func safariViewController(_ controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) {
|
||||
|
||||
if !didLoadSuccessfully {
|
||||
self.delegate?.ssoAuthenticationPresenter(self, authenticationDidFailWithError: SSOAuthenticationPresenterError.failToLoadAuthenticationURL)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
|
||||
enum SSOAuthenticationServiceError: Error {
|
||||
case tokenNotFound
|
||||
case userCanceled
|
||||
case unknown
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
final class SSOAuthenticationService: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private let homeserverStringURL: String
|
||||
|
||||
let callBackURLScheme: String?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(homeserverStringURL: String) {
|
||||
self.homeserverStringURL = homeserverStringURL
|
||||
self.callBackURLScheme = BuildSettings.applicationURLScheme
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func authenticationURL(for identityProvider: String?, transactionId: String) -> URL? {
|
||||
guard var authenticationComponent = URLComponents(string: self.homeserverStringURL) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let ssoRedirectPath: String
|
||||
|
||||
if let identityProvider = identityProvider {
|
||||
ssoRedirectPath = SSOURLConstants.Paths.unstableRedirect + identityProvider
|
||||
} else {
|
||||
ssoRedirectPath = SSOURLConstants.Paths.redirect
|
||||
}
|
||||
|
||||
authenticationComponent.path = ssoRedirectPath
|
||||
|
||||
var queryItems: [URLQueryItem] = []
|
||||
|
||||
if let callBackURLScheme = self.buildCallBackURL(with: transactionId) {
|
||||
queryItems.append(URLQueryItem(name: SSOURLConstants.Parameters.redirectURL, value: callBackURLScheme))
|
||||
}
|
||||
|
||||
authenticationComponent.queryItems = queryItems
|
||||
|
||||
return authenticationComponent.url
|
||||
}
|
||||
|
||||
func loginToken(from url: URL) -> String? {
|
||||
guard let components = URLComponents(string: url.absoluteString) else {
|
||||
return nil
|
||||
}
|
||||
return components.vc_getQueryItemValue(for: SSOURLConstants.Parameters.callbackLoginToken)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func buildCallBackURL(with transactionId: String) -> String? {
|
||||
guard let callBackURLScheme = self.callBackURLScheme else {
|
||||
return nil
|
||||
}
|
||||
var urlComponents = URLComponents()
|
||||
urlComponents.scheme = callBackURLScheme
|
||||
urlComponents.host = CustomSchemeURLConstants.Hosts.connect
|
||||
|
||||
// Transaction id is used to indentify the request
|
||||
urlComponents.queryItems = [URLQueryItem(name: CustomSchemeURLConstants.Parameters.transactionId, value: transactionId)]
|
||||
return urlComponents.string
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
import AuthenticationServices
|
||||
|
||||
/// Provides context to target where in an application's UI the authorization view should be shown.
|
||||
@available(iOS 12.0, *)
|
||||
class SSOAuthenticationSessionContextProvider: NSObject, SSOAuthenticationSessionContextProviding, ASWebAuthenticationPresentationContextProviding {
|
||||
let window: UIWindow
|
||||
|
||||
init(window: UIWindow) {
|
||||
self.window = window
|
||||
}
|
||||
|
||||
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
|
||||
return window
|
||||
}
|
||||
}
|
||||
|
||||
/// SSOAuthentificationSession is session used to authenticate a user through a web service on iOS 12+. It uses ASWebAuthenticationSession.
|
||||
/// More information: https://developer.apple.com/documentation/authenticationservices/authenticating_a_user_through_a_web_service
|
||||
@available(iOS 12.0, *)
|
||||
final class SSOAuthentificationSession: SSOAuthentificationSessionProtocol {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private var authentificationSession: ASWebAuthenticationSession?
|
||||
private var contextProvider: SSOAuthenticationSessionContextProviding?
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func setContextProvider(_ contextProvider: SSOAuthenticationSessionContextProviding) {
|
||||
self.contextProvider = contextProvider
|
||||
}
|
||||
|
||||
func authenticate(with url: URL, callbackURLScheme: String?, completionHandler: @escaping SSOAuthenticationSessionCompletionHandler) {
|
||||
|
||||
let authentificationSession = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackURLScheme) { (callbackURL, error) in
|
||||
|
||||
var finalError: Error?
|
||||
|
||||
if let error = error as? ASWebAuthenticationSessionError {
|
||||
switch error.code {
|
||||
case .canceledLogin:
|
||||
finalError = SSOAuthentificationSessionError.userCanceled
|
||||
default:
|
||||
finalError = error
|
||||
}
|
||||
}
|
||||
|
||||
completionHandler(callbackURL, finalError)
|
||||
}
|
||||
|
||||
// Ask the browser for a private authentication session
|
||||
if #available(iOS 13.0, *) {
|
||||
authentificationSession.prefersEphemeralWebBrowserSession = true
|
||||
}
|
||||
|
||||
self.authentificationSession = authentificationSession
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
if let asWebContextProvider = contextProvider as? ASWebAuthenticationPresentationContextProviding {
|
||||
authentificationSession.presentationContextProvider = asWebContextProvider
|
||||
}
|
||||
}
|
||||
|
||||
authentificationSession.start()
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
self.authentificationSession?.cancel()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
|
||||
enum SSOAuthentificationSessionError: Error {
|
||||
case userCanceled
|
||||
}
|
||||
|
||||
/// A completion handler the session calls when it completes successfully, or when the user cancels the session.
|
||||
public typealias SSOAuthenticationSessionCompletionHandler = (URL?, Error?) -> Void
|
||||
|
||||
/// An interface the session uses to ask a delegate for a presentation context.
|
||||
protocol SSOAuthenticationSessionContextProviding {
|
||||
var window: UIWindow { get }
|
||||
}
|
||||
|
||||
/// SSOAuthentificationSessionProtocol abstract a session that an app uses to authenticate a user through a web service (SFAuthenticationSession or ASWebAuthenticationSession).
|
||||
protocol SSOAuthentificationSessionProtocol {
|
||||
|
||||
/// Cancels the authentication session. Dismiss displayed authentication screen.
|
||||
func cancel()
|
||||
|
||||
/// Provides context to target where in an application's UI the authorization view should be shown.
|
||||
func setContextProvider(_ contextProvider: SSOAuthenticationSessionContextProviding)
|
||||
|
||||
/// Starts a web authentication session.
|
||||
/// - Parameters:
|
||||
/// - url: A URL with the http or https scheme pointing to the authentication webpage.
|
||||
/// - callbackURLScheme: The custom URL scheme that the app expects in the callback URL.
|
||||
/// - completionHandler: A completion handler the session calls when it completes successfully, or when the user cancels the session.
|
||||
func authenticate(with url: URL, callbackURLScheme: String?, completionHandler: @escaping SSOAuthenticationSessionCompletionHandler)
|
||||
}
|
30
Riot/Modules/Authentication/SSO/SSOURLConstants.swift
Normal file
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
|
||||
enum SSOURLConstants {
|
||||
|
||||
enum Parameters {
|
||||
static let callbackLoginToken = "loginToken"
|
||||
static let redirectURL = "redirectUrl"
|
||||
}
|
||||
|
||||
enum Paths {
|
||||
static let redirect = "/_matrix/client/r0/login/sso/redirect"
|
||||
static let unstableRedirect = "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect/"
|
||||
}
|
||||
}
|
143
Riot/Modules/Authentication/SocialLogin/SocialLoginButton.swift
Normal file
|
@ -0,0 +1,143 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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
|
||||
import AFNetworking
|
||||
|
||||
/// SocialLoginButton represents a button associated to a social login provider.
|
||||
final class SocialLoginButton: UIButton, Themable {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let backgroundColorAlpha: CGFloat = 0.2
|
||||
static let cornerRadius: CGFloat = 8.0
|
||||
static let fontSize: CGFloat = 17.0
|
||||
static let borderWidth: CGFloat = 1.0
|
||||
static let imageEdgeInsetRight: CGFloat = 20.0
|
||||
static let imageTargetSize = CGSize(width: 24, height: 24)
|
||||
static let highlightedAlpha: CGFloat = 0.5
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme?
|
||||
private var viewData: SocialLoginButtonViewData?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var identifier: String? {
|
||||
return self.viewData?.identifier
|
||||
}
|
||||
|
||||
// MARK: Setup
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
self.clipsToBounds = true
|
||||
self.layer.masksToBounds = true
|
||||
self.layer.cornerRadius = Constants.cornerRadius
|
||||
self.titleLabel?.font = UIFont.systemFont(ofSize: Constants.fontSize)
|
||||
self.imageEdgeInsets.right = Constants.imageEdgeInsetRight
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func fill(with viewData: SocialLoginButtonViewData) {
|
||||
self.viewData = viewData
|
||||
|
||||
self.setTitle(viewData.title, for: .normal)
|
||||
|
||||
self.updateWithCurrentTheme()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func updateButtonStyle(with theme: Theme) {
|
||||
guard let viewData = self.viewData else {
|
||||
return
|
||||
}
|
||||
|
||||
let buttonStyle: SocialLoginButtonStyle
|
||||
|
||||
if let themeStyle = viewData.themeStyles[theme.identifier] {
|
||||
buttonStyle = themeStyle
|
||||
} else {
|
||||
buttonStyle = viewData.defaultStyle
|
||||
}
|
||||
|
||||
self.update(with: buttonStyle)
|
||||
}
|
||||
|
||||
private func update(with buttonStyle: SocialLoginButtonStyle) {
|
||||
|
||||
// Image
|
||||
if let sourceImage = buttonStyle.logo {
|
||||
switch sourceImage {
|
||||
case .local(let image):
|
||||
self.setImage(image, for: .normal)
|
||||
case .remote(let imageURL):
|
||||
let urlRequest = URLRequest(url: imageURL)
|
||||
self.setImageFor(.normal, with: urlRequest, placeholderImage: nil) { (urlRequest, httpURLResponse, image) in
|
||||
let resizedImage = image.vc_resized(with: Constants.imageTargetSize)
|
||||
self.setImage(resizedImage, for: .normal)
|
||||
} failure: { (urlRequest, httpURLResponse, error) in
|
||||
self.setImage(nil, for: .normal)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.setImage(nil, for: .normal)
|
||||
}
|
||||
|
||||
// Background
|
||||
|
||||
self.vc_setBackgroundColor(buttonStyle.backgroundColor, for: .normal)
|
||||
|
||||
self.layer.borderWidth = buttonStyle.borderColor != nil ? Constants.borderWidth : 0.0
|
||||
self.layer.borderColor = buttonStyle.borderColor?.cgColor
|
||||
|
||||
// Title
|
||||
|
||||
self.setTitleColor(buttonStyle.titleColor, for: .normal)
|
||||
self.setTitleColor(buttonStyle.titleColor.withAlphaComponent(Constants.highlightedAlpha), for: .highlighted)
|
||||
}
|
||||
|
||||
private func updateWithCurrentTheme() {
|
||||
guard let theme = self.theme else {
|
||||
return
|
||||
}
|
||||
self.update(theme: theme)
|
||||
}
|
||||
|
||||
// MARK: - Themable
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
self.updateButtonStyle(with: theme)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
|
||||
@objc
|
||||
enum SocialLoginButtonMode: Int {
|
||||
case `continue`
|
||||
case signIn
|
||||
case signUp
|
||||
}
|
||||
|
||||
/// `SocialLoginButtonFactory` builds SocialLoginButton and apply dedicated theme if needed.
|
||||
class SocialLoginButtonFactory {
|
||||
|
||||
// MARK - Public
|
||||
|
||||
func build(with identityProvider: MXLoginSSOIdentityProvider, mode: SocialLoginButtonMode) -> SocialLoginButton {
|
||||
let button = SocialLoginButton()
|
||||
|
||||
let defaultStyle: SocialLoginButtonStyle
|
||||
var styles: [String: SocialLoginButtonStyle] = [:]
|
||||
|
||||
switch identityProvider.identifier {
|
||||
case "google":
|
||||
(defaultStyle, styles) = self.buildGoogleButtonStyles()
|
||||
case "facebook":
|
||||
(defaultStyle, styles) = self.buildFacebookButtonStyles()
|
||||
case "github":
|
||||
(defaultStyle, styles) = self.buildGitHubButtonStyles()
|
||||
case "apple":
|
||||
(defaultStyle, styles) = self.buildAppleButtonStyles()
|
||||
case "twitter":
|
||||
(defaultStyle, styles) = self.buildTwitterButtonStyles()
|
||||
default:
|
||||
let image: SourceImage?
|
||||
|
||||
if let imageStringURL = identityProvider.icon, let imageURL = URL(string: imageStringURL) {
|
||||
image = .remote(imageURL)
|
||||
} else {
|
||||
image = nil
|
||||
}
|
||||
|
||||
(defaultStyle, styles) = self.buildDefaultButtonStyles(with: image)
|
||||
}
|
||||
|
||||
let title = self.buildButtonTitle(with: identityProvider.name, mode: mode)
|
||||
|
||||
let viewData = SocialLoginButtonViewData(identifier: identityProvider.identifier,
|
||||
title: title,
|
||||
defaultStyle: defaultStyle,
|
||||
themeStyles: styles)
|
||||
|
||||
button.fill(with: viewData)
|
||||
|
||||
return button
|
||||
}
|
||||
|
||||
// MARK - Private
|
||||
|
||||
private func buildButtonTitle(with providerTitle: String, mode: SocialLoginButtonMode) -> String {
|
||||
let buttonTitle: String
|
||||
|
||||
switch mode {
|
||||
case .signIn:
|
||||
buttonTitle = VectorL10n.socialLoginButtonTitleSignIn(providerTitle)
|
||||
case .signUp:
|
||||
buttonTitle = VectorL10n.socialLoginButtonTitleSignUp(providerTitle)
|
||||
case .continue:
|
||||
buttonTitle = VectorL10n.socialLoginButtonTitleContinue(providerTitle)
|
||||
}
|
||||
|
||||
return buttonTitle
|
||||
}
|
||||
|
||||
private func buildAppleButtonStyles() -> (SocialLoginButtonStyle, [String: SocialLoginButtonStyle]) {
|
||||
|
||||
var lightImage: SourceImage?
|
||||
|
||||
let appleLogo = Asset.Images.socialLoginButtonApple.image
|
||||
|
||||
if let appleLogoLightStyle = appleLogo.vc_tintedImage(usingColor: .white) {
|
||||
lightImage = .local(appleLogoLightStyle)
|
||||
}
|
||||
|
||||
let lightStyle = SocialLoginButtonStyle(logo: lightImage,
|
||||
titleColor: .white,
|
||||
backgroundColor: .black,
|
||||
borderColor: nil)
|
||||
|
||||
var darkImage: SourceImage?
|
||||
|
||||
if let appleLogoDarkStyle = appleLogo.vc_tintedImage(usingColor: .black) {
|
||||
darkImage = .local(appleLogoDarkStyle)
|
||||
}
|
||||
|
||||
let darkStyle = SocialLoginButtonStyle(logo: darkImage,
|
||||
titleColor: .black,
|
||||
backgroundColor: .white,
|
||||
borderColor: nil)
|
||||
|
||||
let defaultStyle: SocialLoginButtonStyle = lightStyle
|
||||
|
||||
let styles: [String: SocialLoginButtonStyle] = [
|
||||
ThemeIdentifier.light.rawValue: lightStyle,
|
||||
ThemeIdentifier.dark.rawValue: darkStyle,
|
||||
ThemeIdentifier.black.rawValue: darkStyle
|
||||
]
|
||||
|
||||
return (defaultStyle, styles)
|
||||
}
|
||||
|
||||
private func buildGoogleButtonStyles() -> (SocialLoginButtonStyle, [String: SocialLoginButtonStyle]) {
|
||||
|
||||
let logo = Asset.Images.socialLoginButtonGoogle.image
|
||||
|
||||
let lightImage: SourceImage = .local(logo)
|
||||
|
||||
let lightStyle = SocialLoginButtonStyle(logo: lightImage,
|
||||
titleColor: UIColor(white: 0, alpha: 0.54),
|
||||
backgroundColor: .white,
|
||||
borderColor: .black)
|
||||
|
||||
var darkImage: SourceImage?
|
||||
|
||||
if let logoDarkStyle = logo.vc_tintedImage(usingColor: .white) {
|
||||
darkImage = .local(logoDarkStyle)
|
||||
}
|
||||
|
||||
let darkStyle = SocialLoginButtonStyle(logo: darkImage,
|
||||
titleColor: .white,
|
||||
backgroundColor: UIColor(rgb: 0x4285F4),
|
||||
borderColor: nil)
|
||||
|
||||
let defaultStyle: SocialLoginButtonStyle = lightStyle
|
||||
|
||||
let styles: [String: SocialLoginButtonStyle] = [
|
||||
ThemeIdentifier.light.rawValue: lightStyle,
|
||||
ThemeIdentifier.dark.rawValue: darkStyle,
|
||||
ThemeIdentifier.black.rawValue: darkStyle
|
||||
]
|
||||
|
||||
return (defaultStyle, styles)
|
||||
}
|
||||
|
||||
private func buildTwitterButtonStyles() -> (SocialLoginButtonStyle, [String: SocialLoginButtonStyle]) {
|
||||
|
||||
let defaultStyle = SocialLoginButtonStyle(logo: .local(Asset.Images.socialLoginButtonTwitter.image),
|
||||
titleColor: .white,
|
||||
backgroundColor: UIColor(rgb: 0x47ACDF),
|
||||
borderColor: nil)
|
||||
return (defaultStyle, [:])
|
||||
}
|
||||
|
||||
private func buildFacebookButtonStyles() -> (SocialLoginButtonStyle, [String: SocialLoginButtonStyle]) {
|
||||
|
||||
let defaultStyle = SocialLoginButtonStyle(logo: .local(Asset.Images.socialLoginButtonFacebook.image),
|
||||
titleColor: .white,
|
||||
backgroundColor: UIColor(rgb: 0x3C5A99),
|
||||
borderColor: nil)
|
||||
return (defaultStyle, [:])
|
||||
}
|
||||
|
||||
private func buildGitHubButtonStyles() -> (SocialLoginButtonStyle, [String: SocialLoginButtonStyle]) {
|
||||
|
||||
var lightImage: SourceImage?
|
||||
|
||||
let githubLogo = Asset.Images.socialLoginButtonGithub.image
|
||||
|
||||
if let githubLogoLightStyle = githubLogo.vc_tintedImage(usingColor: .black) {
|
||||
lightImage = .local(githubLogoLightStyle)
|
||||
}
|
||||
|
||||
let lightStyle = SocialLoginButtonStyle(logo: lightImage,
|
||||
titleColor: .black,
|
||||
backgroundColor: .white,
|
||||
borderColor: .black)
|
||||
|
||||
var darkImage: SourceImage?
|
||||
|
||||
if let githubLogoDarkStyle = githubLogo.vc_tintedImage(usingColor: .white) {
|
||||
darkImage = .local(githubLogoDarkStyle)
|
||||
}
|
||||
|
||||
let darkStyle = SocialLoginButtonStyle(logo: darkImage,
|
||||
titleColor: .white,
|
||||
backgroundColor: .black,
|
||||
borderColor: .white)
|
||||
|
||||
let defaultStyle: SocialLoginButtonStyle = lightStyle
|
||||
|
||||
let styles: [String: SocialLoginButtonStyle] = [
|
||||
ThemeIdentifier.light.rawValue: lightStyle,
|
||||
ThemeIdentifier.dark.rawValue: darkStyle,
|
||||
ThemeIdentifier.black.rawValue: darkStyle
|
||||
]
|
||||
|
||||
return (defaultStyle, styles)
|
||||
}
|
||||
|
||||
private func buildDefaultButtonStyles(with image: SourceImage?) -> (SocialLoginButtonStyle, [String: SocialLoginButtonStyle]) {
|
||||
|
||||
let lightStyle = SocialLoginButtonStyle(logo: image,
|
||||
titleColor: .black,
|
||||
backgroundColor: .white,
|
||||
borderColor: .black)
|
||||
|
||||
let darkStyle = SocialLoginButtonStyle(logo: image,
|
||||
titleColor: .white,
|
||||
backgroundColor: .black,
|
||||
borderColor: .white)
|
||||
|
||||
let defaultStyle: SocialLoginButtonStyle = lightStyle
|
||||
|
||||
let styles: [String: SocialLoginButtonStyle] = [
|
||||
ThemeIdentifier.light.rawValue: lightStyle,
|
||||
ThemeIdentifier.dark.rawValue: darkStyle,
|
||||
ThemeIdentifier.black.rawValue: darkStyle
|
||||
]
|
||||
|
||||
return (defaultStyle, styles)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
|
||||
/// SocialLoginButton style
|
||||
struct SocialLoginButtonStyle {
|
||||
let logo: SourceImage?
|
||||
let titleColor: UIColor
|
||||
let backgroundColor: UIColor
|
||||
let borderColor: UIColor?
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
|
||||
/// SocialLoginButton view data
|
||||
struct SocialLoginButtonViewData {
|
||||
|
||||
/// Identify provider identifier
|
||||
let identifier: String
|
||||
|
||||
/// Button title
|
||||
let title: String
|
||||
|
||||
/// Default button style
|
||||
let defaultStyle: SocialLoginButtonStyle
|
||||
|
||||
/// Button style per theme identifier
|
||||
let themeStyles: [String: SocialLoginButtonStyle]
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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
|
||||
import Reusable
|
||||
|
||||
@objc protocol SocialLoginListViewDelegate: class {
|
||||
func socialLoginListView(_ socialLoginListView: SocialLoginListView, didTapSocialButtonWithIdentifier identifier: String)
|
||||
}
|
||||
|
||||
/// SocialLoginListView displays a list of social login buttons according to a given array of SSO Identity Providers.
|
||||
@objcMembers
|
||||
final class SocialLoginListView: UIView, NibLoadable {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private static let sizingView = SocialLoginListView.instantiate()
|
||||
|
||||
private enum Constants {
|
||||
static let buttonHeight: CGFloat = 44.0
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var buttonsStackView: UIStackView!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let socialButtonFactory: SocialLoginButtonFactory = SocialLoginButtonFactory()
|
||||
private var theme: Theme!
|
||||
private var buttons: [SocialLoginButton] = []
|
||||
private(set) var mode: SocialLoginButtonMode = .continue
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: SocialLoginListViewDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
static func instantiate() -> SocialLoginListView {
|
||||
let view = SocialLoginListView.loadFromNib()
|
||||
view.theme = ThemeService.shared().theme
|
||||
return view
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func update(with identityProviders: [MXLoginSSOIdentityProvider], mode: SocialLoginButtonMode) {
|
||||
self.mode = mode
|
||||
|
||||
let title: String
|
||||
|
||||
switch mode {
|
||||
case .continue:
|
||||
title = VectorL10n.socialLoginListTitleContinue
|
||||
case .signIn:
|
||||
title = VectorL10n.socialLoginListTitleSignIn
|
||||
case .signUp:
|
||||
title = VectorL10n.socialLoginListTitleSignUp
|
||||
}
|
||||
|
||||
self.titleLabel.text = title
|
||||
|
||||
self.removeButtons()
|
||||
let buttons = self.socialLoginButtons(for: identityProviders, mode: mode)
|
||||
|
||||
for button in buttons {
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.heightAnchor.constraint(equalToConstant: Constants.buttonHeight).isActive = true
|
||||
self.buttonsStackView.addArrangedSubview(button)
|
||||
}
|
||||
|
||||
self.buttons = buttons
|
||||
}
|
||||
|
||||
static func contentViewHeight(identityProviders: [MXLoginSSOIdentityProvider],
|
||||
mode: SocialLoginButtonMode,
|
||||
fitting width: CGFloat) -> CGFloat {
|
||||
let sizingView = self.sizingView
|
||||
|
||||
sizingView.frame = CGRect(x: 0, y: 0, width: width, height: 1)
|
||||
|
||||
sizingView.update(with: identityProviders, mode: mode)
|
||||
|
||||
sizingView.setNeedsLayout()
|
||||
sizingView.layoutIfNeeded()
|
||||
|
||||
let fittingSize = CGSize(width: width, height: UIView.layoutFittingCompressedSize.height)
|
||||
|
||||
return sizingView.systemLayoutSizeFitting(fittingSize).height
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func removeButtons() {
|
||||
self.buttonsStackView.vc_removeAllSubviews()
|
||||
self.buttons = []
|
||||
}
|
||||
|
||||
private func socialLoginButtons(for identityProviders: [MXLoginSSOIdentityProvider], mode: SocialLoginButtonMode) -> [SocialLoginButton] {
|
||||
|
||||
var buttons: [SocialLoginButton] = []
|
||||
|
||||
// Order alphabeticaly by Identity Provider identifier
|
||||
let sortedIdentityProviders = identityProviders.sorted { (firstIdentityProvider, secondIdentityProvider) -> Bool in
|
||||
firstIdentityProvider.identifier < secondIdentityProvider.identifier
|
||||
}
|
||||
|
||||
for identityProvider in sortedIdentityProviders {
|
||||
let socialLoginButton = self.socialButtonFactory.build(with: identityProvider, mode: mode)
|
||||
socialLoginButton.update(theme: self.theme)
|
||||
socialLoginButton.addTarget(self, action: #selector(socialButtonAction(_:)), for: .touchUpInside)
|
||||
buttons.append(socialLoginButton)
|
||||
}
|
||||
|
||||
return buttons
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
@objc private func socialButtonAction(_ socialLoginButton: SocialLoginButton) {
|
||||
guard let identifier = socialLoginButton.identifier else {
|
||||
return
|
||||
}
|
||||
self.delegate?.socialLoginListView(self, didTapSocialButtonWithIdentifier: identifier)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Themable
|
||||
extension SocialLoginListView: Themable {
|
||||
func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.titleLabel.textColor = theme.textSecondaryColor
|
||||
|
||||
for button in self.buttons {
|
||||
button.update(theme: theme)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17126"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="SocialLoginListView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="197"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" text="Or login with" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aq0-cP-kyj">
|
||||
<rect key="frame" x="20" y="20" width="374" height="39"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="39" id="3N8-38-YLI"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="S94-Mr-AzH">
|
||||
<rect key="frame" x="47" y="79" width="320" height="98"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ebn-i5-Ixd">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<color key="backgroundColor" systemColor="linkColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="c7I-Vf-smw"/>
|
||||
</constraints>
|
||||
<state key="normal" title="Button" image="social_login_button_facebook"/>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7pW-XK-gE7">
|
||||
<rect key="frame" x="0.0" y="54" width="320" height="44"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="LRM-mj-KV9"/>
|
||||
</constraints>
|
||||
<state key="normal" title="Button" image="social_login_button_github">
|
||||
<color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</state>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" priority="750" constant="320" id="oP5-q2-9cK"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="aq0-cP-kyj" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="20" id="2Vc-s3-2c3"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="S94-Mr-AzH" secondAttribute="trailing" constant="20" id="C0z-3g-kxr"/>
|
||||
<constraint firstAttribute="trailing" secondItem="aq0-cP-kyj" secondAttribute="trailing" constant="20" id="EPh-iw-rBR"/>
|
||||
<constraint firstAttribute="bottom" secondItem="S94-Mr-AzH" secondAttribute="bottom" constant="20" id="UlQ-fd-NAJ"/>
|
||||
<constraint firstItem="aq0-cP-kyj" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" id="VOb-tL-VFc"/>
|
||||
<constraint firstItem="S94-Mr-AzH" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="gtL-SP-6lp"/>
|
||||
<constraint firstItem="S94-Mr-AzH" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" id="rb6-Z8-Std"/>
|
||||
<constraint firstItem="S94-Mr-AzH" firstAttribute="top" secondItem="aq0-cP-kyj" secondAttribute="bottom" constant="20" id="sM8-OR-Vnq"/>
|
||||
</constraints>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="buttonsStackView" destination="S94-Mr-AzH" id="1Tu-hR-HhZ"/>
|
||||
<outlet property="titleLabel" destination="aq0-cP-kyj" id="tlN-w9-VKt"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="137.68115942028987" y="-142.29910714285714"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="social_login_button_facebook" width="24" height="24"/>
|
||||
<image name="social_login_button_github" width="24" height="24"/>
|
||||
<systemColor name="linkColor">
|
||||
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
23
Riot/Modules/Common/Models/SourceImage.swift
Normal file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
|
||||
/// SourceImage represents a local or remote image.
|
||||
enum SourceImage {
|
||||
case local(_ image: UIImage)
|
||||
case remote(_ url: URL)
|
||||
}
|
28
Riot/Modules/DeepLink/CustomSchemeURLConstants.swift
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
|
||||
enum CustomSchemeURLConstants {
|
||||
|
||||
enum Parameters {
|
||||
static let transactionId = "transaction_id"
|
||||
}
|
||||
|
||||
enum Hosts {
|
||||
static let connect = "connect"
|
||||
}
|
||||
}
|
68
Riot/Modules/DeepLink/CustomSchemeURLParser.swift
Normal file
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
|
||||
enum CustomSchemeURLParserError: Error {
|
||||
case unknownHost
|
||||
case invalidParameters
|
||||
case unknown
|
||||
}
|
||||
|
||||
/// CustomSchemeURLParser enables to parse custom scheme URL
|
||||
class CustomSchemeURLParser {
|
||||
|
||||
func parse(url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) throws -> DeepLinkOption {
|
||||
guard let host = url.host else {
|
||||
throw CustomSchemeURLParserError.unknownHost
|
||||
}
|
||||
|
||||
guard let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
|
||||
throw CustomSchemeURLParserError.unknown
|
||||
}
|
||||
|
||||
let deepLinkOption: DeepLinkOption
|
||||
|
||||
switch host {
|
||||
case CustomSchemeURLConstants.Hosts.connect:
|
||||
if let deepLinkOpt = self.buildDeepLinkConnect(from: urlComponents) {
|
||||
deepLinkOption = deepLinkOpt
|
||||
} else {
|
||||
throw CustomSchemeURLParserError.invalidParameters
|
||||
}
|
||||
default:
|
||||
throw CustomSchemeURLParserError.unknownHost
|
||||
}
|
||||
|
||||
return deepLinkOption
|
||||
}
|
||||
|
||||
private func buildDeepLinkConnect(from urlComponents: URLComponents) -> DeepLinkOption? {
|
||||
guard let queryItems = urlComponents.queryItems, queryItems.isEmpty == false else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let loginToken = urlComponents.vc_getQueryItemValue(for: SSOURLConstants.Parameters.callbackLoginToken) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let txnId = urlComponents.vc_getQueryItemValue(for: CustomSchemeURLConstants.Parameters.transactionId) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return DeepLinkOption.connect(loginToken, txnId)
|
||||
}
|
||||
}
|
24
Riot/Modules/DeepLink/DeepLinkOption.swift
Normal file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// Copyright 2020 New Vector 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 Foundation
|
||||
|
||||
/// DeepLinkOption represents deep link paths with their respective parameters
|
||||
enum DeepLinkOption {
|
||||
|
||||
/// Used for SSO callback only when VoiceOver is enabled
|
||||
case connect(_ loginToken: String, _ transactionId: String)
|
||||
}
|
|
@ -20,12 +20,30 @@
|
|||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>$(BASE_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>$(APPLICATION_SCHEME)</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<true/>
|
||||
<key>ITSEncryptionExportComplianceCode</key>
|
||||
<string>d1dd539c-d21c-43e2-92e2-212c5269565c</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>http</string>
|
||||
<string>https</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
|
@ -94,10 +112,5 @@
|
|||
<string>$(BASE_BUNDLE_IDENTIFIER)</string>
|
||||
<key>keychainAccessGroup</key>
|
||||
<string>$(KEYCHAIN_ACCESS_GROUP)</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>http</string>
|
||||
<string>https</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
#import "ThemeService.h"
|
||||
|
|