diff --git a/CHANGES.rst b/CHANGES.rst index d6eae89d2..ba044b474 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,13 +2,16 @@ Changes to be released in next version ================================================= ✨ Features - * + * Change Pin inside the app (#3881) + * AuthVC: Add social login (#3846). 🙌 Improvements * Bug report: Add "Continue in background" button (#3816). + * 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 ⚠️ API Changes * diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 886024f84..7b0f0fd3c 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -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" } diff --git a/Config/Common.xcconfig b/Config/Common.xcconfig index 85a47734f..59db66f87 100644 --- a/Config/Common.xcconfig +++ b/Config/Common.xcconfig @@ -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 diff --git a/Podfile b/Podfile index 177c8627b..ccb4515da 100644 --- a/Podfile +++ b/Podfile @@ -11,8 +11,8 @@ use_frameworks! # - `{ {kit spec hash} => {sdk spec hash}` to depend on specific pod options (:git => …, :podspec => …) for each repo. Used by Fastfile during CI # # Warning: our internal tooling depends on the name of this variable name, so be sure not to change it -$matrixKitVersion = '= 0.13.4' -# $matrixKitVersion = :local +# $matrixKitVersion = '= 0.13.4' +$matrixKitVersion = :local # $matrixKitVersion = {'develop' => 'develop'} ######################################## diff --git a/Podfile.lock b/Podfile.lock index d0d853a25..9b969689f 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -127,10 +127,10 @@ DEPENDENCIES: - KeychainAccess (~> 4.2.1) - KTCenterFlowLayout (~> 1.3.1) - MatomoTracker (~> 7.2.2) - - MatrixKit (= 0.13.4) - - MatrixKit/AppExtension (= 0.13.4) - - MatrixSDK - - MatrixSDK/JingleCallStack + - MatrixKit (from `../matrix-ios-kit/MatrixKit.podspec`) + - MatrixKit/AppExtension (from `../matrix-ios-kit/MatrixKit.podspec`) + - MatrixSDK (from `../matrix-ios-sdk/MatrixSDK.podspec`) + - MatrixSDK/JingleCallStack (from `../matrix-ios-sdk/MatrixSDK.podspec`) - OLMKit - ReadMoreTextView (~> 3.0.1) - Reusable (~> 4.1) @@ -164,8 +164,6 @@ SPEC REPOS: - LoggerAPI - Logging - MatomoTracker - - MatrixKit - - MatrixSDK - OLMKit - ReadMoreTextView - Realm @@ -177,6 +175,12 @@ SPEC REPOS: - zxcvbn-ios - ZXingObjC +EXTERNAL SOURCES: + MatrixKit: + :path: "../matrix-ios-kit/MatrixKit.podspec" + MatrixSDK: + :path: "../matrix-ios-sdk/MatrixSDK.podspec" + SPEC CHECKSUMS: AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce BlueCryptor: b0aee3d9b8f367b49b30de11cda90e1735571c24 @@ -212,6 +216,6 @@ SPEC CHECKSUMS: zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: 198d9f0614bcf132a6aa31bdf8c1beb0360fdea7 +PODFILE CHECKSUM: 43728c4df74776cca6a4ff4467ebe9be03737120 -COCOAPODS: 1.10.0 +COCOAPODS: 1.10.1 diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index fca434ab0..1c2f413eb 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -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 = ""; }; B12676812523E4D100BE6B98 /* SecretsResetViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsResetViewAction.swift; sourceTree = ""; }; B12676822523E4D100BE6B98 /* SecretsResetViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsResetViewModel.swift; sourceTree = ""; }; + B12742B9258C46DC00731DA6 /* SSOAuthentificationSessionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSOAuthentificationSessionProtocol.swift; sourceTree = ""; }; + B12742BB258C472800731DA6 /* LegacySSOAuthentificationSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacySSOAuthentificationSession.swift; sourceTree = ""; }; B1284E3D2535FEA6003529D7 /* SecureBackupSetupIntroViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupSetupIntroViewModelType.swift; sourceTree = ""; }; B1284E3F2535FEBA003529D7 /* SecureBackupSetupIntroViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupSetupIntroViewModel.swift; sourceTree = ""; }; B12C56EE2396CB5E00FAC6DE /* RoomMessageURLParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMessageURLParser.swift; sourceTree = ""; }; @@ -1289,6 +1312,9 @@ B140B4A721F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorBridgePresenter.swift; sourceTree = ""; }; B142317822CCFA2000FFA96A /* EditHistoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHistoryCell.swift; sourceTree = ""; }; B142317922CCFA2000FFA96A /* EditHistoryCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EditHistoryCell.xib; sourceTree = ""; }; + B14EED18257992D600448735 /* SocialLoginListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginListView.swift; sourceTree = ""; }; + B14EED1A2579933100448735 /* SocialLoginListView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SocialLoginListView.xib; sourceTree = ""; }; + B14EED1C257D85DF00448735 /* SocialLoginButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginButton.swift; sourceTree = ""; }; B14F142622144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = KeyBackupRecoverFromRecoveryKeyViewController.storyboard; sourceTree = ""; }; B14F142722144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyViewModelType.swift; sourceTree = ""; }; B14F142822144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift; sourceTree = ""; }; @@ -1406,6 +1432,14 @@ B169331020F3CAFC00746532 /* PublicRoomTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PublicRoomTableViewCell.h; sourceTree = ""; }; B169331220F3CAFC00746532 /* PublicRoomsDirectoryDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PublicRoomsDirectoryDataSource.m; sourceTree = ""; }; B169331320F3CAFC00746532 /* PublicRoomsDirectoryDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PublicRoomsDirectoryDataSource.h; sourceTree = ""; }; + B16C028C25A71CA3008CA7B1 /* CustomSchemeURLConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSchemeURLConstants.swift; sourceTree = ""; }; + B16D6353257D8FB0008BDC97 /* SocialLoginButtonFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginButtonFactory.swift; sourceTree = ""; }; + B16DC22A2587DC35004DAB1A /* SSOAuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSOAuthenticationService.swift; sourceTree = ""; }; + B16DC234258A723B004DAB1A /* SourceImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceImage.swift; sourceTree = ""; }; + B16DC237258A7595004DAB1A /* SocialLoginButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginButtonStyle.swift; sourceTree = ""; }; + B16DC239258A75B7004DAB1A /* SocialLoginButtonViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginButtonViewData.swift; sourceTree = ""; }; + B16DC23B258A7D13004DAB1A /* ThemeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeService.swift; sourceTree = ""; }; + B16DC23D258A8025004DAB1A /* ThemeIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeIdentifier.swift; sourceTree = ""; }; B17982FE2119FED2001FD722 /* GDPRConsentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GDPRConsentViewController.swift; sourceTree = ""; }; B1798301211B13B3001FD722 /* OnBoardingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnBoardingManager.swift; sourceTree = ""; }; B183226523F55D6B0035B2E8 /* CameraAccessManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraAccessManager.swift; sourceTree = ""; }; @@ -1426,6 +1460,9 @@ B190F55822CE356800AEB493 /* EditHistoryHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHistoryHeaderView.swift; sourceTree = ""; }; B190F55A22CE35FD00AEB493 /* EditHistoryHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EditHistoryHeaderView.xib; sourceTree = ""; }; B190F55C22CE5A9600AEB493 /* EditHistorySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHistorySection.swift; sourceTree = ""; }; + B1945CC6258B77520020D8D6 /* SocialLoginDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginDataSource.swift; sourceTree = ""; }; + B1945CC8258C04F20020D8D6 /* SSOAuthenticationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSOAuthenticationPresenter.swift; sourceTree = ""; }; + B1945CCA258C244F0020D8D6 /* SSOAuthentificationSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSOAuthentificationSession.swift; sourceTree = ""; }; B1963B25228F1C4800CBA17F /* BubbleReactionsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleReactionsView.swift; sourceTree = ""; }; B1963B26228F1C4800CBA17F /* BubbleReactionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BubbleReactionViewCell.xib; sourceTree = ""; }; B1963B27228F1C4800CBA17F /* BubbleReactionsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleReactionsViewModel.swift; sourceTree = ""; }; @@ -1437,6 +1474,10 @@ B197B7C5243DE947005ABBF3 /* EncryptionTrustLevelBadgeImageHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionTrustLevelBadgeImageHelper.swift; sourceTree = ""; }; B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorType.swift; sourceTree = ""; }; B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinator.swift; sourceTree = ""; }; + B1A15BD325A4BAA800BDCA36 /* CustomSchemeURLParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSchemeURLParser.swift; sourceTree = ""; }; + B1A15BD525A526A200BDCA36 /* URLComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLComponents.swift; sourceTree = ""; }; + B1A15BD825A6652800BDCA36 /* DeepLinkOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinkOption.swift; sourceTree = ""; }; + B1A15BDA25A6680E00BDCA36 /* SSOURLConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSOURLConstants.swift; sourceTree = ""; }; B1A5B33D227ADF2A004CBA85 /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = ""; }; B1A6805324B7C65200E312CC /* MajorUpdateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MajorUpdateManager.swift; sourceTree = ""; }; B1A68592229E807800D6C09A /* RoomBubbleCellLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomBubbleCellLayout.swift; sourceTree = ""; }; @@ -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 = ""; }; + 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 = ""; + }; + 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 = ""; + }; B17982FD2119FEA7001FD722 /* GDPR */ = { isa = PBXGroup; children = ( @@ -3340,6 +3410,16 @@ path = KeyVerification; sourceTree = ""; }; + B1A15BD725A664F400BDCA36 /* DeepLink */ = { + isa = PBXGroup; + children = ( + B1A15BD825A6652800BDCA36 /* DeepLinkOption.swift */, + B16C028C25A71CA3008CA7B1 /* CustomSchemeURLConstants.swift */, + B1A15BD325A4BAA800BDCA36 /* CustomSchemeURLParser.swift */, + ); + path = DeepLink; + sourceTree = ""; + }; 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 = ""; @@ -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 */, diff --git a/Riot/Assets/Images.xcassets/Authentication/Contents.json b/Riot/Assets/Images.xcassets/Authentication/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Riot/Assets/Images.xcassets/Authentication/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/Contents.json b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_apple.imageset/Contents.json b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_apple.imageset/Contents.json new file mode 100644 index 000000000..8bede60cf --- /dev/null +++ b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_apple.imageset/Contents.json @@ -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 + } +} diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_apple.imageset/social_login_button_apple.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_apple.imageset/social_login_button_apple.png new file mode 100644 index 000000000..5398d2ae2 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_apple.imageset/social_login_button_apple.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_apple.imageset/social_login_button_apple@2x.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_apple.imageset/social_login_button_apple@2x.png new file mode 100644 index 000000000..5cd31cc7f Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_apple.imageset/social_login_button_apple@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_apple.imageset/social_login_button_apple@3x.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_apple.imageset/social_login_button_apple@3x.png new file mode 100644 index 000000000..4e5b5c2d8 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_apple.imageset/social_login_button_apple@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_facebook.imageset/Contents.json b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_facebook.imageset/Contents.json new file mode 100644 index 000000000..64f276339 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_facebook.imageset/Contents.json @@ -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 + } +} diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_facebook.imageset/social_login_button_facebook.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_facebook.imageset/social_login_button_facebook.png new file mode 100644 index 000000000..184578a2a Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_facebook.imageset/social_login_button_facebook.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_facebook.imageset/social_login_button_facebook@2x.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_facebook.imageset/social_login_button_facebook@2x.png new file mode 100644 index 000000000..15bcb3060 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_facebook.imageset/social_login_button_facebook@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_facebook.imageset/social_login_button_facebook@3x.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_facebook.imageset/social_login_button_facebook@3x.png new file mode 100644 index 000000000..6c232fe03 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_facebook.imageset/social_login_button_facebook@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_github.imageset/Contents.json b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_github.imageset/Contents.json new file mode 100644 index 000000000..33a7a4568 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_github.imageset/Contents.json @@ -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 + } +} diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_github.imageset/social_login_button_github.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_github.imageset/social_login_button_github.png new file mode 100644 index 000000000..fdb7d895d Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_github.imageset/social_login_button_github.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_github.imageset/social_login_button_github@2x.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_github.imageset/social_login_button_github@2x.png new file mode 100644 index 000000000..917da8ec4 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_github.imageset/social_login_button_github@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_github.imageset/social_login_button_github@3x.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_github.imageset/social_login_button_github@3x.png new file mode 100644 index 000000000..48e3cbb3f Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_github.imageset/social_login_button_github@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_google.imageset/Contents.json b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_google.imageset/Contents.json new file mode 100644 index 000000000..180a74c6c --- /dev/null +++ b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_google.imageset/Contents.json @@ -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 + } +} diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_google.imageset/social_login_button_google.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_google.imageset/social_login_button_google.png new file mode 100644 index 000000000..09f592fa6 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_google.imageset/social_login_button_google.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_google.imageset/social_login_button_google@2x.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_google.imageset/social_login_button_google@2x.png new file mode 100644 index 000000000..fcfe650c4 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_google.imageset/social_login_button_google@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_google.imageset/social_login_button_google@3x.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_google.imageset/social_login_button_google@3x.png new file mode 100644 index 000000000..60f329d7b Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_google.imageset/social_login_button_google@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_twitter.imageset/Contents.json b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_twitter.imageset/Contents.json new file mode 100644 index 000000000..ce4236543 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_twitter.imageset/Contents.json @@ -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 + } +} diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_twitter.imageset/social_login_button_twitter.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_twitter.imageset/social_login_button_twitter.png new file mode 100644 index 000000000..696a52391 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_twitter.imageset/social_login_button_twitter.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_twitter.imageset/social_login_button_twitter@2x.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_twitter.imageset/social_login_button_twitter@2x.png new file mode 100644 index 000000000..ed424baff Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_twitter.imageset/social_login_button_twitter@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_twitter.imageset/social_login_button_twitter@3x.png b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_twitter.imageset/social_login_button_twitter@3x.png new file mode 100644 index 000000000..7e8c4e7d9 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Authentication/SocialLogin/social_login_button_twitter.imageset/social_login_button_twitter@3x.png differ diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 43eeda5d2..ba19b28ac 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -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?"; @@ -1461,6 +1471,7 @@ Tap the + to start adding people."; "pin_protection_choose_pin" = "Create a PIN for security"; "pin_protection_confirm_pin" = "Confirm your PIN"; "pin_protection_confirm_pin_to_disable" = "Confirm PIN to disable PIN"; +"pin_protection_confirm_pin_to_change" = "Confirm PIN to change PIN"; "pin_protection_enter_pin" = "Enter your PIN"; "pin_protection_forgot_pin" = "Forgot PIN"; "pin_protection_reset_alert_title" = "Reset PIN"; @@ -1473,6 +1484,7 @@ Tap the + to start adding people."; "pin_protection_settings_section_footer" = "To reset your PIN, you'll need to re-login and create a new one."; "pin_protection_settings_enabled_forced" = "PIN enabled"; "pin_protection_settings_enable_pin" = "Enable PIN"; +"pin_protection_settings_change_pin" = "Change PIN"; "pin_protection_not_allowed_pin" = "For security reasons, this PIN isn’t available. Please try another PIN"; "pin_protection_explanatory" = "Setting up a PIN lets you protect data like messages and contacts, so only you can access them by entering the PIN at the start of the app."; "pin_protection_kick_user_alert_message" = "Too many errors, you've been logged out"; diff --git a/Riot/Categories/UIImage.swift b/Riot/Categories/UIImage.swift index adafbea9a..4b5a9703a 100644 --- a/Riot/Categories/UIImage.swift +++ b/Riot/Categories/UIImage.swift @@ -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) diff --git a/Riot/Categories/URLComponents.swift b/Riot/Categories/URLComponents.swift new file mode 100644 index 000000000..b315d7820 --- /dev/null +++ b/Riot/Categories/URLComponents.swift @@ -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 + } +} diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift index d2c1f1a7e..a6b1ae49f 100644 --- a/Riot/Generated/Images.swift +++ b/Riot/Generated/Images.swift @@ -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") diff --git a/Riot/Generated/InfoPlist.swift b/Riot/Generated/InfoPlist.swift index 4485dcea7..b9784e400 100644 --- a/Riot/Generated/InfoPlist.swift +++ b/Riot/Generated/InfoPlist.swift @@ -21,6 +21,7 @@ internal enum InfoPlist { internal static let cfBundlePackageType: String = _document["CFBundlePackageType"] internal static let cfBundleShortVersionString: String = _document["CFBundleShortVersionString"] internal static let cfBundleSignature: String = _document["CFBundleSignature"] + internal static let cfBundleURLTypes: [[String: Any]] = _document["CFBundleURLTypes"] internal static let cfBundleVersion: String = _document["CFBundleVersion"] internal static let itsAppUsesNonExemptEncryption: Bool = _document["ITSAppUsesNonExemptEncryption"] internal static let itsEncryptionExportComplianceCode: String = _document["ITSEncryptionExportComplianceCode"] diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 3bdfa2fc6..d08d928a8 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -2066,6 +2066,10 @@ internal enum VectorL10n { internal static var pinProtectionConfirmPin: String { return VectorL10n.tr("Vector", "pin_protection_confirm_pin") } + /// Confirm PIN to change PIN + internal static var pinProtectionConfirmPinToChange: String { + return VectorL10n.tr("Vector", "pin_protection_confirm_pin_to_change") + } /// Confirm PIN to disable PIN internal static var pinProtectionConfirmPinToDisable: String { return VectorL10n.tr("Vector", "pin_protection_confirm_pin_to_disable") @@ -2114,6 +2118,10 @@ internal enum VectorL10n { internal static var pinProtectionResetAlertTitle: String { return VectorL10n.tr("Vector", "pin_protection_reset_alert_title") } + /// Change PIN + internal static var pinProtectionSettingsChangePin: String { + return VectorL10n.tr("Vector", "pin_protection_settings_change_pin") + } /// Enable PIN internal static var pinProtectionSettingsEnablePin: String { return VectorL10n.tr("Vector", "pin_protection_settings_enable_pin") @@ -4270,6 +4278,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") diff --git a/Riot/Managers/Theme/ThemeIdentifier.swift b/Riot/Managers/Theme/ThemeIdentifier.swift new file mode 100644 index 000000000..76b19e57d --- /dev/null +++ b/Riot/Managers/Theme/ThemeIdentifier.swift @@ -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 + } + } +} diff --git a/Riot/Managers/Theme/ThemeService.swift b/Riot/Managers/Theme/ThemeService.swift new file mode 100644 index 000000000..209812111 --- /dev/null +++ b/Riot/Managers/Theme/ThemeService.swift @@ -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) + } +} diff --git a/Riot/Managers/Theme/Themes/BlackTheme.swift b/Riot/Managers/Theme/Themes/BlackTheme.swift index a8263c5dc..d0ff58cd9 100644 --- a/Riot/Managers/Theme/Themes/BlackTheme.swift +++ b/Riot/Managers/Theme/Themes/BlackTheme.swift @@ -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) diff --git a/Riot/Managers/Theme/Themes/DarkTheme.swift b/Riot/Managers/Theme/Themes/DarkTheme.swift index 146185f54..c7998763b 100644 --- a/Riot/Managers/Theme/Themes/DarkTheme.swift +++ b/Riot/Managers/Theme/Themes/DarkTheme.swift @@ -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) diff --git a/Riot/Managers/Theme/Themes/DefaultTheme.swift b/Riot/Managers/Theme/Themes/DefaultTheme.swift index cc3225bf0..8dcab9c10 100644 --- a/Riot/Managers/Theme/Themes/DefaultTheme.swift +++ b/Riot/Managers/Theme/Themes/DefaultTheme.swift @@ -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) diff --git a/Riot/Modules/Application/AppCoordinator.swift b/Riot/Modules/Application/AppCoordinator.swift index 534b2c3ab..026e4fc58 100755 --- a/Riot/Modules/Application/AppCoordinator.swift +++ b/Riot/Modules/Application/AppCoordinator.swift @@ -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 diff --git a/Riot/Modules/Application/AppCoordinatorType.swift b/Riot/Modules/Application/AppCoordinatorType.swift index ea1065446..5532df921 100644 --- a/Riot/Modules/Application/AppCoordinatorType.swift +++ b/Riot/Modules/Application/AppCoordinatorType.swift @@ -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 } diff --git a/Riot/Modules/Application/AppDelegate.swift b/Riot/Modules/Application/AppDelegate.swift index c881a90a4..5a54a63b0 100644 --- a/Riot/Modules/Application/AppDelegate.swift +++ b/Riot/Modules/Application/AppDelegate.swift @@ -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 { diff --git a/Riot/Modules/Application/LegacyAppDelegate.h b/Riot/Modules/Application/LegacyAppDelegate.h index f844adcfc..6a53f17b6 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.h +++ b/Riot/Modules/Application/LegacyAppDelegate.h @@ -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 diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m index c6be618c7..21bb7c98c 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.m +++ b/Riot/Modules/Application/LegacyAppDelegate.m @@ -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 diff --git a/Riot/Modules/Authentication/AuthenticationViewController.h b/Riot/Modules/Authentication/AuthenticationViewController.h index 65aa53835..a689fd5ae 100644 --- a/Riot/Modules/Authentication/AuthenticationViewController.h +++ b/Riot/Modules/Authentication/AuthenticationViewController.h @@ -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 diff --git a/Riot/Modules/Authentication/AuthenticationViewController.m b/Riot/Modules/Authentication/AuthenticationViewController.m index 3afa185f8..eb47f6090 100644 --- a/Riot/Modules/Authentication/AuthenticationViewController.m +++ b/Riot/Modules/Authentication/AuthenticationViewController.m @@ -27,7 +27,12 @@ #import "Riot-Swift.h" -@interface AuthenticationViewController () +static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; + +@interface AuthenticationViewController () { /** 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*)loginFlows +{ + for (MXLoginFlow *loginFlow in loginFlows) + { + if ([loginFlow.type isEqualToString:kMXLoginFlowTypePassword]) + { + return YES; + } + } + + return NO; +} + +- (MXLoginSSOFlow*)logginSSOFlowWithProvidersFromFlows:(NSArray*)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 *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 diff --git a/Riot/Modules/Authentication/AuthenticationViewController.xib b/Riot/Modules/Authentication/AuthenticationViewController.xib index e388674d0..28b60026d 100644 --- a/Riot/Modules/Authentication/AuthenticationViewController.xib +++ b/Riot/Modules/Authentication/AuthenticationViewController.xib @@ -1,9 +1,9 @@ - + - + @@ -42,6 +42,7 @@ + @@ -111,7 +112,7 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/BugReport/BugReportViewController.m b/Riot/Modules/BugReport/BugReportViewController.m index 93db86a6a..84fc7dc6e 100644 --- a/Riot/Modules/BugReport/BugReportViewController.m +++ b/Riot/Modules/BugReport/BugReportViewController.m @@ -136,6 +136,9 @@ }]; [self userInterfaceThemeDidChange]; + + UIGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backgroundViewTapped)]; + [self.view addGestureRecognizer:recognizer]; } - (void)userInterfaceThemeDidChange @@ -496,4 +499,10 @@ [self dismissViewControllerAnimated:YES completion:^{}]; } +- (void)backgroundViewTapped +{ + // Dismiss keyboard if user taps on background view: https://github.com/vector-im/element-ios/issues/3819 + [self.bugReportDescriptionTextView resignFirstResponder]; +} + @end diff --git a/Riot/Modules/Common/Models/SourceImage.swift b/Riot/Modules/Common/Models/SourceImage.swift new file mode 100644 index 000000000..bf2ae6208 --- /dev/null +++ b/Riot/Modules/Common/Models/SourceImage.swift @@ -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) +} diff --git a/Riot/Modules/DeepLink/CustomSchemeURLConstants.swift b/Riot/Modules/DeepLink/CustomSchemeURLConstants.swift new file mode 100644 index 000000000..6393592f2 --- /dev/null +++ b/Riot/Modules/DeepLink/CustomSchemeURLConstants.swift @@ -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" + } +} diff --git a/Riot/Modules/DeepLink/CustomSchemeURLParser.swift b/Riot/Modules/DeepLink/CustomSchemeURLParser.swift new file mode 100644 index 000000000..5da141a30 --- /dev/null +++ b/Riot/Modules/DeepLink/CustomSchemeURLParser.swift @@ -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) + } +} diff --git a/Riot/Modules/DeepLink/DeepLinkOption.swift b/Riot/Modules/DeepLink/DeepLinkOption.swift new file mode 100644 index 000000000..1799be588 --- /dev/null +++ b/Riot/Modules/DeepLink/DeepLinkOption.swift @@ -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) +} diff --git a/Riot/Modules/Room/Views/Title/Preview/PreviewRoomTitleView.m b/Riot/Modules/Room/Views/Title/Preview/PreviewRoomTitleView.m index 983af68a5..52327d07d 100644 --- a/Riot/Modules/Room/Views/Title/Preview/PreviewRoomTitleView.m +++ b/Riot/Modules/Room/Views/Title/Preview/PreviewRoomTitleView.m @@ -217,7 +217,8 @@ // self.roomMembers.text = nil; // } - self.previewLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_preview_invitation_format", @"Vector", nil), inviter]; + NSString *displayName = [inviter isEqualToString:inviterUserId] ? inviter : [NSString stringWithFormat:@"%@ (%@)", inviter, inviterUserId]; + self.previewLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_preview_invitation_format", @"Vector", nil), displayName]; }; [self.mxRoom members:^(MXRoomMembers *roomMembers) { diff --git a/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewController.swift b/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewController.swift index 68ba18939..50c41980d 100644 --- a/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewController.swift +++ b/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewController.swift @@ -216,6 +216,8 @@ final class EnterPinCodeViewController: UIViewController { self.renderConfirmPinToDisable() case .inactive: self.renderInactive() + case .changePin: + self.renderChangePin() } } @@ -336,6 +338,17 @@ final class EnterPinCodeViewController: UIViewController { self.explanatoryLabel.isHidden = true } + private func renderChangePin() { + self.inactiveView.isHidden = true + self.mainStackView.isHidden = false + self.logoImageView.isHidden = true + self.informationLabel.text = VectorL10n.pinProtectionConfirmPinToChange + self.explanatoryLabel.isHidden = true + self.forgotPinButton.isHidden = true + self.bottomView.isHidden = false + self.notAllowedPinView.isHidden = true + } + private func renderPlaceholdersCount(_ count: Int, error: Bool = false) { UIView.animate(withDuration: 0.3) { for case let imageView as UIImageView in self.placeholderStackView.arrangedSubviews { diff --git a/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewModel.swift b/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewModel.swift index c3d4e5435..92ff8e357 100644 --- a/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewModel.swift +++ b/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewModel.swift @@ -28,6 +28,7 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType { private var originalViewMode: SetPinCoordinatorViewMode private var viewMode: SetPinCoordinatorViewMode + private var initialPin: String = "" private var firstPin: String = "" private var currentPin: String = "" { didSet { @@ -116,49 +117,12 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType { switch viewMode { case .setPin, .setPinAfterLogin, .setPinAfterRegister: // choosing pin - if firstPin.isEmpty { - // check if this PIN is allowed - if pinCodePreferences.notAllowedPINs.contains(currentPin) { - viewMode = .notAllowedPin - update(viewState: .notAllowedPin) - return - } - // go to next screen - firstPin = currentPin - currentPin.removeAll() - update(viewState: .confirmPin) - } else { - // check first and second pins - if firstPin == currentPin { - // complete with a little delay - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - self.coordinatorDelegate?.enterPinCodeViewModel(self, didCompleteWithPin: self.firstPin) - } - } else { - update(viewState: .pinsDontMatch) - } - } + updateAfterPinSet() case .unlock, .confirmPinToDeactivate: // unlocking if currentPin != pinCodePreferences.pin { // no match - numberOfFailuresDuringEnterPIN += 1 - pinCodePreferences.numberOfPinFailures += 1 - if viewMode == .unlock && localAuthenticationService.shouldLogOutUser() { - // log out user - self.coordinatorDelegate?.enterPinCodeViewModelDidCompleteWithReset(self, dueToTooManyErrors: true) - return - } - if numberOfFailuresDuringEnterPIN < pinCodePreferences.allowedNumberOfTrialsBeforeAlert { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - self.viewDelegate?.enterPinCodeViewModel(self, didUpdateViewState: .wrongPin) - self.currentPin.removeAll() - } - } else { - viewDelegate?.enterPinCodeViewModel(self, didUpdateViewState: .wrongPinTooManyTimes) - numberOfFailuresDuringEnterPIN = 0 - currentPin.removeAll() - } + updateAfterUnlockFailed() } else { // match // we can use biometrics anymore, if set @@ -169,6 +133,22 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType { self.coordinatorDelegate?.enterPinCodeViewModelDidComplete(self) } } + case .changePin: + // unlocking + if initialPin.isEmpty && currentPin != pinCodePreferences.pin { + // no match + updateAfterUnlockFailed() + } else if initialPin.isEmpty { + // match or already unlocked + + // the user can choose a new Pin code + initialPin = currentPin + currentPin.removeAll() + update(viewState: .choosePin) + } else { + // choosing pin + updateAfterPinSet() + } default: break } @@ -185,6 +165,8 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType { return .choosePinAfterLogin case .setPinAfterRegister: return .choosePinAfterRegister + case .changePin: + return .changePin default: return .inactive } @@ -201,6 +183,8 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType { update(viewState: .confirmPinToDisable) case .inactive: update(viewState: .inactive) + case .changePin: + update(viewState: .changePin) default: break } @@ -209,4 +193,46 @@ final class EnterPinCodeViewModel: EnterPinCodeViewModelType { private func update(viewState: EnterPinCodeViewState) { self.viewDelegate?.enterPinCodeViewModel(self, didUpdateViewState: viewState) } + + private func updateAfterUnlockFailed() { + numberOfFailuresDuringEnterPIN += 1 + pinCodePreferences.numberOfPinFailures += 1 + if viewMode == .unlock && localAuthenticationService.shouldLogOutUser() { + // log out user + self.coordinatorDelegate?.enterPinCodeViewModelDidCompleteWithReset(self, dueToTooManyErrors: true) + return + } + if numberOfFailuresDuringEnterPIN < pinCodePreferences.allowedNumberOfTrialsBeforeAlert { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + self.viewDelegate?.enterPinCodeViewModel(self, didUpdateViewState: .wrongPin) + self.currentPin.removeAll() + } + } else { + viewDelegate?.enterPinCodeViewModel(self, didUpdateViewState: .wrongPinTooManyTimes) + numberOfFailuresDuringEnterPIN = 0 + currentPin.removeAll() + } + } + + private func updateAfterPinSet() { + if firstPin.isEmpty { + // check if this PIN is allowed + if pinCodePreferences.notAllowedPINs.contains(currentPin) { + viewMode = .notAllowedPin + update(viewState: .notAllowedPin) + return + } + // go to next screen + firstPin = currentPin + currentPin.removeAll() + update(viewState: .confirmPin) + } else if firstPin == currentPin { // check first and second pins + // complete with a little delay + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + self.coordinatorDelegate?.enterPinCodeViewModel(self, didCompleteWithPin: self.firstPin) + } + } else { + update(viewState: .pinsDontMatch) + } + } } diff --git a/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewState.swift b/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewState.swift index c9a1d033a..09ecfd8b3 100644 --- a/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewState.swift +++ b/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewState.swift @@ -32,4 +32,5 @@ enum EnterPinCodeViewState { case forgotPin // after pin has been set, user tapped forgot pin case confirmPinToDisable // after pin has been set, confirm pin to disable pin case inactive // inactive state, only used when app is not active + case changePin // pin is set, user tapped change pin from settings } diff --git a/Riot/Modules/SetPinCode/SetPinCoordinator.swift b/Riot/Modules/SetPinCode/SetPinCoordinator.swift index 181036e3f..5bc9ea80f 100644 --- a/Riot/Modules/SetPinCode/SetPinCoordinator.swift +++ b/Riot/Modules/SetPinCode/SetPinCoordinator.swift @@ -64,6 +64,8 @@ final class SetPinCoordinator: SetPinCoordinatorType { return createSetupBiometricsCoordinator() case .inactive: return createEnterPinCodeCoordinator() + case .changePin: + return createEnterPinCodeCoordinator() } } diff --git a/Riot/Modules/SetPinCode/SetPinCoordinatorBridgePresenter.swift b/Riot/Modules/SetPinCode/SetPinCoordinatorBridgePresenter.swift index 1ed733551..99319c6b3 100644 --- a/Riot/Modules/SetPinCode/SetPinCoordinatorBridgePresenter.swift +++ b/Riot/Modules/SetPinCode/SetPinCoordinatorBridgePresenter.swift @@ -29,6 +29,7 @@ import Foundation case setupBiometricsFromSettings case confirmBiometricsToDeactivate case inactive + case changePin } @objc protocol SetPinCoordinatorBridgePresenterDelegate { diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index a6cf815e6..fd01afb92 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -71,6 +71,7 @@ enum { enum { PIN_CODE_SETTING, PIN_CODE_DESCRIPTION, + PIN_CODE_CHANGE, PIN_CODE_BIOMETRICS, PIN_CODE_COUNT }; @@ -315,7 +316,10 @@ TableViewSectionsDelegate> // Rows [pinCodeSection addRowWithTag:PIN_CODE_SETTING]; [pinCodeSection addRowWithTag:PIN_CODE_DESCRIPTION]; - + if ([PinCodePreferences shared].isPinSet) { + [pinCodeSection addRowWithTag:PIN_CODE_CHANGE]; + } + if ([PinCodePreferences shared].isBiometricsAvailable) { [pinCodeSection addRowWithTag:PIN_CODE_BIOMETRICS]; @@ -1293,6 +1297,10 @@ TableViewSectionsDelegate> cell = [self descriptionCellForTableView:tableView withText:nil]; } } + else if (indexPath.row == PIN_CODE_CHANGE) + { + cell = [self buttonCellWithTitle:NSLocalizedStringFromTable(@"pin_protection_settings_change_pin", @"Vector", nil) action:@selector(changePinCode: ) forTableView:tableView atIndexPath:indexPath]; + } else if (indexPath.row == PIN_CODE_BIOMETRICS) { MXKTableViewCellWithLabelAndSwitch *switchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; @@ -1708,6 +1716,14 @@ TableViewSectionsDelegate> [self.setPinCoordinatorBridgePresenter presentFrom:self animated:YES]; } +- (void)changePinCode:(UIButton *)sender +{ + SetPinCoordinatorViewMode viewMode = SetPinCoordinatorViewModeChangePin; + self.setPinCoordinatorBridgePresenter = [[SetPinCoordinatorBridgePresenter alloc] initWithSession:self.mainSession viewMode:viewMode]; + self.setPinCoordinatorBridgePresenter.delegate = self; + [self.setPinCoordinatorBridgePresenter presentFrom:self animated:YES]; +} + #pragma mark - SettingsKeyBackupTableViewSectionDelegate #ifdef CROSS_SIGNING_AND_BACKUP_DEV - (void)settingsKeyBackupTableViewSectionDidUpdate:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection diff --git a/Riot/SupportingFiles/Info.plist b/Riot/SupportingFiles/Info.plist index efd56a7e9..5bcda43e6 100644 --- a/Riot/SupportingFiles/Info.plist +++ b/Riot/SupportingFiles/Info.plist @@ -20,12 +20,30 @@ $(MARKETING_VERSION) CFBundleSignature ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + $(BASE_BUNDLE_IDENTIFIER) + CFBundleURLSchemes + + $(APPLICATION_SCHEME) + + + CFBundleVersion $(CURRENT_PROJECT_VERSION) ITSAppUsesNonExemptEncryption ITSEncryptionExportComplianceCode d1dd539c-d21c-43e2-92e2-212c5269565c + LSApplicationQueriesSchemes + + http + https + LSRequiresIPhoneOS NSAppTransportSecurity @@ -94,10 +112,5 @@ $(BASE_BUNDLE_IDENTIFIER) keychainAccessGroup $(KEYCHAIN_ACCESS_GROUP) - LSApplicationQueriesSchemes - - http - https - diff --git a/RiotShareExtension/SupportingFiles/RiotShareExtension-Bridging-Header.h b/RiotShareExtension/SupportingFiles/RiotShareExtension-Bridging-Header.h index 1b2cb5d6d..7047b9786 100644 --- a/RiotShareExtension/SupportingFiles/RiotShareExtension-Bridging-Header.h +++ b/RiotShareExtension/SupportingFiles/RiotShareExtension-Bridging-Header.h @@ -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"