mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-18 23:48:29 +02:00
# Conflicts: # .github/workflows/ci-build.yml # .github/workflows/ci-tests.yml # .github/workflows/release-alpha.yml # .github/workflows/triage-move-labelled.yml # .github/workflows/triage-priority-bugs.yml # .gitignore # CHANGES.md # Config/AppConfiguration.swift # Config/AppIdentifiers.xcconfig # Config/AppVersion.xcconfig # Config/BuildSettings.swift # Config/CommonConfiguration.swift # Gemfile # Gemfile.lock # IDETemplateMacros.plist # Podfile # Podfile.lock # README.md # Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme # Riot/Assets/Images.xcassets/Common/reveal_password_button.imageset/reveal_password_button.png # Riot/Assets/Images.xcassets/Common/reveal_password_button.imageset/reveal_password_button@2x.png # Riot/Assets/Images.xcassets/Common/reveal_password_button.imageset/reveal_password_button@3x.png # Riot/Assets/Images.xcassets/People/people_floating_action.imageset/Contents.json # Riot/Assets/Images.xcassets/Rooms/rooms_floating_action.imageset/Contents.json # Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_key.imageset/Contents.json # Riot/Assets/Images.xcassets/Secrets/Recovery/secrets_recovery_passphrase.imageset/Contents.json # Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_key.imageset/Contents.json # Riot/Assets/Images.xcassets/Secrets/Setup/secrets_setup_passphrase.imageset/Contents.json # Riot/Assets/Images.xcassets/TabBar/tab_people.imageset/Contents.json # Riot/Assets/Images.xcassets/TabBar/tab_rooms.imageset/Contents.json # Riot/Assets/SharedImages.xcassets/AppIcon.appiconset/Contents.json # Riot/Assets/SharedImages.xcassets/horizontal_logo.imageset/Contents.json # Riot/Assets/ar.lproj/InfoPlist.strings # Riot/Assets/cs.lproj/Vector.strings # Riot/Assets/de.lproj/InfoPlist.strings # Riot/Assets/de.lproj/Localizable.strings # Riot/Assets/de.lproj/Vector.strings # Riot/Assets/en.lproj/Localizable.strings # Riot/Assets/en.lproj/Untranslated.strings # Riot/Assets/en.lproj/Vector.strings # Riot/Assets/es.lproj/InfoPlist.strings # Riot/Assets/es.lproj/Vector.strings # Riot/Assets/et.lproj/InfoPlist.strings # Riot/Assets/et.lproj/Vector.strings # Riot/Assets/fr.lproj/InfoPlist.strings # Riot/Assets/fr.lproj/Vector.strings # Riot/Assets/hu.lproj/InfoPlist.strings # Riot/Assets/hu.lproj/Vector.strings # Riot/Assets/id.lproj/InfoPlist.strings # Riot/Assets/id.lproj/Vector.strings # Riot/Assets/is.lproj/InfoPlist.strings # Riot/Assets/is.lproj/Vector.strings # Riot/Assets/it.lproj/InfoPlist.strings # Riot/Assets/it.lproj/Vector.strings # Riot/Assets/ja.lproj/InfoPlist.strings # Riot/Assets/ja.lproj/Localizable.strings # Riot/Assets/ja.lproj/Vector.strings # Riot/Assets/nl.lproj/InfoPlist.strings # Riot/Assets/nl.lproj/Vector.strings # Riot/Assets/pl.lproj/InfoPlist.strings # Riot/Assets/pl.lproj/Vector.strings # Riot/Assets/pt_BR.lproj/InfoPlist.strings # Riot/Assets/pt_BR.lproj/Vector.strings # Riot/Assets/ru.lproj/InfoPlist.strings # Riot/Assets/ru.lproj/Vector.strings # Riot/Assets/sk.lproj/InfoPlist.strings # Riot/Assets/sk.lproj/Vector.strings # Riot/Assets/sq.lproj/InfoPlist.strings # Riot/Assets/sq.lproj/Vector.strings # Riot/Assets/sv.lproj/InfoPlist.strings # Riot/Assets/sv.lproj/Vector.strings # Riot/Assets/third_party_licenses.html # Riot/Assets/uk.lproj/InfoPlist.strings # Riot/Assets/uk.lproj/Vector.strings # Riot/Assets/zh_Hans.lproj/InfoPlist.strings # Riot/Assets/zh_Hans.lproj/Localizable.strings # Riot/Assets/zh_Hans.lproj/Vector.strings # Riot/Assets/zh_Hant.lproj/Vector.strings # Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h # Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m # Riot/Categories/MXRestClient+Async.swift # Riot/Categories/MXSession+Riot.m # Riot/Categories/NSAttributedString.swift # Riot/Categories/Publisher+Riot.swift # Riot/Categories/RoomBubbleCellData.swift # Riot/Categories/UILabel.swift # Riot/Categories/UIScrollView.swift # Riot/Categories/UIView.swift # Riot/Categories/UIViewController.swift # Riot/Generated/Images.swift # Riot/Generated/Strings.swift # Riot/Generated/UntranslatedStrings.swift # Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift # Riot/Managers/LocalAuthentication/LocalAuthenticationService.swift # Riot/Managers/PushNotification/PushNotificationService.m # Riot/Managers/PushNotification/PushNotificationStore.swift # Riot/Managers/Settings/RiotSettings.swift # Riot/Managers/Settings/Shared/RiotSharedSettings.swift # Riot/Managers/Theme/Themes/DarkTheme.swift # Riot/Managers/Theme/Themes/DefaultTheme.swift # Riot/Managers/UISIAutoReporter/UISIAutoReporter.swift # Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift # Riot/Modules/Analytics/Analytics.swift # Riot/Modules/Analytics/AnalyticsUIElement.swift # Riot/Modules/Analytics/DecryptionFailureTracker.m # Riot/Modules/Application/AppCoordinator.swift # Riot/Modules/Application/LegacyAppDelegate.h # Riot/Modules/Application/LegacyAppDelegate.m # Riot/Modules/Authentication/AuthenticationCoordinatorProtocol.swift # Riot/Modules/Common/Avatar/AvatarView.swift # Riot/Modules/Common/Recents/DataSources/RecentsDataSource.h # Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m # Riot/Modules/Common/Recents/RecentsViewController.h # Riot/Modules/Common/Recents/RecentsViewController.m # Riot/Modules/Common/Recents/Views/RecentTableViewCell.m # Riot/Modules/Common/SwiftUI/VectorHostingController.swift # Riot/Modules/Common/Toasts/RoundedToastView.swift # Riot/Modules/Common/Toasts/ToastViewState.swift # Riot/Modules/Common/UserIndicators/UserIndicatorPresenter.swift # Riot/Modules/Common/UserIndicators/UserIndicatorStore.swift # Riot/Modules/Common/UserIndicators/ViewPresenters/ToastViewPresenter.swift # Riot/Modules/Common/WebViewController/WebViewViewController.m # Riot/Modules/Communities/Home/GroupHomeViewController.m # Riot/Modules/Communities/Members/GroupParticipantsViewController.m # Riot/Modules/Communities/Rooms/GroupRoomsViewController.m # Riot/Modules/Contacts/ContactsTableViewController.m # Riot/Modules/Contacts/ContactsTableViewController.xib # Riot/Modules/Contacts/Details/ContactDetailsViewController.m # Riot/Modules/Contacts/Views/ContactTableViewCell.m # Riot/Modules/Contacts/Views/ContactTableViewCell.xib # Riot/Modules/ContextMenu/ActionProviders/RoomActionProvider.swift # Riot/Modules/ContextMenu/Services/RoomContextActionService.swift # Riot/Modules/CreateRoom/CreateRoomCoordinator.swift # Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsCoordinator.swift # Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift # Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift # Riot/Modules/DeepLink/UniversalLinkParameters.swift # Riot/Modules/Favorites/FavouritesViewController.h # Riot/Modules/Favorites/FavouritesViewController.m # Riot/Modules/GlobalSearch/UnifiedSearchViewController.m # Riot/Modules/Home/HomeViewController.m # Riot/Modules/Home/Views/RoomCollectionViewCell.m # Riot/Modules/Integrations/WidgetPicker/WidgetPickerViewController.m # Riot/Modules/Integrations/Widgets/StickerPicker/StickerPickerViewController.m # Riot/Modules/KeyBackup/ManualExport/EncryptionKeysExportPresenter.swift # Riot/Modules/KeyVerification/Common/KeyVerificationCoordinator.swift # Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewController.swift # Riot/Modules/KeyVerification/Device/SelfVerifyWait/KeyVerificationSelfVerifyWaitCoordinator.swift # Riot/Modules/KeyVerification/Device/SelfVerifyWait/KeyVerificationSelfVerifyWaitViewController.storyboard # Riot/Modules/KeyVerification/Device/SelfVerifyWait/KeyVerificationSelfVerifyWaitViewController.swift # Riot/Modules/KeyVerification/User/SessionStatus/UserVerificationSessionStatusViewController.swift # Riot/Modules/LocationSharing/LocationManager.swift # Riot/Modules/LocationSharing/UserLocationService.swift # Riot/Modules/LocationSharing/UserLocationServiceProvider.swift # Riot/Modules/MatrixKit/Categories/NSBundle+MatrixKit.m # Riot/Modules/MatrixKit/Controllers/MXKAccountDetailsViewController.m # Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m # Riot/Modules/MatrixKit/Controllers/MXKRoomMemberDetailsViewController.h # Riot/Modules/MatrixKit/Controllers/MXKViewController.h # Riot/Modules/MatrixKit/Models/Account/MXKAccount.h # Riot/Modules/MatrixKit/Models/Account/MXKAccount.m # Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.h # Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m # Riot/Modules/MatrixKit/Models/Contact/MXKContact.h # Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleCellData.h # Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleCellData.m # Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleCellDataStoring.h # Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleCellDataWithAppendingMode.m # Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleComponent.h # Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleComponent.m # Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m # Riot/Modules/MatrixKit/Models/Room/MXKSendReplyEventStringLocalizer.swift # Riot/Modules/MatrixKit/Utils/ErrorPresentation/MXKErrorPresentableBuilder.m # Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.h # Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m # Riot/Modules/MatrixKit/Utils/MXKTools.h # Riot/Modules/MatrixKit/Utils/MXKTools.m # Riot/Modules/MatrixKit/Views/Account/MXKAccountTableViewCell.m # Riot/Modules/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithLabelAndTextField.xib # Riot/Modules/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithTextFieldAndButton.m # Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.m # Riot/Modules/MatrixKit/Views/RoomMemberList/MXKRoomMemberTableViewCell.m # Riot/Modules/MediaPicker/MediaPickerViewController.m # Riot/Modules/MediaPicker/SingleImagePickerPresenter.swift # Riot/Modules/MediaPickerV2/MediaPickerPresenter.swift # Riot/Modules/Onboarding/OnboardingCoordinator.swift # Riot/Modules/Onboarding/OnboardingCoordinatorBridgePresenter.swift # Riot/Modules/Onboarding/OnboardingCoordinatorProtocol.swift # Riot/Modules/People/PeopleViewController.h # Riot/Modules/People/PeopleViewController.m # Riot/Modules/Pills/PillTextAttachment.swift # Riot/Modules/Pills/PillsFormatter.swift # Riot/Modules/QRCode/QRCodeGenerator.swift # Riot/Modules/Room/CellData/RoomBubbleCellData.m # Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift # Riot/Modules/Room/CreationModal/RoomCreationEventsModal/RoomCreationEventsModalCoordinator.swift # Riot/Modules/Room/CreationModal/RoomCreationEventsModal/RoomCreationEventsModalViewModel.swift # Riot/Modules/Room/CreationModal/RoomCreationModalCoordinatorBridgePresenter.swift # Riot/Modules/Room/DataSources/RoomDataSource.m # Riot/Modules/Room/DataSources/RoomDataSource.swift # Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift # Riot/Modules/Room/Files/RoomFilesViewController.m # Riot/Modules/Room/Location/RoomTimelineLocationView.swift # Riot/Modules/Room/Location/RoomTimelineLocationView.xib # Riot/Modules/Room/MXKRoomViewController.h # Riot/Modules/Room/MXKRoomViewController.m # Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.h # Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m # Riot/Modules/Room/Members/RoomParticipantsViewController.h # Riot/Modules/Room/Members/RoomParticipantsViewController.m # Riot/Modules/Room/ParticipantsInviteModal/ContactsPicker/ContactsPickerViewModel.swift # Riot/Modules/Room/RoomCoordinator.swift # Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift # Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewAction.swift # Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift # Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewModel.swift # Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewModelType.swift # Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewState.swift # Riot/Modules/Room/RoomInfo/RoomInfoList/Views/RoomInfoBasicView.swift # Riot/Modules/Room/RoomViewController.m # Riot/Modules/Room/Settings/RoomSettingsViewController.m # Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellContentView.xib # Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.m # Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCell.swift # Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCellContentView.swift # Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroViewData.swift # Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipCollapsedBubbleCell.m # Riot/Modules/Room/TimelineCells/RoomTimelineCellIdentifier.h # Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m # Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineStyle.swift # Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailBaseBubbleCell.swift # Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.swift # Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithPaginationTitleBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationPlainCell.swift # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollPlainCell.swift # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Sticker/RoomSelectedStickerBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.xib # Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m # Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineStyle.swift # Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyle.swift # Riot/Modules/Room/TimelineDecorations/Reactions/RoomReactionActionViewCell.swift # Riot/Modules/Room/TimelineDecorations/Reactions/RoomReactionActionViewCell.xib # Riot/Modules/Room/TimelineDecorations/Reactions/RoomReactionViewCell.xib # Riot/Modules/Room/TimelineDecorations/Reactions/RoomReactionsView.swift # Riot/Modules/Room/TimelineDecorations/Reactions/RoomReactionsViewModel.swift # Riot/Modules/Room/TimelineDecorations/Reactions/RoomReactionsViewModelType.swift # Riot/Modules/Room/TimelineDecorations/Threads/Summary/ThreadSummaryView.swift # Riot/Modules/Room/VoiceMessages/VoiceMessageAudioConverter.swift # Riot/Modules/Room/VoiceMessages/VoiceMessageAudioRecorder.swift # Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.swift # Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.xib # Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.swift # Riot/Modules/Rooms/RoomsViewController.h # Riot/Modules/Rooms/ShowDirectory/Cells/Network/DirectoryNetworkTableHeaderFooterView.swift # Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCell.swift # Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCell.xib # Riot/Modules/Rooms/ShowDirectory/PublicRoomsDirectoryViewModel.swift # Riot/Modules/Rooms/ShowDirectory/ShowDirectoryViewController.swift # Riot/Modules/Rooms/ShowDirectory/ShowDirectoryViewModel.swift # Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift # Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift # Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift # Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift # Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift # Riot/Modules/Secrets/Reset/SecretsResetViewController.storyboard # Riot/Modules/Secrets/Reset/SecretsResetViewController.swift # Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewController.swift # Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.storyboard # Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewController.swift # Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewController.swift # Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModel.swift # Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModelType.swift # Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinator.swift # Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewController.storyboard # Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewController.swift # Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewModel.swift # Riot/Modules/SetPinCode/PinCodePreferences.swift # Riot/Modules/SetPinCode/SetPinCoordinator.swift # Riot/Modules/SetPinCode/SetPinCoordinatorBridgePresenter.swift # Riot/Modules/SetPinCode/SetupBiometrics/BiometricsAuthenticationPresenter.swift # Riot/Modules/SetPinCode/SetupBiometrics/SetupBiometricsCoordinator.swift # Riot/Modules/SetPinCode/SetupBiometrics/SetupBiometricsViewController.swift # Riot/Modules/SetPinCode/SetupBiometrics/SetupBiometricsViewModel.swift # Riot/Modules/Settings/DeactivateAccount/DeactivateAccountViewController.m # Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m # Riot/Modules/Settings/Security/SecurityViewController.m # Riot/Modules/Settings/SettingsViewController.m # Riot/Modules/SideMenu/SideMenuCoordinator.swift # Riot/Modules/SideMenu/SideMenuViewModel.swift # Riot/Modules/Spaces/SpaceList/SpaceListViewModel.swift # Riot/Modules/Spaces/SpaceMenu/SpaceMenuPresenter.swift # Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift # Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinator.swift # Riot/Modules/SplitView/SplitViewCoordinator.swift # Riot/Modules/SplitView/SplitViewCoordinatorType.swift # Riot/Modules/StartChat/StartChatViewController.m # Riot/Modules/TabBar/MasterTabBarController.h # Riot/Modules/TabBar/MasterTabBarController.m # Riot/Modules/TabBar/TabBarCoordinator.swift # Riot/Modules/TabBar/TabBarCoordinatorType.swift # Riot/Modules/Threads/ThreadList/ThreadListViewModel.swift # Riot/SupportingFiles/Info.plist # Riot/SupportingFiles/Riot-Bridging-Header.h # Riot/SupportingFiles/Riot.entitlements # Riot/Utils/EventFormatter+DTCoreTextFix.m # Riot/Utils/EventFormatter.m # Riot/Utils/Tools.h # Riot/Utils/Tools.m # Riot/Utils/URLValidator.swift # Riot/Utils/UniversalLink.h # Riot/Utils/UniversalLink.m # Riot/target.yml # RiotNSE/NotificationService.swift # RiotNSE/RiotNSE.entitlements # RiotNSE/target.yml # RiotShareExtension/Shared/View/ShareViewController.m # RiotShareExtension/SupportingFiles/Info.plist # RiotShareExtension/target.yml # RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptViewModel.swift # RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptCoordinator.swift # RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptStrings.swift # RiotSwiftUI/Modules/AnalyticsPrompt/MockAnalyticsPromptScreenState.swift # RiotSwiftUI/Modules/AnalyticsPrompt/Test/UI/AnalyticsPromptUITests.swift # RiotSwiftUI/Modules/AnalyticsPrompt/View/AnalyticsPrompt.swift # RiotSwiftUI/Modules/AnalyticsPrompt/View/AnalyticsPromptCheckmarkItem.swift # RiotSwiftUI/Modules/Authentication/Common/AuthenticationHomeserverViewData.swift # RiotSwiftUI/Modules/Authentication/Common/AuthenticationModels.swift # RiotSwiftUI/Modules/Authentication/Common/AuthenticationServerInfoSection.swift # RiotSwiftUI/Modules/Authentication/Common/Service/MatrixSDK/AuthenticationRestClient.swift # RiotSwiftUI/Modules/Authentication/Common/Service/MatrixSDK/AuthenticationService.swift # RiotSwiftUI/Modules/Authentication/Common/Service/MatrixSDK/AuthenticationState.swift # RiotSwiftUI/Modules/Authentication/Common/Service/MatrixSDK/LoginModels.swift # RiotSwiftUI/Modules/Authentication/Common/Service/MatrixSDK/LoginParameters.swift # RiotSwiftUI/Modules/Authentication/Common/Service/MatrixSDK/LoginWizard.swift # RiotSwiftUI/Modules/Authentication/Common/Service/MatrixSDK/RegistrationParameters.swift # RiotSwiftUI/Modules/Authentication/Common/Service/MatrixSDK/RegistrationWizard.swift # RiotSwiftUI/Modules/Authentication/Common/Service/MatrixSDK/SessionCreator.swift # RiotSwiftUI/Modules/Authentication/Common/Service/MatrixSDK/ThreePIDModels.swift # RiotSwiftUI/Modules/Authentication/Login/AuthenticationLoginModels.swift # RiotSwiftUI/Modules/Authentication/Login/Coordinator/AuthenticationLoginCoordinator.swift # RiotSwiftUI/Modules/Authentication/Login/Test/UI/AuthenticationLoginUITests.swift # RiotSwiftUI/Modules/Authentication/Login/Test/Unit/AuthenticationLoginViewModelTests.swift # RiotSwiftUI/Modules/Authentication/Login/View/AuthenticationLoginScreen.swift # RiotSwiftUI/Modules/Authentication/ReCaptcha/Test/UI/AuthenticationReCaptchaUITests.swift # RiotSwiftUI/Modules/Authentication/ReCaptcha/View/AuthenticationReCaptchaScreen.swift # RiotSwiftUI/Modules/Authentication/ReCaptcha/View/AuthenticationReCaptchaWebView.swift # RiotSwiftUI/Modules/Authentication/Registration/AuthenticationRegistrationModels.swift # RiotSwiftUI/Modules/Authentication/Registration/AuthenticationRegistrationViewModel.swift # RiotSwiftUI/Modules/Authentication/Registration/AuthenticationRegistrationViewModelProtocol.swift # RiotSwiftUI/Modules/Authentication/Registration/Coordinator/AuthenticationRegistrationCoordinator.swift # RiotSwiftUI/Modules/Authentication/Registration/MockAuthenticationRegistrationScreenState.swift # RiotSwiftUI/Modules/Authentication/Registration/Test/UI/AuthenticationRegistrationUITests.swift # RiotSwiftUI/Modules/Authentication/Registration/Test/Unit/AuthenticationRegistrationViewModelTests.swift # RiotSwiftUI/Modules/Authentication/Registration/View/AuthenticationRegistrationScreen.swift # RiotSwiftUI/Modules/Authentication/ServerSelection/AuthenticationServerSelectionModels.swift # RiotSwiftUI/Modules/Authentication/ServerSelection/AuthenticationServerSelectionViewModel.swift # RiotSwiftUI/Modules/Authentication/ServerSelection/Coordinator/AuthenticationServerSelectionCoordinator.swift # RiotSwiftUI/Modules/Authentication/ServerSelection/MockAuthenticationServerSelectionScreenState.swift # RiotSwiftUI/Modules/Authentication/ServerSelection/Test/UI/AuthenticationServerSelectionUITests.swift # RiotSwiftUI/Modules/Authentication/ServerSelection/Test/Unit/AuthenticationServerSelectionViewModelTests.swift # RiotSwiftUI/Modules/Authentication/ServerSelection/View/AuthenticationServerSelectionScreen.swift # RiotSwiftUI/Modules/Authentication/Terms/AuthenticationTermsModels.swift # RiotSwiftUI/Modules/Authentication/Terms/AuthenticationTermsViewModel.swift # RiotSwiftUI/Modules/Authentication/Terms/Coordinator/AuthenticationTermsCoordinator.swift # RiotSwiftUI/Modules/Authentication/Terms/MockAuthenticationTermsScreenState.swift # RiotSwiftUI/Modules/Authentication/Terms/Test/UI/AuthenticationTermsUITests.swift # RiotSwiftUI/Modules/Authentication/Terms/View/AuthenticationTermsScreen.swift # RiotSwiftUI/Modules/Authentication/Terms/View/AuthenticationTermsToggleStyle.swift # RiotSwiftUI/Modules/Authentication/VerifyEmail/AuthenticationVerifyEmailModels.swift # RiotSwiftUI/Modules/Authentication/VerifyEmail/AuthenticationVerifyEmailViewModel.swift # RiotSwiftUI/Modules/Authentication/VerifyEmail/Coordinator/AuthenticationVerifyEmailCoordinator.swift # RiotSwiftUI/Modules/Authentication/VerifyEmail/MockAuthenticationVerifyEmailScreenState.swift # RiotSwiftUI/Modules/Authentication/VerifyEmail/Test/UI/AuthenticationVerifyEmailUITests.swift # RiotSwiftUI/Modules/Authentication/VerifyEmail/Test/Unit/AuthenticationVerifyEmailViewModelTests.swift # RiotSwiftUI/Modules/Authentication/VerifyEmail/View/AuthenticationVerifyEmailForm.swift # RiotSwiftUI/Modules/Authentication/VerifyEmail/View/AuthenticationVerifyEmailScreen.swift # RiotSwiftUI/Modules/Authentication/VerifyMsisdn/AuthenticationVerifyMsisdnModels.swift # RiotSwiftUI/Modules/Authentication/VerifyMsisdn/AuthenticationVerifyMsisdnViewModel.swift # RiotSwiftUI/Modules/Authentication/VerifyMsisdn/Coordinator/AuthenticationVerifyMsisdnCoordinator.swift # RiotSwiftUI/Modules/Authentication/VerifyMsisdn/MockAuthenticationVerifyMsisdnScreenState.swift # RiotSwiftUI/Modules/Authentication/VerifyMsisdn/Test/UI/AuthenticationVerifyMsisdnUITests.swift # RiotSwiftUI/Modules/Authentication/VerifyMsisdn/Test/Unit/AuthenticationVerifyMsisdnViewModelTests.swift # RiotSwiftUI/Modules/Authentication/VerifyMsisdn/View/AuthenticationVerifyMsisdnForm.swift # RiotSwiftUI/Modules/Common/ActivityIndicator/ActivityIndicator.swift # RiotSwiftUI/Modules/Common/ActivityIndicator/ActivityIndicatorModifier.swift # RiotSwiftUI/Modules/Common/Avatar/Service/MatrixSDK/AvatarService.swift # RiotSwiftUI/Modules/Common/Avatar/Service/Mock/MockAvatarService.swift # RiotSwiftUI/Modules/Common/Avatar/View/AvatarImage.swift # RiotSwiftUI/Modules/Common/Avatar/View/PlaceholderAvatarImage.swift # RiotSwiftUI/Modules/Common/Avatar/View/SpaceAvatarImage.swift # RiotSwiftUI/Modules/Common/Avatar/ViewModel/AvatarServiceProtocol.swift # RiotSwiftUI/Modules/Common/Avatar/ViewModel/AvatarViewModel.swift # RiotSwiftUI/Modules/Common/Bridging/VectorContentView.swift # RiotSwiftUI/Modules/Common/DependencyInjection/DependencyContainerKey.swift # RiotSwiftUI/Modules/Common/EffectsScene/EffectsScene.swift # RiotSwiftUI/Modules/Common/EffectsScene/EffectsView.swift # RiotSwiftUI/Modules/Common/Extensions/Publisher.swift # RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift # RiotSwiftUI/Modules/Common/Mock/MockScreenState.swift # RiotSwiftUI/Modules/Common/Mock/ScreenList.swift # RiotSwiftUI/Modules/Common/Mock/ScreenStateInfo.swift # RiotSwiftUI/Modules/Common/Mock/StateRenderer.swift # RiotSwiftUI/Modules/Common/Test/UI/MockScreenTest.swift # RiotSwiftUI/Modules/Common/Test/UI/XCUIApplication+Riot.swift # RiotSwiftUI/Modules/Common/Test/XCTestPublisherExtensions.swift # RiotSwiftUI/Modules/Common/Theme/ThemeIdentifierExtensions.swift # RiotSwiftUI/Modules/Common/Theme/ThemeKey.swift # RiotSwiftUI/Modules/Common/Theme/ThemePublisher.swift # RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift # RiotSwiftUI/Modules/Common/Theme/ThemeUsersColorsExtension.swift # RiotSwiftUI/Modules/Common/Theme/Themes/DarkThemeSwiftUI.swift # RiotSwiftUI/Modules/Common/Theme/Themes/DefaultThemeSwiftUI.swift # RiotSwiftUI/Modules/Common/Util/BorderModifier.swift # RiotSwiftUI/Modules/Common/Util/BorderedInputFieldStyle.swift # RiotSwiftUI/Modules/Common/Util/ClearViewModifier.swift # RiotSwiftUI/Modules/Common/Util/InlineTextButton.swift # RiotSwiftUI/Modules/Common/Util/MultilineTextField.swift # RiotSwiftUI/Modules/Common/Util/OptionButton.swift # RiotSwiftUI/Modules/Common/Util/PrimaryActionButtonStyle.swift # RiotSwiftUI/Modules/Common/Util/RadioButton.swift # RiotSwiftUI/Modules/Common/Util/RoundedBorderTextEditor.swift # RiotSwiftUI/Modules/Common/Util/RoundedBorderTextField.swift # RiotSwiftUI/Modules/Common/Util/RoundedCornerShape.swift # RiotSwiftUI/Modules/Common/Util/SafeBindingCollectionEnumerator.swift # RiotSwiftUI/Modules/Common/Util/ScreenTrackerViewModifier.swift # RiotSwiftUI/Modules/Common/Util/SearchBar.swift # RiotSwiftUI/Modules/Common/Util/SecondaryActionButtonStyle.swift # RiotSwiftUI/Modules/Common/Util/StyledText.swift # RiotSwiftUI/Modules/Common/Util/ThemableButton.swift # RiotSwiftUI/Modules/Common/Util/ThemableNavigationBar.swift # RiotSwiftUI/Modules/Common/Util/ThemableTextEditor.swift # RiotSwiftUI/Modules/Common/Util/ThemableTextField.swift # RiotSwiftUI/Modules/Common/Util/WaitOverlay.swift # RiotSwiftUI/Modules/Common/ViewFrameReader/FramePreferenceKey.swift # RiotSwiftUI/Modules/Common/ViewFrameReader/ViewFrameReader.swift # RiotSwiftUI/Modules/Common/ViewModel/StateStoreViewModel.swift # RiotSwiftUI/Modules/Onboarding/Avatar/Coordinator/OnboardingAvatarCoordinator.swift # RiotSwiftUI/Modules/Onboarding/Avatar/MockOnboardingAvatarScreenState.swift # RiotSwiftUI/Modules/Onboarding/Avatar/Test/UI/OnboardingAvatarUITests.swift # RiotSwiftUI/Modules/Onboarding/Avatar/Test/Unit/OnboardingAvatarViewModelTests.swift # RiotSwiftUI/Modules/Onboarding/Celebration/Test/UI/OnboardingCelebrationUITests.swift # RiotSwiftUI/Modules/Onboarding/Celebration/View/OnboardingCelebrationScreen.swift # RiotSwiftUI/Modules/Onboarding/Common/OnboardingIcon.swift # RiotSwiftUI/Modules/Onboarding/Common/OnboardingMetrics.swift # RiotSwiftUI/Modules/Onboarding/Congratulations/OnboardingCongratulationsModels.swift # RiotSwiftUI/Modules/Onboarding/Congratulations/Test/UI/OnboardingCongratulationsUITests.swift # RiotSwiftUI/Modules/Onboarding/Congratulations/View/OnboardingCongratulationsScreen.swift # RiotSwiftUI/Modules/Onboarding/DisplayName/Coordinator/OnboardingDisplayNameCoordinator.swift # RiotSwiftUI/Modules/Onboarding/DisplayName/Test/UI/OnboardingDisplayNameUITests.swift # RiotSwiftUI/Modules/Onboarding/DisplayName/View/OnboardingDisplayNameScreen.swift # RiotSwiftUI/Modules/Onboarding/SplashScreen/OnboardingSplashScreenModels.swift # RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreen.swift # RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPage.swift # RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPageIndicator.swift # RiotSwiftUI/Modules/Onboarding/UseCase/Coordinator/OnboardingUseCaseSelectionCoordinator.swift # RiotSwiftUI/Modules/Onboarding/UseCase/OnboardingUseCaseModels.swift # RiotSwiftUI/Modules/Onboarding/UseCase/Test/UI/OnboardingUseCaseUITests.swift # RiotSwiftUI/Modules/Onboarding/UseCase/View/OnboardingUseCaseSelectionScreen.swift # RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/Coordinator/LiveLocationSharingViewerCoordinator.swift # RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/LiveLocationSharingViewerModels.swift # RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/LiveLocationSharingViewerViewModel.swift # RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/LiveLocationSharingViewerViewModelProtocol.swift # RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/MockLiveLocationSharingViewerScreenState.swift # RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/Service/LiveLocationSharingViewerServiceProtocol.swift # RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/Service/MatrixSDK/LiveLocationSharingViewerService.swift # RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/Service/Mock/MockLiveLocationSharingViewerService.swift # RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/Test/UI/LiveLocationSharingViewerUITests.swift # RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/Test/Unit/LiveLocationSharingViewerViewModelTests.swift # RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationListItem.swift # RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift # RiotSwiftUI/Modules/Room/LocationSharing/Coordinator/LocationSharingCoordinator.swift # RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingModels.swift # RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingScreenState.swift # RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingViewModel.swift # RiotSwiftUI/Modules/Room/LocationSharing/Service/MatrixSDK/LocationSharingService.swift # RiotSwiftUI/Modules/Room/LocationSharing/Service/Mock/MockLocationSharingService.swift # RiotSwiftUI/Modules/Room/LocationSharing/Test/UI/LocationSharingUITests.swift # RiotSwiftUI/Modules/Room/LocationSharing/Test/Unit/LocationSharingViewModelTests.swift # RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingMapView.swift # RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingMarkerView.swift # RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingOptionButton.swift # RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift # RiotSwiftUI/Modules/Room/LocationSharing/View/MapCreditsView.swift # RiotSwiftUI/Modules/Room/LocationSharing/View/UserLocationAnnotationView.swift # RiotSwiftUI/Modules/Room/NotificationSettings/Coordinator/RoomNotificationSettingsCoordinator.swift # RiotSwiftUI/Modules/Room/NotificationSettings/View/FormItemButtonStyle.swift # RiotSwiftUI/Modules/Room/NotificationSettings/View/FormPickerItem.swift # RiotSwiftUI/Modules/Room/NotificationSettings/View/FormSectionFooter.swift # RiotSwiftUI/Modules/Room/NotificationSettings/View/FormSectionHeader.swift # RiotSwiftUI/Modules/Room/NotificationSettings/View/RoomNotificationSettings.swift # RiotSwiftUI/Modules/Room/NotificationSettings/View/RoomNotificationSettingsHeader.swift # RiotSwiftUI/Modules/Room/NotificationSettings/View/VectorForm.swift # RiotSwiftUI/Modules/Room/NotificationSettings/ViewModel/RoomNotificationSettingsSwiftUIViewModel.swift # RiotSwiftUI/Modules/Room/PollEditForm/Coordinator/PollEditFormCoordinator.swift # RiotSwiftUI/Modules/Room/PollEditForm/PollEditFormScreenState.swift # RiotSwiftUI/Modules/Room/PollEditForm/PollEditFormViewModel.swift # RiotSwiftUI/Modules/Room/PollEditForm/Test/UI/PollEditFormUITests.swift # RiotSwiftUI/Modules/Room/PollEditForm/Test/Unit/PollEditFormViewModelTests.swift # RiotSwiftUI/Modules/Room/PollEditForm/View/PollEditForm.swift # RiotSwiftUI/Modules/Room/PollEditForm/View/PollEditFormAnswerOptionView.swift # RiotSwiftUI/Modules/Room/PollEditForm/View/PollEditFormTypePicker.swift # RiotSwiftUI/Modules/Room/RoomAccess/Coordinator/RoomAccessCoordinator.swift # RiotSwiftUI/Modules/Room/RoomAccess/Coordinator/RoomAccessCoordinatorBridgePresenter.swift # RiotSwiftUI/Modules/Room/RoomAccess/RoomAccessTypeChooser/Coordinator/RoomAccessTypeChooserCoordinator.swift # RiotSwiftUI/Modules/Room/RoomAccess/RoomAccessTypeChooser/MockRoomAccessTypeChooserScreenState.swift # RiotSwiftUI/Modules/Room/RoomAccess/RoomAccessTypeChooser/RoomAccessTypeChooserViewModel.swift # RiotSwiftUI/Modules/Room/RoomAccess/RoomAccessTypeChooser/RoomAccessTypeChooserViewModelProtocol.swift # RiotSwiftUI/Modules/Room/RoomAccess/RoomAccessTypeChooser/Service/MatrixSDK/RoomAccessTypeChooserService.swift # RiotSwiftUI/Modules/Room/RoomAccess/RoomAccessTypeChooser/Service/Mock/MockRoomAccessTypeChooserService.swift # RiotSwiftUI/Modules/Room/RoomAccess/RoomAccessTypeChooser/Service/RoomAccessTypeChooserServiceProtocol.swift # RiotSwiftUI/Modules/Room/RoomAccess/RoomAccessTypeChooser/Test/UI/RoomAccessTypeChooserUITests.swift # RiotSwiftUI/Modules/Room/RoomAccess/RoomAccessTypeChooser/Test/Unit/RoomAccessTypeChooserViewModelTests.swift # RiotSwiftUI/Modules/Room/RoomAccess/RoomAccessTypeChooser/View/RoomAccessTypeChooser.swift # RiotSwiftUI/Modules/Room/RoomAccess/RoomAccessTypeChooser/View/RoomAccessTypeChooserRow.swift # RiotSwiftUI/Modules/Room/RoomAccess/RoomRestrictedAccessSpaceChooser/Coordinator/RoomRestrictedAccessSpaceChooserViewProvider.swift # RiotSwiftUI/Modules/Room/RoomAccess/RoomRestrictedAccessSpaceChooser/View/RoomRestrictedAccessSpaceChooserSelector.swift # RiotSwiftUI/Modules/Room/RoomSuggestion/Coordinator/RoomSuggestionCoordinator.swift # RiotSwiftUI/Modules/Room/RoomSuggestion/Coordinator/RoomSuggestionCoordinatorBridgePresenter.swift # RiotSwiftUI/Modules/Room/RoomSuggestion/RoomSuggestionSpaceChooser/Coordinator/RoomSuggestionSpaceChooserViewProvider.swift # RiotSwiftUI/Modules/Room/RoomSuggestion/RoomSuggestionSpaceChooser/View/RoomSuggestionSpaceChooserSelector.swift # RiotSwiftUI/Modules/Room/RoomUpgrade/Coordinator/RoomUpgradeCoordinator.swift # RiotSwiftUI/Modules/Room/RoomUpgrade/MockRoomUpgradeScreenState.swift # RiotSwiftUI/Modules/Room/RoomUpgrade/RoomUpgradeViewModel.swift # RiotSwiftUI/Modules/Room/RoomUpgrade/RoomUpgradeViewModelProtocol.swift # RiotSwiftUI/Modules/Room/RoomUpgrade/Service/MatrixSDK/RoomUpgradeService.swift # RiotSwiftUI/Modules/Room/RoomUpgrade/Service/Mock/MockRoomUpgradeService.swift # RiotSwiftUI/Modules/Room/RoomUpgrade/Service/RoomUpgradeServiceProtocol.swift # RiotSwiftUI/Modules/Room/RoomUpgrade/Test/UI/RoomUpgradeUITests.swift # RiotSwiftUI/Modules/Room/RoomUpgrade/Test/Unit/RoomUpgradeViewModelTests.swift # RiotSwiftUI/Modules/Room/RoomUpgrade/View/RoomUpgrade.swift # RiotSwiftUI/Modules/Room/StaticLocationSharingViewer/Coordinator/StaticLocationViewingCoordinator.swift # RiotSwiftUI/Modules/Room/StaticLocationSharingViewer/MockStaticLocationViewingScreenState.swift # RiotSwiftUI/Modules/Room/StaticLocationSharingViewer/StaticLocationViewingModels.swift # RiotSwiftUI/Modules/Room/StaticLocationSharingViewer/StaticLocationViewingViewModel.swift # RiotSwiftUI/Modules/Room/StaticLocationSharingViewer/StaticLocationViewingViewModelProtocol.swift # RiotSwiftUI/Modules/Room/StaticLocationSharingViewer/Test/UI/StaticLocationViewingUITests.swift # RiotSwiftUI/Modules/Room/StaticLocationSharingViewer/Test/Unit/StaticLocationViewingViewModelTests.swift # RiotSwiftUI/Modules/Room/StaticLocationSharingViewer/View/StaticLocationView.swift # RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift # RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift # RiotSwiftUI/Modules/Room/TimelinePoll/Test/UI/TimelinePollUITests.swift # RiotSwiftUI/Modules/Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift # RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollScreenState.swift # RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollViewModel.swift # RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollViewModelProtocol.swift # RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollAnswerOptionButton.swift # RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift # RiotSwiftUI/Modules/Room/UserSuggestion/Coordinator/UserSuggestionCoordinator.swift # RiotSwiftUI/Modules/Room/UserSuggestion/Coordinator/UserSuggestionCoordinatorBridge.swift # RiotSwiftUI/Modules/Room/UserSuggestion/Service/UserSuggestionService.swift # RiotSwiftUI/Modules/Room/UserSuggestion/Service/UserSuggestionServiceProtocol.swift # RiotSwiftUI/Modules/Room/UserSuggestion/Test/UI/UserSuggestionUITests.swift # RiotSwiftUI/Modules/Room/UserSuggestion/Test/Unit/UserSuggestionServiceTests.swift # RiotSwiftUI/Modules/Room/UserSuggestion/UserSuggestionScreenState.swift # RiotSwiftUI/Modules/Room/UserSuggestion/UserSuggestionViewModel.swift # RiotSwiftUI/Modules/Room/UserSuggestion/View/UserSuggestionList.swift # RiotSwiftUI/Modules/Room/UserSuggestion/View/UserSuggestionListItem.swift # RiotSwiftUI/Modules/Room/UserSuggestion/View/UserSuggestionListWithInput.swift # RiotSwiftUI/Modules/Settings/Notifications/Coordinator/NotificationSettingsBridgePresenter.swift # RiotSwiftUI/Modules/Settings/Notifications/Coordinator/NotificationSettingsCoordinator.swift # RiotSwiftUI/Modules/Settings/Notifications/Model/NotificationSettingsScreen.swift # RiotSwiftUI/Modules/Settings/Notifications/Service/MatrixSDK/MXNotificationSettingsService.swift # RiotSwiftUI/Modules/Settings/Notifications/Service/Mock/MockNotificationSettingsService.swift # RiotSwiftUI/Modules/Settings/Notifications/Service/NotificationSettingsServiceType.swift # RiotSwiftUI/Modules/Settings/Notifications/View/Chip.swift # RiotSwiftUI/Modules/Settings/Notifications/View/Chips.swift # RiotSwiftUI/Modules/Settings/Notifications/View/ChipsInput.swift # RiotSwiftUI/Modules/Settings/Notifications/View/DefaultNotificationSettings.swift # RiotSwiftUI/Modules/Settings/Notifications/View/FormInputFieldStyle.swift # RiotSwiftUI/Modules/Settings/Notifications/View/MentionsAndKeywordNotificationSettings.swift # RiotSwiftUI/Modules/Settings/Notifications/View/NotificationSettings.swift # RiotSwiftUI/Modules/Settings/Notifications/View/NotificationSettingsKeywords.swift # RiotSwiftUI/Modules/Settings/Notifications/View/OtherNotificationSettings.swift # RiotSwiftUI/Modules/Settings/Notifications/ViewModel/NotificationSettingsViewModel.swift # RiotSwiftUI/Modules/Spaces/AddRoomSelector/Coordinator/AddRoomSelectorViewProvider.swift # RiotSwiftUI/Modules/Spaces/AddRoomSelector/View/AddRoomSelector.swift # RiotSwiftUI/Modules/Spaces/LeaveSpace/Coordinator/LeaveSpaceViewProvider.swift # RiotSwiftUI/Modules/Spaces/LeaveSpace/View/LeaveSpace.swift # RiotSwiftUI/Modules/Spaces/MatrixItemChooser/Coordinator/MatrixItemChooserCoordinator.swift # RiotSwiftUI/Modules/Spaces/MatrixItemChooser/MatrixItemChooserViewModel.swift # RiotSwiftUI/Modules/Spaces/MatrixItemChooser/MatrixItemChooserViewModelProtocol.swift # RiotSwiftUI/Modules/Spaces/MatrixItemChooser/MockMatrixItemChooserScreenState.swift # RiotSwiftUI/Modules/Spaces/MatrixItemChooser/Service/MatrixItemChooserServiceProtocol.swift # RiotSwiftUI/Modules/Spaces/MatrixItemChooser/Service/MatrixSDK/MatrixItemChooserService.swift # RiotSwiftUI/Modules/Spaces/MatrixItemChooser/Service/Mock/MockMatrixItemChooserService.swift # RiotSwiftUI/Modules/Spaces/MatrixItemChooser/Test/UI/MatrixItemChooserUITests.swift # RiotSwiftUI/Modules/Spaces/MatrixItemChooser/Test/Unit/MatrixItemChooserViewModelTests.swift # RiotSwiftUI/Modules/Spaces/MatrixItemChooser/View/MatrixItemChooser.swift # RiotSwiftUI/Modules/Spaces/MatrixItemChooser/View/MatrixItemChooserListRow.swift # RiotSwiftUI/Modules/Spaces/MatrixItemChooser/View/MatrixItemChooserSectionHeader.swift # RiotSwiftUI/Modules/Spaces/RoomAncestorSelector/Coordinator/RoomAncestorSelectorViewProvider.swift # RiotSwiftUI/Modules/Spaces/RoomAncestorSelector/View/RoomAncestorSelector.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/Coordinator/SpaceCreationCoordinator.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationEmailInvites/Coordinator/SpaceCreationEmailInvitesCoordinator.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationEmailInvites/Service/MatrixSDK/SpaceCreationEmailInvitesService.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationEmailInvites/Service/Mock/MockSpaceCreationEmailInvitesScreenState.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationEmailInvites/Service/Mock/MockSpaceCreationEmailInvitesService.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationEmailInvites/Service/SpaceCreationEmailInvitesServiceProtocol.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationEmailInvites/Test/UI/SpaceCreationEmailInvitesUITests.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationEmailInvites/Test/Unit/SpaceCreationEmailInvitesViewModelTests.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationEmailInvites/View/SpaceCreationEmailInvites.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationEmailInvites/ViewModel/SpaceCreationEmailInvitesViewModel.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationEmailInvites/ViewModel/SpaceCreationEmailInvitesViewModelProtocol.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationMatrixItemChooser/Coordinator/SpaceCreationMatrixItemChooserViewProvider.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationMatrixItemChooser/View/SpaceCreationMatrixItemChooser.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationMenu/Coordinator/SpaceCreationMenuCoordinator.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationMenu/Test/UI/SpaceCreationMenuUITests.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationMenu/Test/Unit/SpaceCreationMenuViewModelTests.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationMenu/View/SpaceCreationMenu.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationMenu/ViewModel/SpaceCreationMenuViewModel.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationMenu/ViewModel/SpaceCreationMenuViewModelProtocol.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationPostProcess/Coordinator/SpaceCreationPostProcessCoordinator.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationPostProcess/Service/MatrixSDK/SpaceCreationPostProcessService.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationPostProcess/Service/Mock/MockSpaceCreationPostProcessScreenState.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationPostProcess/Service/Mock/MockSpaceCreationPostProcessService.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationPostProcess/Service/SpaceCreationPostProcessServiceProtocol.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationPostProcess/Test/UI/SpaceCreationPostProcessUITests.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationPostProcess/Test/Unit/SpaceCreationPostProcessViewModelTests.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationPostProcess/View/SpaceCreationPostProcess.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationPostProcess/View/SpaceCreationPostProcessItem.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationPostProcess/ViewModel/SpaceCreationPostProcessViewModel.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationPostProcess/ViewModel/SpaceCreationPostProcessViewModelProtocol.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationRooms/Coordinator/SpaceCreationRoomsCoordinator.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationRooms/Service/Mock/MockSpaceCreationRoomsScreenState.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationRooms/Test/UI/SpaceCreationRoomsUITests.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationRooms/Test/Unit/SpaceCreationRoomsViewModelTests.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationRooms/View/SpaceCreationRooms.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationRooms/ViewModel/SpaceCreationRoomsViewModel.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationRooms/ViewModel/SpaceCreationRoomsViewModelProtocol.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationSettings/Coordinator/SpaceCreationSettingsCoordinator.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationSettings/Service/MatrixSDK/SpaceCreationSettingsService.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationSettings/Service/Mock/MockSpaceCreationSettingsScreenState.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationSettings/Service/Mock/MockSpaceCreationSettingsService.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationSettings/Service/SpaceCreationSettingsServiceProtocol.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationSettings/Test/UI/SpaceCreationSettingsUITests.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationSettings/Test/Unit/SpaceCreationSettingsViewModelTests.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationSettings/View/SpaceCreationSettings.swift # RiotSwiftUI/Modules/Spaces/SpaceCreation/SpaceCreationSettings/ViewModel/SpaceCreationSettingsViewModel.swift # RiotSwiftUI/Modules/Spaces/SpaceSettings/Coordinator/SpaceSettingsModalCoordinator.swift # RiotSwiftUI/Modules/Spaces/SpaceSettings/Coordinator/SpaceSettingsModalCoordinatorBridgePresenter.swift # RiotSwiftUI/Modules/Spaces/SpaceSettings/SpaceSettings/Coordinator/SpaceSettingsCoordinator.swift # RiotSwiftUI/Modules/Spaces/SpaceSettings/SpaceSettings/MockSpaceSettingsScreenState.swift # RiotSwiftUI/Modules/Spaces/SpaceSettings/SpaceSettings/Service/MatrixSDK/SpaceSettingsService.swift # RiotSwiftUI/Modules/Spaces/SpaceSettings/SpaceSettings/Service/Mock/MockSpaceSettingsService.swift # RiotSwiftUI/Modules/Spaces/SpaceSettings/SpaceSettings/Service/SpaceSettingsServiceProtocol.swift # RiotSwiftUI/Modules/Spaces/SpaceSettings/SpaceSettings/SpaceSettingsViewModel.swift # RiotSwiftUI/Modules/Spaces/SpaceSettings/SpaceSettings/SpaceSettingsViewModelProtocol.swift # RiotSwiftUI/Modules/Spaces/SpaceSettings/SpaceSettings/Test/UI/SpaceSettingsUITests.swift # RiotSwiftUI/Modules/Spaces/SpaceSettings/SpaceSettings/Test/Unit/SpaceSettingsViewModelTests.swift # RiotSwiftUI/Modules/Spaces/SpaceSettings/SpaceSettings/View/SpaceSettings.swift # RiotSwiftUI/Modules/Spaces/SpaceSettings/SpaceSettings/View/SpaceSettingsOptionListItem.swift # RiotSwiftUI/Modules/Template/SimpleScreenExample/Test/UI/TemplateSimpleScreenUITests.swift # RiotSwiftUI/Modules/Template/SimpleUserProfileExample/Test/UI/TemplateUserProfileUITests.swift # RiotSwiftUI/Modules/Template/TemplateAdvancedRoomsExample/TemplateRoomChat/Test/UI/TemplateRoomChatUITests.swift # RiotSwiftUI/Modules/Template/TemplateAdvancedRoomsExample/TemplateRoomList/Test/UI/TemplateRoomListUITests.swift # RiotSwiftUI/RiotSwiftUIApp.swift # RiotSwiftUI/target.yml # RiotSwiftUI/targetUITests.yml # RiotTests/MatrixKitTests/MXKEventFormatter+Tests.h # RiotTests/MatrixKitTests/MXKEventFormatterTests.m # RiotTests/MatrixKitTests/MXKRoomDataSourceTests.swift # RiotTests/MatrixKitTests/MatrixKitTests-Bridging-Header.h # RiotTests/Modules/Authentication/AuthenticationServiceTests.swift # RiotTests/OnboardingTests.swift # RiotTests/PillsFormatterTests.swift # RiotTests/target.yml # SiriIntents/IntentHandler.m # SiriIntents/target.yml # Tools/SwiftGen/swiftgen-config.yml # Tools/Templates/buildable/SimpleScreenTemplate/SimpleScreenTemplateViewController.storyboard # fastlane/.env.default # fastlane/Fastfile # project.yml
2227 lines
82 KiB
Objective-C
2227 lines
82 KiB
Objective-C
/*
|
||
Copyright 2015 OpenMarket Ltd
|
||
Copyright 2017 Vector Creations Ltd
|
||
Copyright 2019 The Matrix.org Foundation C.I.C
|
||
|
||
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 "MXKAuthenticationViewController.h"
|
||
|
||
#import "MXKAuthInputsEmailCodeBasedView.h"
|
||
#import "MXKAuthInputsPasswordBasedView.h"
|
||
|
||
#import "MXKAccountManager.h"
|
||
|
||
#import "NSBundle+MatrixKit.h"
|
||
|
||
#import <AFNetworking/AFNetworking.h>
|
||
#import "MXKAppSettings.h"
|
||
|
||
#import "MXKSwiftHeader.h"
|
||
|
||
#import "GeneratedInterface-Swift.h"
|
||
|
||
@interface MXKAuthenticationViewController ()
|
||
{
|
||
/**
|
||
The matrix REST client used to make matrix API requests.
|
||
*/
|
||
MXRestClient *mxRestClient;
|
||
|
||
/**
|
||
Current request in progress.
|
||
*/
|
||
MXHTTPOperation *mxCurrentOperation;
|
||
|
||
/**
|
||
The MXKAuthInputsView class or a sub-class used when logging in.
|
||
*/
|
||
Class loginAuthInputsViewClass;
|
||
|
||
/**
|
||
The MXKAuthInputsView class or a sub-class used when registering.
|
||
*/
|
||
Class registerAuthInputsViewClass;
|
||
|
||
/**
|
||
The MXKAuthInputsView class or a sub-class used to handle forgot password case.
|
||
*/
|
||
Class forgotPasswordAuthInputsViewClass;
|
||
|
||
/**
|
||
Customized block used to handle unrecognized certificate (nil by default).
|
||
*/
|
||
MXHTTPClientOnUnrecognizedCertificate onUnrecognizedCertificateCustomBlock;
|
||
|
||
/**
|
||
The current authentication fallback URL (if any).
|
||
*/
|
||
NSString *authenticationFallback;
|
||
|
||
/**
|
||
The cancel button added in navigation bar when fallback page is opened.
|
||
*/
|
||
UIBarButtonItem *cancelFallbackBarButton;
|
||
|
||
/**
|
||
The timer used to postpone the registration when the authentication is pending (for example waiting for email validation)
|
||
*/
|
||
NSTimer* registrationTimer;
|
||
|
||
/**
|
||
Identity server discovery.
|
||
*/
|
||
MXAutoDiscovery *autoDiscovery;
|
||
|
||
MXHTTPOperation *checkIdentityServerOperation;
|
||
}
|
||
|
||
/**
|
||
The identity service used to make identity server API requests.
|
||
*/
|
||
@property (nonatomic) MXIdentityService *identityService;
|
||
|
||
@property (nonatomic) AnalyticsScreenTracker *screenTracker;
|
||
|
||
@property (nonatomic) BOOL isViewVisible;
|
||
|
||
@end
|
||
|
||
@implementation MXKAuthenticationViewController
|
||
|
||
#pragma mark - Class methods
|
||
|
||
+ (UINib *)nib
|
||
{
|
||
return [UINib nibWithNibName:NSStringFromClass([MXKAuthenticationViewController class])
|
||
bundle:[NSBundle bundleForClass:[MXKAuthenticationViewController class]]];
|
||
}
|
||
|
||
+ (instancetype)authenticationViewController
|
||
{
|
||
return [[[self class] alloc] initWithNibName:NSStringFromClass([MXKAuthenticationViewController class])
|
||
bundle:[NSBundle bundleForClass:[MXKAuthenticationViewController class]]];
|
||
}
|
||
|
||
#pragma mark -
|
||
|
||
- (void)finalizeInit
|
||
{
|
||
[super finalizeInit];
|
||
|
||
// Set initial auth type
|
||
_authType = MXKAuthenticationTypeLogin;
|
||
|
||
_deviceDisplayName = nil;
|
||
|
||
// Initialize authInputs view classes
|
||
loginAuthInputsViewClass = MXKAuthInputsPasswordBasedView.class;
|
||
registerAuthInputsViewClass = nil; // No registration flow is supported yet
|
||
forgotPasswordAuthInputsViewClass = nil;
|
||
}
|
||
|
||
#pragma mark -
|
||
|
||
- (void)viewDidLoad
|
||
{
|
||
[super viewDidLoad];
|
||
// Do any additional setup after loading the view, typically from a nib.
|
||
|
||
// Check whether the view controller has been pushed via storyboard
|
||
if (!_authenticationScrollView)
|
||
{
|
||
// Instantiate view controller objects
|
||
[[[self class] nib] instantiateWithOwner:self options:nil];
|
||
}
|
||
|
||
self.authFallbackWebView = [[MXKAuthenticationFallbackWebView alloc] initWithFrame:self.authFallbackWebViewContainer.bounds];
|
||
[self.authFallbackWebViewContainer addSubview:self.authFallbackWebView];
|
||
[self.authFallbackWebView.leadingAnchor constraintEqualToAnchor:self.authFallbackWebViewContainer.leadingAnchor constant:0].active = YES;
|
||
[self.authFallbackWebView.trailingAnchor constraintEqualToAnchor:self.authFallbackWebViewContainer.trailingAnchor constant:0].active = YES;
|
||
[self.authFallbackWebView.topAnchor constraintEqualToAnchor:self.authFallbackWebViewContainer.topAnchor constant:0].active = YES;
|
||
[self.authFallbackWebView.bottomAnchor constraintEqualToAnchor:self.authFallbackWebViewContainer.bottomAnchor constant:0].active = YES;
|
||
|
||
// Load welcome image from MatrixKit asset bundle
|
||
self.welcomeImageView.image = [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"logoHighRes"];
|
||
|
||
_authenticationScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||
|
||
_subTitleLabel.numberOfLines = 0;
|
||
|
||
_submitButton.enabled = NO;
|
||
_authSwitchButton.enabled = YES;
|
||
|
||
_homeServerTextField.text = _defaultHomeServerUrl;
|
||
_identityServerTextField.text = _defaultIdentityServerUrl;
|
||
|
||
// Hide the identity server by default
|
||
[self setIdentityServerHidden:YES];
|
||
|
||
// Create here REST client (if homeserver is defined)
|
||
[self updateRESTClient];
|
||
|
||
// Localize labels
|
||
_homeServerLabel.text = [VectorL10n loginHomeServerTitle];
|
||
_homeServerTextField.placeholder = [VectorL10n loginServerUrlPlaceholder];
|
||
_homeServerInfoLabel.text = [VectorL10n loginHomeServerInfo];
|
||
_identityServerLabel.text = [VectorL10n loginIdentityServerTitle];
|
||
_identityServerTextField.placeholder = [VectorL10n loginServerUrlPlaceholder];
|
||
_identityServerInfoLabel.text = [VectorL10n loginIdentityServerInfo];
|
||
[_cancelAuthFallbackButton setTitle:[VectorL10n cancel] forState:UIControlStateNormal];
|
||
[_cancelAuthFallbackButton setTitle:[VectorL10n cancel] forState:UIControlStateHighlighted];
|
||
}
|
||
|
||
- (void)didReceiveMemoryWarning
|
||
{
|
||
[super didReceiveMemoryWarning];
|
||
// Dispose of any resources that can be recreated.
|
||
}
|
||
|
||
- (void)viewWillAppear:(BOOL)animated
|
||
{
|
||
[super viewWillAppear:animated];
|
||
|
||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onTextFieldChange:) name:UITextFieldTextDidChangeNotification object:nil];
|
||
|
||
self.isViewVisible = YES;
|
||
[self.screenTracker trackScreen];
|
||
}
|
||
|
||
- (void)viewWillDisappear:(BOOL)animated
|
||
{
|
||
[super viewWillDisappear:animated];
|
||
|
||
[self dismissKeyboard];
|
||
|
||
// close any opened alert
|
||
if (alert)
|
||
{
|
||
[alert dismissViewControllerAnimated:NO completion:nil];
|
||
alert = nil;
|
||
}
|
||
[[NSNotificationCenter defaultCenter] removeObserver:self name:AFNetworkingReachabilityDidChangeNotification object:nil];
|
||
|
||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil];
|
||
}
|
||
|
||
- (void)viewDidDisappear:(BOOL)animated
|
||
{
|
||
[super viewDidDisappear:animated];
|
||
|
||
self.isViewVisible = NO;
|
||
}
|
||
|
||
#pragma mark - Override MXKViewController
|
||
|
||
- (void)onKeyboardShowAnimationComplete
|
||
{
|
||
// Report the keyboard view in order to track keyboard frame changes
|
||
// TODO define inputAccessoryView for each text input
|
||
// and report the inputAccessoryView.superview of the firstResponder in self.keyboardView.
|
||
}
|
||
|
||
#pragma clang diagnostic push
|
||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||
- (void)setKeyboardHeight:(CGFloat)keyboardHeight
|
||
{
|
||
// Deduce the bottom inset for the scroll view (Don't forget the potential tabBar)
|
||
CGFloat scrollViewInsetBottom = keyboardHeight - self.bottomLayoutGuide.length;
|
||
// Check whether the keyboard is over the tabBar
|
||
if (scrollViewInsetBottom < 0)
|
||
{
|
||
scrollViewInsetBottom = 0;
|
||
}
|
||
|
||
UIEdgeInsets insets = self.authenticationScrollView.contentInset;
|
||
insets.bottom = scrollViewInsetBottom;
|
||
self.authenticationScrollView.contentInset = insets;
|
||
}
|
||
#pragma clang diagnostic pop
|
||
|
||
- (void)destroy
|
||
{
|
||
self.authInputsView = nil;
|
||
|
||
if (registrationTimer)
|
||
{
|
||
[registrationTimer invalidate];
|
||
registrationTimer = nil;
|
||
}
|
||
|
||
if (mxCurrentOperation)
|
||
{
|
||
[mxCurrentOperation cancel];
|
||
mxCurrentOperation = nil;
|
||
}
|
||
|
||
[self cancelIdentityServerCheck];
|
||
|
||
[mxRestClient close];
|
||
mxRestClient = nil;
|
||
|
||
authenticationFallback = nil;
|
||
cancelFallbackBarButton = nil;
|
||
|
||
[super destroy];
|
||
}
|
||
|
||
#pragma mark - Class methods
|
||
|
||
- (void)registerAuthInputsViewClass:(Class)authInputsViewClass forAuthType:(MXKAuthenticationType)authType
|
||
{
|
||
// Sanity check: accept only MXKAuthInputsView classes or sub-classes
|
||
NSParameterAssert([authInputsViewClass isSubclassOfClass:MXKAuthInputsView.class]);
|
||
|
||
if (authType == MXKAuthenticationTypeLogin)
|
||
{
|
||
loginAuthInputsViewClass = authInputsViewClass;
|
||
}
|
||
else if (authType == MXKAuthenticationTypeRegister)
|
||
{
|
||
registerAuthInputsViewClass = authInputsViewClass;
|
||
}
|
||
else if (authType == MXKAuthenticationTypeForgotPassword)
|
||
{
|
||
forgotPasswordAuthInputsViewClass = authInputsViewClass;
|
||
}
|
||
}
|
||
|
||
- (void)setAuthType:(MXKAuthenticationType)authType
|
||
{
|
||
if (_authType != authType)
|
||
{
|
||
_authType = authType;
|
||
|
||
// Cancel external registration parameters if any
|
||
_externalRegistrationParameters = nil;
|
||
|
||
// Remove the current inputs view
|
||
self.authInputsView = nil;
|
||
|
||
isPasswordReseted = NO;
|
||
|
||
[self.authInputsContainerView bringSubviewToFront: _authenticationActivityIndicator];
|
||
[_authenticationActivityIndicator startAnimating];
|
||
}
|
||
|
||
// Restore user interaction
|
||
self.userInteractionEnabled = YES;
|
||
|
||
if (authType == MXKAuthenticationTypeLogin)
|
||
{
|
||
_subTitleLabel.hidden = YES;
|
||
[_submitButton setTitle:[VectorL10n login] forState:UIControlStateNormal];
|
||
[_submitButton setTitle:[VectorL10n login] forState:UIControlStateHighlighted];
|
||
[_authSwitchButton setTitle:[VectorL10n createAccount] forState:UIControlStateNormal];
|
||
[_authSwitchButton setTitle:[VectorL10n createAccount] forState:UIControlStateHighlighted];
|
||
|
||
// Update supported authentication flow and associated information (defined in authentication session)
|
||
[self refreshAuthenticationSession];
|
||
|
||
self.screenTracker = [[AnalyticsScreenTracker alloc] initWithScreen:AnalyticsScreenLogin];
|
||
}
|
||
else if (authType == MXKAuthenticationTypeRegister)
|
||
{
|
||
_subTitleLabel.hidden = NO;
|
||
_subTitleLabel.text = [VectorL10n loginCreateAccount];
|
||
[_submitButton setTitle:[VectorL10n signUp] forState:UIControlStateNormal];
|
||
[_submitButton setTitle:[VectorL10n signUp] forState:UIControlStateHighlighted];
|
||
[_authSwitchButton setTitle:[VectorL10n back] forState:UIControlStateNormal];
|
||
[_authSwitchButton setTitle:[VectorL10n back] forState:UIControlStateHighlighted];
|
||
|
||
// Update supported authentication flow and associated information (defined in authentication session)
|
||
[self refreshAuthenticationSession];
|
||
|
||
self.screenTracker = [[AnalyticsScreenTracker alloc] initWithScreen:AnalyticsScreenRegister];
|
||
}
|
||
else if (authType == MXKAuthenticationTypeForgotPassword)
|
||
{
|
||
_subTitleLabel.hidden = YES;
|
||
|
||
if (isPasswordReseted)
|
||
{
|
||
[_submitButton setTitle:[VectorL10n back] forState:UIControlStateNormal];
|
||
[_submitButton setTitle:[VectorL10n back] forState:UIControlStateHighlighted];
|
||
}
|
||
else
|
||
{
|
||
[_submitButton setTitle:[VectorL10n submit] forState:UIControlStateNormal];
|
||
[_submitButton setTitle:[VectorL10n submit] forState:UIControlStateHighlighted];
|
||
|
||
[self refreshForgotPasswordSession];
|
||
}
|
||
|
||
[_authSwitchButton setTitle:[VectorL10n back] forState:UIControlStateNormal];
|
||
[_authSwitchButton setTitle:[VectorL10n back] forState:UIControlStateHighlighted];
|
||
|
||
self.screenTracker = [[AnalyticsScreenTracker alloc] initWithScreen:AnalyticsScreenForgotPassword];
|
||
}
|
||
|
||
if (self.isViewVisible)
|
||
{
|
||
[self.screenTracker trackScreen];
|
||
}
|
||
|
||
[self checkIdentityServer];
|
||
}
|
||
|
||
- (void)setAuthInputsView:(MXKAuthInputsView *)authInputsView
|
||
{
|
||
// Here a new view will be loaded, hide first subviews which depend on auth flow
|
||
_submitButton.hidden = YES;
|
||
_noFlowLabel.hidden = YES;
|
||
_retryButton.hidden = YES;
|
||
|
||
if (_authInputsView)
|
||
{
|
||
[_authInputsView removeObserver:self forKeyPath:@"viewHeightConstraint.constant"];
|
||
|
||
[NSLayoutConstraint deactivateConstraints:_authInputsView.constraints];
|
||
[_authInputsView removeFromSuperview];
|
||
_authInputsView.delegate = nil;
|
||
[_authInputsView destroy];
|
||
_authInputsView = nil;
|
||
}
|
||
|
||
_authInputsView = authInputsView;
|
||
|
||
CGFloat previousInputsContainerViewHeight = _authInputContainerViewHeightConstraint.constant;
|
||
|
||
if (_authInputsView)
|
||
{
|
||
_authInputsView.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[_authInputsContainerView addSubview:_authInputsView];
|
||
|
||
_authInputsView.delegate = self;
|
||
|
||
_submitButton.hidden = NO;
|
||
_authInputsView.hidden = NO;
|
||
|
||
_authInputContainerViewHeightConstraint.constant = _authInputsView.viewHeightConstraint.constant;
|
||
|
||
NSLayoutConstraint* topConstraint = [NSLayoutConstraint constraintWithItem:_authInputsContainerView
|
||
attribute:NSLayoutAttributeTop
|
||
relatedBy:NSLayoutRelationEqual
|
||
toItem:_authInputsView
|
||
attribute:NSLayoutAttributeTop
|
||
multiplier:1.0f
|
||
constant:0.0f];
|
||
|
||
|
||
NSLayoutConstraint* leadingConstraint = [NSLayoutConstraint constraintWithItem:_authInputsContainerView
|
||
attribute:NSLayoutAttributeLeading
|
||
relatedBy:NSLayoutRelationEqual
|
||
toItem:_authInputsView
|
||
attribute:NSLayoutAttributeLeading
|
||
multiplier:1.0f
|
||
constant:0.0f];
|
||
|
||
NSLayoutConstraint* trailingConstraint = [NSLayoutConstraint constraintWithItem:_authInputsContainerView
|
||
attribute:NSLayoutAttributeTrailing
|
||
relatedBy:NSLayoutRelationEqual
|
||
toItem:_authInputsView
|
||
attribute:NSLayoutAttributeTrailing
|
||
multiplier:1.0f
|
||
constant:0.0f];
|
||
|
||
|
||
[NSLayoutConstraint activateConstraints:@[topConstraint, leadingConstraint, trailingConstraint]];
|
||
|
||
[_authInputsView addObserver:self forKeyPath:@"viewHeightConstraint.constant" options:0 context:nil];
|
||
}
|
||
else
|
||
{
|
||
// No input fields are displayed
|
||
_authInputContainerViewHeightConstraint.constant = _authInputContainerViewMinHeightConstraint.constant;
|
||
}
|
||
|
||
[self.view layoutIfNeeded];
|
||
|
||
// Refresh content view height by considering the updated height of inputs container
|
||
_contentViewHeightConstraint.constant += (_authInputContainerViewHeightConstraint.constant - previousInputsContainerViewHeight);
|
||
}
|
||
|
||
- (void)setDefaultHomeServerUrl:(NSString *)defaultHomeServerUrl
|
||
{
|
||
_defaultHomeServerUrl = defaultHomeServerUrl;
|
||
|
||
if (!_homeServerTextField.text.length)
|
||
{
|
||
[self setHomeServerTextFieldText:defaultHomeServerUrl];
|
||
}
|
||
}
|
||
|
||
- (void)setDefaultIdentityServerUrl:(NSString *)defaultIdentityServerUrl
|
||
{
|
||
_defaultIdentityServerUrl = defaultIdentityServerUrl;
|
||
|
||
if (!_identityServerTextField.text.length)
|
||
{
|
||
[self setIdentityServerTextFieldText:defaultIdentityServerUrl];
|
||
}
|
||
}
|
||
|
||
- (void)setHomeServerTextFieldText:(NSString *)homeServerUrl;
|
||
{
|
||
NSString *serverUrl;
|
||
NSUInteger index = [ServerURLHelper.shared indexOf:homeServerUrl];
|
||
if (index != NSNotFound) {
|
||
ServerURLHelper.shared.selectedIndex = index;
|
||
serverUrl = homeServerUrl;
|
||
|
||
MXSDKOptions *option = MXSDKOptions.sharedInstance;
|
||
|
||
option.clientPermalinkBaseUrl = [AppConfigService.shared permalinkUrl];
|
||
} else {
|
||
serverUrl = @"";
|
||
}
|
||
|
||
_homeServerTextField.text = homeServerUrl;
|
||
|
||
if (!mxRestClient || ![mxRestClient.homeserver isEqualToString:homeServerUrl])
|
||
{
|
||
[self updateRESTClient];
|
||
|
||
if (_authType == MXKAuthenticationTypeLogin || _authType == MXKAuthenticationTypeRegister)
|
||
{
|
||
// Restore default UI
|
||
self.authType = _authType;
|
||
}
|
||
else
|
||
{
|
||
// Refresh the IS anyway
|
||
[self checkIdentityServer];
|
||
}
|
||
}
|
||
}
|
||
|
||
- (void)setIdentityServerTextFieldText:(NSString *)identityServerUrl
|
||
{
|
||
_identityServerTextField.text = identityServerUrl;
|
||
|
||
[self updateIdentityServerURL:identityServerUrl];
|
||
}
|
||
|
||
- (void)updateIdentityServerURL:(NSString*)url
|
||
{
|
||
if (![self.identityService.identityServer isEqualToString:url])
|
||
{
|
||
if (url.length)
|
||
{
|
||
self.identityService = [[MXIdentityService alloc] initWithIdentityServer:url accessToken:nil andHomeserverRestClient:mxRestClient];
|
||
}
|
||
else
|
||
{
|
||
self.identityService = nil;
|
||
}
|
||
}
|
||
|
||
[mxRestClient setIdentityServer:url.length ? url : nil];
|
||
}
|
||
|
||
- (void)setIdentityServerHidden:(BOOL)hidden
|
||
{
|
||
_identityServerContainer.hidden = hidden;
|
||
}
|
||
|
||
- (void)checkIdentityServer
|
||
{
|
||
[self cancelIdentityServerCheck];
|
||
|
||
// Hide the field while checking data
|
||
[self setIdentityServerHidden:YES];
|
||
|
||
NSString *homeserver = mxRestClient.homeserver;
|
||
|
||
// First, fetch the IS advertised by the HS
|
||
if (homeserver)
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] checkIdentityServer for homeserver %@", homeserver);
|
||
|
||
autoDiscovery = [[MXAutoDiscovery alloc] initWithUrl:homeserver];
|
||
|
||
MXWeakify(self);
|
||
checkIdentityServerOperation = [autoDiscovery findClientConfig:^(MXDiscoveredClientConfig * _Nonnull discoveredClientConfig) {
|
||
MXStrongifyAndReturnIfNil(self);
|
||
|
||
NSString *identityServer = discoveredClientConfig.wellKnown.identityServer.baseUrl;
|
||
MXLogDebug(@"[MXKAuthenticationVC] checkIdentityServer: Identity server: %@", identityServer);
|
||
|
||
if (identityServer)
|
||
{
|
||
// Apply the provided IS
|
||
[self setIdentityServerTextFieldText:identityServer];
|
||
}
|
||
|
||
// Then, check if the HS needs an IS for running
|
||
MXWeakify(self);
|
||
MXHTTPOperation *operation = [self checkIdentityServerRequirementWithCompletion:^(BOOL identityServerRequired) {
|
||
|
||
MXStrongifyAndReturnIfNil(self);
|
||
|
||
self->checkIdentityServerOperation = nil;
|
||
|
||
// Show the field only if an IS is required so that the user can customise it
|
||
[self setIdentityServerHidden:!identityServerRequired];
|
||
}];
|
||
|
||
if (operation)
|
||
{
|
||
[self->checkIdentityServerOperation mutateTo:operation];
|
||
}
|
||
else
|
||
{
|
||
self->checkIdentityServerOperation = nil;
|
||
}
|
||
|
||
self->autoDiscovery = nil;
|
||
|
||
} failure:^(NSError *error) {
|
||
MXStrongifyAndReturnIfNil(self);
|
||
|
||
// No need to report this error to the end user
|
||
// There will be already an error about failing to get the auth flow from the HS
|
||
MXLogDebug(@"[MXKAuthenticationVC] checkIdentityServer. Error: %@", error);
|
||
|
||
self->autoDiscovery = nil;
|
||
}];
|
||
}
|
||
}
|
||
|
||
- (void)cancelIdentityServerCheck
|
||
{
|
||
if (checkIdentityServerOperation)
|
||
{
|
||
[checkIdentityServerOperation cancel];
|
||
checkIdentityServerOperation = nil;
|
||
}
|
||
}
|
||
|
||
- (MXHTTPOperation*)checkIdentityServerRequirementWithCompletion:(void (^)(BOOL identityServerRequired))completion
|
||
{
|
||
MXHTTPOperation *operation;
|
||
|
||
if (_authType == MXKAuthenticationTypeLogin)
|
||
{
|
||
// The identity server is only required for registration and password reset
|
||
// It is then stored in the user account data
|
||
completion(NO);
|
||
}
|
||
else
|
||
{
|
||
operation = [mxRestClient supportedMatrixVersions:^(MXMatrixVersions *matrixVersions) {
|
||
|
||
MXLogDebug(@"[MXKAuthenticationVC] checkIdentityServerRequirement: %@", matrixVersions.doesServerRequireIdentityServerParam ? @"YES": @"NO");
|
||
completion(matrixVersions.doesServerRequireIdentityServerParam);
|
||
|
||
} failure:^(NSError *error) {
|
||
// No need to report this error to the end user
|
||
// There will be already an error about failing to get the auth flow from the HS
|
||
MXLogDebug(@"[MXKAuthenticationVC] checkIdentityServerRequirement. Error: %@", error);
|
||
}];
|
||
}
|
||
|
||
return operation;
|
||
}
|
||
|
||
- (void)setUserInteractionEnabled:(BOOL)userInteractionEnabled
|
||
{
|
||
_submitButton.enabled = (userInteractionEnabled && _authInputsView.areAllRequiredFieldsSet);
|
||
_authSwitchButton.enabled = userInteractionEnabled;
|
||
|
||
_homeServerTextField.enabled = userInteractionEnabled;
|
||
_identityServerTextField.enabled = userInteractionEnabled;
|
||
|
||
_userInteractionEnabled = userInteractionEnabled;
|
||
}
|
||
|
||
- (void)refreshAuthenticationSession
|
||
{
|
||
// Remove reachability observer
|
||
[[NSNotificationCenter defaultCenter] removeObserver:self name:AFNetworkingReachabilityDidChangeNotification object:nil];
|
||
|
||
// Cancel potential request in progress
|
||
[mxCurrentOperation cancel];
|
||
mxCurrentOperation = nil;
|
||
|
||
// Reset potential authentication fallback url
|
||
authenticationFallback = nil;
|
||
|
||
if (mxRestClient && (self.authType == MXKAuthenticationTypeLogin || self.authType == MXKAuthenticationTypeRegister))
|
||
{
|
||
MXWeakify(self);
|
||
|
||
// Get the login session to determine available SSO flows.
|
||
mxCurrentOperation = [mxRestClient getLoginSession:^(MXAuthenticationSession* loginAuthSession) {
|
||
MXStrongifyAndReturnIfNil(self);
|
||
|
||
if (self.authType == MXKAuthenticationTypeRegister)
|
||
{
|
||
MXWeakify(self);
|
||
self->mxCurrentOperation = [self->mxRestClient getRegisterSession:^(MXAuthenticationSession* registerAuthSession) {
|
||
MXStrongifyAndReturnIfNil(self);
|
||
|
||
// Handle the register session along with any SSO flows from the login session
|
||
MXLoginSSOFlow *loginSSOFlow = [self loginSSOFlowWithProvidersFromFlows:loginAuthSession.flows];
|
||
[self handleAuthenticationSession:registerAuthSession withFallbackSSOFlow:loginSSOFlow];
|
||
|
||
} failure:^(NSError *error) {
|
||
|
||
[self onFailureDuringMXOperation:error];
|
||
|
||
}];
|
||
}
|
||
else
|
||
{
|
||
// Handle the login session by itself
|
||
[self handleAuthenticationSession:loginAuthSession withFallbackSSOFlow:nil];
|
||
}
|
||
|
||
} failure:^(NSError *error) {
|
||
|
||
MXStrongifyAndReturnIfNil(self);
|
||
[self onFailureDuringMXOperation:error];
|
||
|
||
}];
|
||
}
|
||
else
|
||
{
|
||
// Not supported for other types
|
||
MXLogDebug(@"[MXKAuthenticationVC] refreshAuthenticationSession is ignored");
|
||
}
|
||
}
|
||
|
||
- (void)handleAuthenticationSession:(MXAuthenticationSession *)authSession withFallbackSSOFlow:(MXLoginSSOFlow *)fallbackSSOFlow
|
||
{
|
||
mxCurrentOperation = nil;
|
||
|
||
[_authenticationActivityIndicator stopAnimating];
|
||
|
||
// Check whether fallback is defined, and retrieve the right input view class.
|
||
Class authInputsViewClass;
|
||
if (_authType == MXKAuthenticationTypeLogin)
|
||
{
|
||
authenticationFallback = [mxRestClient loginFallback];
|
||
authInputsViewClass = loginAuthInputsViewClass;
|
||
|
||
}
|
||
else if (_authType == MXKAuthenticationTypeRegister)
|
||
{
|
||
authenticationFallback = [mxRestClient registerFallback];
|
||
authInputsViewClass = registerAuthInputsViewClass;
|
||
}
|
||
else
|
||
{
|
||
// Not supported for other types
|
||
MXLogDebug(@"[MXKAuthenticationVC] handleAuthenticationSession is ignored");
|
||
return;
|
||
}
|
||
|
||
MXKAuthInputsView *authInputsView = nil;
|
||
if (authInputsViewClass)
|
||
{
|
||
// Instantiate a new auth inputs view, except if the current one is already an instance of this class.
|
||
if (self.authInputsView && self.authInputsView.class == authInputsViewClass)
|
||
{
|
||
// Use the current view
|
||
authInputsView = self.authInputsView;
|
||
}
|
||
else
|
||
{
|
||
authInputsView = [authInputsViewClass authInputsView];
|
||
}
|
||
}
|
||
|
||
if (authInputsView)
|
||
{
|
||
// Apply authentication session on inputs view
|
||
if ([authInputsView setAuthSession:authSession withAuthType:_authType] == NO)
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Received authentication settings are not supported");
|
||
authInputsView = nil;
|
||
}
|
||
else if (!_softLogoutCredentials)
|
||
{
|
||
// If all listed flows in this authentication session are not supported we suggest using the fallback page.
|
||
if (authenticationFallback.length && authInputsView.authSession.flows.count == 0)
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] No supported flow, suggest using fallback page");
|
||
authInputsView = nil;
|
||
}
|
||
else if (authInputsView.authSession.flows.count != authSession.flows.count)
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] The authentication session contains at least one unsupported flow");
|
||
}
|
||
}
|
||
}
|
||
|
||
if (authInputsView)
|
||
{
|
||
// Check whether the current view must be replaced
|
||
if (self.authInputsView != authInputsView)
|
||
{
|
||
// Refresh layout
|
||
self.authInputsView = authInputsView;
|
||
}
|
||
|
||
// Refresh user interaction
|
||
self.userInteractionEnabled = _userInteractionEnabled;
|
||
|
||
// Check whether an external set of parameters have been defined to pursue a registration
|
||
if (self.externalRegistrationParameters)
|
||
{
|
||
if ([authInputsView setExternalRegistrationParameters:self.externalRegistrationParameters])
|
||
{
|
||
// Launch authentication now
|
||
[self onButtonPressed:_submitButton];
|
||
}
|
||
else
|
||
{
|
||
[self onFailureDuringAuthRequest:[NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[VectorL10n notSupportedYet]}]];
|
||
|
||
_externalRegistrationParameters = nil;
|
||
|
||
// Restore login screen on failure
|
||
self.authType = MXKAuthenticationTypeLogin;
|
||
}
|
||
}
|
||
|
||
if (_softLogoutCredentials)
|
||
{
|
||
[authInputsView setSoftLogoutCredentials:_softLogoutCredentials];
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Remove the potential auth inputs view
|
||
self.authInputsView = nil;
|
||
|
||
// Cancel external registration parameters if any
|
||
_externalRegistrationParameters = nil;
|
||
|
||
// Notify user that no flow is supported
|
||
if (_authType == MXKAuthenticationTypeLogin)
|
||
{
|
||
_noFlowLabel.text = [VectorL10n loginErrorDoNotSupportLoginFlows];
|
||
}
|
||
else
|
||
{
|
||
_noFlowLabel.text = [VectorL10n loginErrorRegistrationIsNotSupported];
|
||
}
|
||
MXLogDebug(@"[MXKAuthenticationVC] Warning: %@", _noFlowLabel.text);
|
||
|
||
if (authenticationFallback.length)
|
||
{
|
||
[_retryButton setTitle:[VectorL10n loginUseFallback] forState:UIControlStateNormal];
|
||
[_retryButton setTitle:[VectorL10n loginUseFallback] forState:UIControlStateNormal];
|
||
}
|
||
else
|
||
{
|
||
[_retryButton setTitle:[VectorL10n retry] forState:UIControlStateNormal];
|
||
[_retryButton setTitle:[VectorL10n retry] forState:UIControlStateNormal];
|
||
}
|
||
|
||
_noFlowLabel.hidden = NO;
|
||
_retryButton.hidden = NO;
|
||
}
|
||
}
|
||
|
||
- (void)setExternalRegistrationParameters:(NSDictionary*)parameters
|
||
{
|
||
if (parameters.count)
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] setExternalRegistrationParameters");
|
||
|
||
// Cancel the current operation if any.
|
||
[self cancel];
|
||
|
||
// Load the view controller’s view if it has not yet been loaded.
|
||
// This is required before updating view's textfields (homeserver url...)
|
||
[self loadViewIfNeeded];
|
||
|
||
// Force register mode
|
||
self.authType = MXKAuthenticationTypeRegister;
|
||
|
||
// Apply provided homeserver if any
|
||
id hs_url = parameters[@"hs_url"];
|
||
NSString *homeserverURL = nil;
|
||
if (hs_url && [hs_url isKindOfClass:NSString.class])
|
||
{
|
||
homeserverURL = hs_url;
|
||
}
|
||
[self setHomeServerTextFieldText:homeserverURL];
|
||
|
||
// Apply provided identity server if any
|
||
id is_url = parameters[@"is_url"];
|
||
NSString *identityURL = nil;
|
||
if (is_url && [is_url isKindOfClass:NSString.class])
|
||
{
|
||
identityURL = is_url;
|
||
}
|
||
[self setIdentityServerTextFieldText:identityURL];
|
||
|
||
// Disable user interaction
|
||
self.userInteractionEnabled = NO;
|
||
|
||
// Cancel potential request in progress
|
||
[mxCurrentOperation cancel];
|
||
mxCurrentOperation = nil;
|
||
|
||
// Remove the current auth inputs view
|
||
self.authInputsView = nil;
|
||
|
||
// Set external parameters and trigger a refresh (the parameters will be taken into account during [handleAuthenticationSession:])
|
||
_externalRegistrationParameters = parameters;
|
||
[self refreshAuthenticationSession];
|
||
}
|
||
else
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] reset externalRegistrationParameters");
|
||
_externalRegistrationParameters = nil;
|
||
|
||
// Restore default UI
|
||
self.authType = _authType;
|
||
}
|
||
}
|
||
|
||
- (void)setSoftLogoutCredentials:(MXCredentials *)softLogoutCredentials
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] setSoftLogoutCredentials");
|
||
|
||
// Cancel the current operation if any.
|
||
[self cancel];
|
||
|
||
// Load the view controller’s view if it has not yet been loaded.
|
||
// This is required before updating view's textfields (homeserver url...)
|
||
[self loadViewIfNeeded];
|
||
|
||
if (softLogoutCredentials)
|
||
{
|
||
// Force register mode
|
||
self.authType = MXKAuthenticationTypeLogin;
|
||
|
||
[self setHomeServerTextFieldText:softLogoutCredentials.homeServer];
|
||
[self setIdentityServerTextFieldText:softLogoutCredentials.identityServer];
|
||
|
||
// Cancel potential request in progress
|
||
[mxCurrentOperation cancel];
|
||
mxCurrentOperation = nil;
|
||
|
||
// Remove the current auth inputs view
|
||
self.authInputsView = nil;
|
||
}
|
||
|
||
// Set parameters and trigger a refresh (the parameters will be taken into account during [handleAuthenticationSession:])
|
||
_softLogoutCredentials = softLogoutCredentials;
|
||
[self refreshAuthenticationSession];
|
||
}
|
||
|
||
- (void)setOnUnrecognizedCertificateBlock:(MXHTTPClientOnUnrecognizedCertificate)onUnrecognizedCertificateBlock
|
||
{
|
||
onUnrecognizedCertificateCustomBlock = onUnrecognizedCertificateBlock;
|
||
}
|
||
|
||
- (void)isUserNameInUse:(void (^)(BOOL isUserNameInUse))callback
|
||
{
|
||
mxCurrentOperation = [mxRestClient isUserNameInUse:self.authInputsView.userId callback:^(BOOL isUserNameInUse) {
|
||
|
||
self->mxCurrentOperation = nil;
|
||
|
||
if (callback)
|
||
{
|
||
callback (isUserNameInUse);
|
||
}
|
||
|
||
}];
|
||
}
|
||
|
||
- (void)testUserRegistration:(void (^)(MXError *mxError))callback
|
||
{
|
||
mxCurrentOperation = [mxRestClient testUserRegistration:self.authInputsView.userId callback:callback];
|
||
}
|
||
|
||
- (MXLoginSSOFlow*)loginSSOFlowWithProvidersFromFlows:(NSArray<MXLoginFlow*>*)loginFlows
|
||
{
|
||
MXLoginSSOFlow *ssoFlowWithProviders;
|
||
|
||
for (MXLoginFlow *loginFlow in loginFlows)
|
||
{
|
||
if ([loginFlow isKindOfClass:MXLoginSSOFlow.class])
|
||
{
|
||
MXLoginSSOFlow *ssoFlow = (MXLoginSSOFlow *)loginFlow;
|
||
|
||
if (ssoFlow.identityProviders.count)
|
||
{
|
||
ssoFlowWithProviders = ssoFlow;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return ssoFlowWithProviders;
|
||
}
|
||
|
||
- (IBAction)onButtonPressed:(id)sender
|
||
{
|
||
[self dismissKeyboard];
|
||
|
||
if (sender == _submitButton)
|
||
{
|
||
// Disable user interaction to prevent multiple requests
|
||
self.userInteractionEnabled = NO;
|
||
|
||
// Check parameters validity
|
||
NSString *errorMsg = [self.authInputsView validateParameters];
|
||
if (errorMsg)
|
||
{
|
||
[self onFailureDuringAuthRequest:[NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:errorMsg}]];
|
||
}
|
||
else
|
||
{
|
||
[self.authInputsContainerView bringSubviewToFront: _authenticationActivityIndicator];
|
||
|
||
// Launch the authentication according to its type
|
||
if (_authType == MXKAuthenticationTypeLogin)
|
||
{
|
||
// Prepare the parameters dict
|
||
[self.authInputsView prepareParameters:^(NSDictionary *parameters, NSError *error) {
|
||
|
||
if (parameters && self->mxRestClient)
|
||
{
|
||
[self->_authenticationActivityIndicator startAnimating];
|
||
[self loginWithParameters:parameters];
|
||
}
|
||
else
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Failed to prepare parameters");
|
||
[self onFailureDuringAuthRequest:error];
|
||
}
|
||
|
||
}];
|
||
}
|
||
else if (_authType == MXKAuthenticationTypeRegister)
|
||
{
|
||
// Check here the availability of the userId
|
||
if (self.authInputsView.userId.length)
|
||
{
|
||
[_authenticationActivityIndicator startAnimating];
|
||
|
||
if (self.authInputsView.password.length)
|
||
{
|
||
// Trigger here a register request in order to associate the filled userId and password to the current session id
|
||
// This will check the availability of the userId at the same time
|
||
NSDictionary *parameters = @{@"auth": @{
|
||
@"session": self.authInputsView.authSession.session,
|
||
@"type": kMXLoginFlowTypeDummy
|
||
},
|
||
@"username": self.authInputsView.userId,
|
||
@"password": self.authInputsView.password,
|
||
@"bind_email": @(NO),
|
||
@"initial_device_display_name":self.deviceDisplayName
|
||
};
|
||
|
||
mxCurrentOperation = [mxRestClient registerWithParameters:parameters success:^(NSDictionary *JSONResponse) {
|
||
|
||
// Unexpected case where the registration succeeds without any other stages
|
||
MXLoginResponse *loginResponse;
|
||
MXJSONModelSetMXJSONModel(loginResponse, MXLoginResponse, JSONResponse);
|
||
|
||
MXCredentials *credentials = [[MXCredentials alloc] initWithLoginResponse:loginResponse
|
||
andDefaultCredentials:self->mxRestClient.credentials];
|
||
|
||
// Sanity check
|
||
if (!credentials.userId || !credentials.accessToken)
|
||
{
|
||
[self onFailureDuringAuthRequest:[NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[VectorL10n notSupportedYet]}]];
|
||
}
|
||
else
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Registration succeeded");
|
||
|
||
// Report the certificate trusted by user (if any)
|
||
credentials.allowedCertificate = self->mxRestClient.allowedCertificate;
|
||
|
||
[self onSuccessfulLogin:credentials];
|
||
}
|
||
|
||
} failure:^(NSError *error) {
|
||
|
||
self->mxCurrentOperation = nil;
|
||
|
||
// An updated authentication session should be available in response data in case of unauthorized request.
|
||
NSDictionary *JSONResponse = nil;
|
||
if (error.userInfo[MXHTTPClientErrorResponseDataKey])
|
||
{
|
||
JSONResponse = error.userInfo[MXHTTPClientErrorResponseDataKey];
|
||
}
|
||
|
||
if (JSONResponse)
|
||
{
|
||
MXAuthenticationSession *authSession = [MXAuthenticationSession modelFromJSON:JSONResponse];
|
||
|
||
[self->_authenticationActivityIndicator stopAnimating];
|
||
|
||
// Update session identifier
|
||
self.authInputsView.authSession.session = authSession.session;
|
||
|
||
// Launch registration by preparing parameters dict
|
||
[self.authInputsView prepareParameters:^(NSDictionary *parameters, NSError *error) {
|
||
|
||
if (parameters && self->mxRestClient)
|
||
{
|
||
[self->_authenticationActivityIndicator startAnimating];
|
||
[self registerWithParameters:parameters];
|
||
}
|
||
else
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Failed to prepare parameters");
|
||
[self onFailureDuringAuthRequest:error];
|
||
}
|
||
|
||
}];
|
||
}
|
||
else
|
||
{
|
||
[self onFailureDuringAuthRequest:error];
|
||
}
|
||
}];
|
||
}
|
||
else
|
||
{
|
||
[self isUserNameInUse:^(BOOL isUserNameInUse) {
|
||
|
||
if (isUserNameInUse)
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] User name is already use");
|
||
[self onFailureDuringAuthRequest:[NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[VectorL10n authUsernameInUse]}]];
|
||
}
|
||
else
|
||
{
|
||
[self->_authenticationActivityIndicator stopAnimating];
|
||
|
||
// Launch registration by preparing parameters dict
|
||
[self.authInputsView prepareParameters:^(NSDictionary *parameters, NSError *error) {
|
||
|
||
if (parameters && self->mxRestClient)
|
||
{
|
||
[self->_authenticationActivityIndicator startAnimating];
|
||
[self registerWithParameters:parameters];
|
||
}
|
||
else
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Failed to prepare parameters");
|
||
[self onFailureDuringAuthRequest:error];
|
||
}
|
||
|
||
}];
|
||
}
|
||
|
||
}];
|
||
}
|
||
}
|
||
else if (self.externalRegistrationParameters)
|
||
{
|
||
// Launch registration by preparing parameters dict
|
||
[self.authInputsView prepareParameters:^(NSDictionary *parameters, NSError *error) {
|
||
|
||
if (parameters && self->mxRestClient)
|
||
{
|
||
[self->_authenticationActivityIndicator startAnimating];
|
||
[self registerWithParameters:parameters];
|
||
}
|
||
else
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Failed to prepare parameters");
|
||
[self onFailureDuringAuthRequest:error];
|
||
}
|
||
|
||
}];
|
||
}
|
||
else
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] User name is missing");
|
||
[self onFailureDuringAuthRequest:[NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[VectorL10n authInvalidUserName]}]];
|
||
}
|
||
}
|
||
else if (_authType == MXKAuthenticationTypeForgotPassword)
|
||
{
|
||
// Check whether the password has been reseted
|
||
if (isPasswordReseted)
|
||
{
|
||
// Return to login screen
|
||
self.authType = MXKAuthenticationTypeLogin;
|
||
}
|
||
else
|
||
{
|
||
// Prepare the parameters dict
|
||
[self.authInputsView prepareParameters:^(NSDictionary *parameters, NSError *error) {
|
||
|
||
if (parameters && self->mxRestClient)
|
||
{
|
||
[self->_authenticationActivityIndicator startAnimating];
|
||
[self resetPasswordWithParameters:parameters];
|
||
}
|
||
else
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Failed to prepare parameters");
|
||
[self onFailureDuringAuthRequest:error];
|
||
}
|
||
|
||
}];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (sender == _authSwitchButton)
|
||
{
|
||
if (_authType == MXKAuthenticationTypeLogin)
|
||
{
|
||
self.authType = MXKAuthenticationTypeRegister;
|
||
}
|
||
else
|
||
{
|
||
self.authType = MXKAuthenticationTypeLogin;
|
||
}
|
||
}
|
||
else if (sender == _retryButton)
|
||
{
|
||
if (authenticationFallback)
|
||
{
|
||
[self showAuthenticationFallBackView:authenticationFallback];
|
||
}
|
||
else
|
||
{
|
||
[self refreshAuthenticationSession];
|
||
}
|
||
}
|
||
else if (sender == _cancelAuthFallbackButton)
|
||
{
|
||
// Hide fallback webview
|
||
[self hideRegistrationFallbackView];
|
||
}
|
||
}
|
||
|
||
- (void)cancel
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] cancel");
|
||
|
||
// Cancel external registration parameters if any
|
||
_externalRegistrationParameters = nil;
|
||
|
||
if (registrationTimer)
|
||
{
|
||
[registrationTimer invalidate];
|
||
registrationTimer = nil;
|
||
}
|
||
|
||
// Cancel request in progress
|
||
if (mxCurrentOperation)
|
||
{
|
||
[mxCurrentOperation cancel];
|
||
mxCurrentOperation = nil;
|
||
}
|
||
|
||
[_authenticationActivityIndicator stopAnimating];
|
||
self.userInteractionEnabled = YES;
|
||
|
||
// Reset potential completed stages
|
||
self.authInputsView.authSession.completed = nil;
|
||
|
||
// Update authentication inputs view to return in initial step
|
||
[self.authInputsView setAuthSession:self.authInputsView.authSession withAuthType:_authType];
|
||
}
|
||
|
||
- (void)onFailureDuringAuthRequest:(NSError *)error
|
||
{
|
||
mxCurrentOperation = nil;
|
||
[_authenticationActivityIndicator stopAnimating];
|
||
self.userInteractionEnabled = YES;
|
||
|
||
// Ignore connection cancellation error
|
||
if (([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled))
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Auth request cancelled");
|
||
return;
|
||
}
|
||
|
||
MXLogDebug(@"[MXKAuthenticationVC] Auth request failed: %@", error);
|
||
|
||
// Cancel external registration parameters if any
|
||
_externalRegistrationParameters = nil;
|
||
|
||
// Translate the error code to a human message
|
||
NSString *title = error.localizedFailureReason;
|
||
if (!title)
|
||
{
|
||
if (self.authType == MXKAuthenticationTypeLogin)
|
||
{
|
||
title = [VectorL10n loginErrorTitle];
|
||
}
|
||
else if (self.authType == MXKAuthenticationTypeRegister)
|
||
{
|
||
title = [VectorL10n registerErrorTitle];
|
||
}
|
||
else
|
||
{
|
||
title = [VectorL10n error];
|
||
}
|
||
}
|
||
NSString* message = error.localizedDescription;
|
||
NSDictionary* dict = error.userInfo;
|
||
|
||
// detect if it is a Matrix SDK issue
|
||
if (dict)
|
||
{
|
||
NSString* localizedError = [dict valueForKey:@"error"];
|
||
NSString* errCode = [dict valueForKey:@"errcode"];
|
||
|
||
if (localizedError.length > 0)
|
||
{
|
||
message = localizedError;
|
||
}
|
||
|
||
if (errCode)
|
||
{
|
||
if ([errCode isEqualToString:kMXErrCodeStringForbidden])
|
||
{
|
||
message = [VectorL10n loginErrorForbidden];
|
||
}
|
||
else if ([errCode isEqualToString:kMXErrCodeStringUnknownToken])
|
||
{
|
||
message = [VectorL10n loginErrorUnknownToken];
|
||
}
|
||
else if ([errCode isEqualToString:kMXErrCodeStringBadJSON])
|
||
{
|
||
message = [VectorL10n loginErrorBadJson];
|
||
}
|
||
else if ([errCode isEqualToString:kMXErrCodeStringNotJSON])
|
||
{
|
||
message = [VectorL10n loginErrorNotJson];
|
||
}
|
||
else if ([errCode isEqualToString:kMXErrCodeStringLimitExceeded])
|
||
{
|
||
message = [VectorL10n loginErrorLimitExceeded];
|
||
}
|
||
else if ([errCode isEqualToString:kMXErrCodeStringUserInUse])
|
||
{
|
||
message = [VectorL10n loginErrorUserInUse];
|
||
}
|
||
else if ([errCode isEqualToString:kMXErrCodeStringLoginEmailURLNotYet])
|
||
{
|
||
message = [VectorL10n loginErrorLoginEmailNotYet];
|
||
}
|
||
else if ([errCode isEqualToString:kMXErrCodeStringResourceLimitExceeded])
|
||
{
|
||
[self showResourceLimitExceededError:dict onAdminContactTapped:nil];
|
||
return;
|
||
}
|
||
else if (!message.length)
|
||
{
|
||
message = errCode;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Alert user
|
||
if (alert)
|
||
{
|
||
[alert dismissViewControllerAnimated:NO completion:nil];
|
||
}
|
||
|
||
alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
|
||
|
||
[alert addAction:[UIAlertAction actionWithTitle:[VectorL10n ok]
|
||
style:UIAlertActionStyleDefault
|
||
handler:^(UIAlertAction * action) {
|
||
|
||
self->alert = nil;
|
||
|
||
}]];
|
||
|
||
|
||
[self presentViewController:alert animated:YES completion:nil];
|
||
|
||
// Update authentication inputs view to return in initial step
|
||
[self.authInputsView setAuthSession:self.authInputsView.authSession withAuthType:_authType];
|
||
if (self.softLogoutCredentials)
|
||
{
|
||
self.authInputsView.softLogoutCredentials = self.softLogoutCredentials;
|
||
}
|
||
}
|
||
|
||
- (void)showResourceLimitExceededError:(NSDictionary *)errorDict onAdminContactTapped:(void (^)(NSURL *adminContact))onAdminContactTapped
|
||
{
|
||
mxCurrentOperation = nil;
|
||
[_authenticationActivityIndicator stopAnimating];
|
||
self.userInteractionEnabled = YES;
|
||
|
||
// Alert user
|
||
if (alert)
|
||
{
|
||
[alert dismissViewControllerAnimated:NO completion:nil];
|
||
}
|
||
|
||
// Parse error data
|
||
NSString *limitType, *adminContactString;
|
||
NSURL *adminContact;
|
||
|
||
MXJSONModelSetString(limitType, errorDict[kMXErrorResourceLimitExceededLimitTypeKey]);
|
||
MXJSONModelSetString(adminContactString, errorDict[kMXErrorResourceLimitExceededAdminContactKey]);
|
||
|
||
if (adminContactString)
|
||
{
|
||
adminContact = [NSURL URLWithString:adminContactString];
|
||
}
|
||
|
||
NSString *title = [VectorL10n loginErrorResourceLimitExceededTitle];
|
||
|
||
// Build the message content
|
||
NSMutableString *message = [NSMutableString new];
|
||
if ([limitType isEqualToString:kMXErrorResourceLimitExceededLimitTypeMonthlyActiveUserValue])
|
||
{
|
||
[message appendString:[VectorL10n loginErrorResourceLimitExceededMessageMonthlyActiveUser]];
|
||
}
|
||
else
|
||
{
|
||
[message appendString:[VectorL10n loginErrorResourceLimitExceededMessageDefault]];
|
||
}
|
||
|
||
[message appendString:[VectorL10n loginErrorResourceLimitExceededMessageContact]];
|
||
|
||
// Build the alert
|
||
alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
|
||
|
||
MXWeakify(self);
|
||
if (adminContact && onAdminContactTapped)
|
||
{
|
||
[alert addAction:[UIAlertAction actionWithTitle:[VectorL10n loginErrorResourceLimitExceededContactButton]
|
||
style:UIAlertActionStyleDefault
|
||
handler:^(UIAlertAction * action)
|
||
{
|
||
MXStrongifyAndReturnIfNil(self);
|
||
self->alert = nil;
|
||
|
||
// Let the system handle the URI
|
||
// It could be something like "mailto: server.admin@example.com"
|
||
onAdminContactTapped(adminContact);
|
||
}]];
|
||
}
|
||
|
||
[alert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
|
||
style:UIAlertActionStyleDefault
|
||
handler:^(UIAlertAction * action)
|
||
{
|
||
MXStrongifyAndReturnIfNil(self);
|
||
self->alert = nil;
|
||
}]];
|
||
|
||
[self presentViewController:alert animated:YES completion:nil];
|
||
|
||
// Update authentication inputs view to return in initial step
|
||
[self.authInputsView setAuthSession:self.authInputsView.authSession withAuthType:_authType];
|
||
}
|
||
|
||
- (void)onSuccessfulLogin:(MXCredentials*)credentials
|
||
{
|
||
mxCurrentOperation = nil;
|
||
[_authenticationActivityIndicator stopAnimating];
|
||
self.userInteractionEnabled = YES;
|
||
|
||
if (self.softLogoutCredentials)
|
||
{
|
||
// Hydrate the account with the new access token
|
||
MXKAccount *account = [[MXKAccountManager sharedManager] accountForUserId:self.softLogoutCredentials.userId];
|
||
[[MXKAccountManager sharedManager] hydrateAccount:account withCredentials:credentials];
|
||
|
||
if (_delegate)
|
||
{
|
||
[_delegate authenticationViewController:self didLogWithUserId:credentials.userId];
|
||
}
|
||
}
|
||
// Sanity check: check whether the user is not already logged in with this id
|
||
else if ([[MXKAccountManager sharedManager] accountForUserId:credentials.userId])
|
||
{
|
||
//Alert user
|
||
__weak typeof(self) weakSelf = self;
|
||
|
||
if (alert)
|
||
{
|
||
[alert dismissViewControllerAnimated:NO completion:nil];
|
||
}
|
||
|
||
alert = [UIAlertController alertControllerWithTitle:[VectorL10n loginErrorAlreadyLoggedIn] message:nil preferredStyle:UIAlertControllerStyleAlert];
|
||
|
||
[alert addAction:[UIAlertAction actionWithTitle:[VectorL10n ok]
|
||
style:UIAlertActionStyleDefault
|
||
handler:^(UIAlertAction * action) {
|
||
|
||
// We remove the authentication view controller.
|
||
typeof(self) self = weakSelf;
|
||
self->alert = nil;
|
||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||
|
||
}]];
|
||
|
||
|
||
[self presentViewController:alert animated:YES completion:nil];
|
||
}
|
||
else
|
||
{
|
||
// Report the new account in account manager
|
||
if (!credentials.identityServer)
|
||
{
|
||
credentials.identityServer = _identityServerTextField.text;
|
||
}
|
||
|
||
[self createAccountWithCredentials:credentials];
|
||
}
|
||
}
|
||
|
||
- (MXHTTPOperation *)currentHttpOperation
|
||
{
|
||
return mxCurrentOperation;
|
||
}
|
||
|
||
#pragma mark - Privates
|
||
|
||
// Hook point for triggering device rehydration in subclasses
|
||
// Avoid cycles by using a separate private method do to the actual work
|
||
- (void)createAccountWithCredentials:(MXCredentials *)credentials
|
||
{
|
||
[self _createAccountWithCredentials:credentials];
|
||
}
|
||
|
||
- (void)attemptDeviceRehydrationWithKeyData:(NSData *)keyData
|
||
credentials:(MXCredentials *)credentials
|
||
{
|
||
[self attemptDeviceRehydrationWithKeyData:keyData
|
||
credentials:credentials
|
||
retry:YES];
|
||
}
|
||
|
||
- (void)attemptDeviceRehydrationWithKeyData:(NSData *)keyData
|
||
credentials:(MXCredentials *)credentials
|
||
retry:(BOOL)retry
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationViewController] attemptDeviceRehydration: starting device rehydration");
|
||
|
||
if (keyData == nil)
|
||
{
|
||
MXLogError(@"[MXKAuthenticationViewController] attemptDeviceRehydration: no key provided for device rehydration");
|
||
[self _createAccountWithCredentials:credentials];
|
||
return;
|
||
}
|
||
|
||
MXRestClient *mxRestClient = [[MXRestClient alloc] initWithCredentials:credentials andOnUnrecognizedCertificateBlock:^BOOL(NSData *certificate) {
|
||
return NO;
|
||
} andPersistentTokenDataHandler:^(void (^handler)(NSArray<MXCredentials *> *credentials, void (^completion)(BOOL didUpdateCredentials))) {
|
||
[[MXKAccountManager sharedManager] readAndWriteCredentials:handler];
|
||
} andUnauthenticatedHandler: nil];
|
||
|
||
MXWeakify(self);
|
||
[[MXKAccountManager sharedManager].dehydrationService rehydrateDeviceWithMatrixRestClient:mxRestClient dehydrationKey:keyData success:^(NSString * deviceId) {
|
||
MXStrongifyAndReturnIfNil(self);
|
||
|
||
if (deviceId)
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationViewController] attemptDeviceRehydration: device %@ rehydrated successfully.", deviceId);
|
||
credentials.deviceId = deviceId;
|
||
}
|
||
else
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationViewController] attemptDeviceRehydration: device rehydration has been canceled.");
|
||
}
|
||
|
||
[self _createAccountWithCredentials:credentials];
|
||
} failure:^(NSError *error) {
|
||
MXStrongifyAndReturnIfNil(self);
|
||
|
||
if (retry)
|
||
{
|
||
MXLogError(@"[MXKAuthenticationViewController] attemptDeviceRehydration: device rehydration failed due to error: %@. Retrying", error);
|
||
[self attemptDeviceRehydrationWithKeyData:keyData credentials:credentials retry:NO];
|
||
return;
|
||
}
|
||
|
||
MXLogError(@"[MXKAuthenticationViewController] attemptDeviceRehydration: device rehydration failed due to error: %@", error);
|
||
|
||
[self _createAccountWithCredentials:credentials];
|
||
}];
|
||
}
|
||
|
||
- (void)_createAccountWithCredentials:(MXCredentials *)credentials
|
||
{
|
||
MXKAccount *account = [[MXKAccount alloc] initWithCredentials:credentials];
|
||
account.identityServerURL = credentials.identityServer;
|
||
|
||
[[MXKAccountManager sharedManager] addAccount:account andOpenSession:YES];
|
||
|
||
if (_delegate)
|
||
{
|
||
[_delegate authenticationViewController:self didLogWithUserId:credentials.userId];
|
||
}
|
||
}
|
||
|
||
- (NSString *)deviceDisplayName
|
||
{
|
||
if (_deviceDisplayName)
|
||
{
|
||
return _deviceDisplayName;
|
||
}
|
||
|
||
#if TARGET_OS_IPHONE
|
||
NSString *deviceName = [[UIDevice currentDevice].model isEqualToString:@"iPad"] ? [VectorL10n loginTabletDevice] : [VectorL10n loginMobileDevice];
|
||
#elif TARGET_OS_OSX
|
||
NSString *deviceName = [VectorL10n loginDesktopDevice];
|
||
#endif
|
||
|
||
return deviceName;
|
||
}
|
||
|
||
- (void)refreshForgotPasswordSession
|
||
{
|
||
[_authenticationActivityIndicator stopAnimating];
|
||
|
||
MXKAuthInputsView *authInputsView = nil;
|
||
if (forgotPasswordAuthInputsViewClass)
|
||
{
|
||
// Instantiate a new auth inputs view, except if the current one is already an instance of this class.
|
||
if (self.authInputsView && self.authInputsView.class == forgotPasswordAuthInputsViewClass)
|
||
{
|
||
// Use the current view
|
||
authInputsView = self.authInputsView;
|
||
}
|
||
else
|
||
{
|
||
authInputsView = [forgotPasswordAuthInputsViewClass authInputsView];
|
||
}
|
||
}
|
||
|
||
if (authInputsView)
|
||
{
|
||
// Update authentication inputs view to return in initial step
|
||
[authInputsView setAuthSession:nil withAuthType:MXKAuthenticationTypeForgotPassword];
|
||
|
||
// Check whether the current view must be replaced
|
||
if (self.authInputsView != authInputsView)
|
||
{
|
||
// Refresh layout
|
||
self.authInputsView = authInputsView;
|
||
}
|
||
|
||
// Refresh user interaction
|
||
self.userInteractionEnabled = _userInteractionEnabled;
|
||
}
|
||
else
|
||
{
|
||
// Remove the potential auth inputs view
|
||
self.authInputsView = nil;
|
||
|
||
_noFlowLabel.text = [VectorL10n loginErrorForgotPasswordIsNotSupported];
|
||
|
||
MXLogDebug(@"[MXKAuthenticationVC] Warning: %@", _noFlowLabel.text);
|
||
|
||
_noFlowLabel.hidden = NO;
|
||
}
|
||
}
|
||
|
||
- (void)updateRESTClient
|
||
{
|
||
NSString *homeserverURL = [HomeserverAddress sanitized:_homeServerTextField.text];
|
||
|
||
if (homeserverURL.length)
|
||
{
|
||
// Check change
|
||
if ([homeserverURL isEqualToString:mxRestClient.homeserver] == NO)
|
||
{
|
||
mxRestClient = [[MXRestClient alloc] initWithHomeServer:homeserverURL andOnUnrecognizedCertificateBlock:^BOOL(NSData *certificate) {
|
||
|
||
// Check first if the app developer provided its own certificate handler.
|
||
if (self->onUnrecognizedCertificateCustomBlock)
|
||
{
|
||
return self->onUnrecognizedCertificateCustomBlock (certificate);
|
||
}
|
||
|
||
// Else prompt the user by displaying a fingerprint (SHA256) of the certificate.
|
||
__block BOOL isTrusted;
|
||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||
|
||
NSString *title = [VectorL10n sslCouldNotVerify];
|
||
NSString *homeserverURLStr = [VectorL10n sslHomeserverUrl:homeserverURL];
|
||
NSString *fingerprint = [VectorL10n sslFingerprintHash:@"SHA256"];
|
||
NSString *certFingerprint = [certificate mx_SHA256AsHexString];
|
||
|
||
NSString *msg = [NSString stringWithFormat:@"%@\n\n%@\n\n%@\n\n%@\n\n%@\n\n%@", [VectorL10n sslCertNotTrust], [VectorL10n sslCertNewAccountExpl], homeserverURLStr, fingerprint, certFingerprint, [VectorL10n sslOnlyAccept]];
|
||
|
||
if (self->alert)
|
||
{
|
||
[self->alert dismissViewControllerAnimated:NO completion:nil];
|
||
}
|
||
|
||
self->alert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
|
||
|
||
[self->alert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
|
||
style:UIAlertActionStyleDefault
|
||
handler:^(UIAlertAction * action) {
|
||
|
||
self->alert = nil;
|
||
isTrusted = NO;
|
||
dispatch_semaphore_signal(semaphore);
|
||
|
||
}]];
|
||
|
||
[self->alert addAction:[UIAlertAction actionWithTitle:[VectorL10n sslTrust]
|
||
style:UIAlertActionStyleDefault
|
||
handler:^(UIAlertAction * action) {
|
||
|
||
self->alert = nil;
|
||
isTrusted = YES;
|
||
dispatch_semaphore_signal(semaphore);
|
||
|
||
}]];
|
||
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
[self presentViewController:self->alert animated:YES completion:nil];
|
||
});
|
||
|
||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||
|
||
if (!isTrusted)
|
||
{
|
||
// Cancel request in progress
|
||
[self->mxCurrentOperation cancel];
|
||
self->mxCurrentOperation = nil;
|
||
[[NSNotificationCenter defaultCenter] removeObserver:self name:AFNetworkingReachabilityDidChangeNotification object:nil];
|
||
|
||
[self->_authenticationActivityIndicator stopAnimating];
|
||
}
|
||
|
||
return isTrusted;
|
||
}];
|
||
|
||
if (_identityServerTextField.text.length)
|
||
{
|
||
[self updateIdentityServerURL:self.identityServerTextField.text];
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
[mxRestClient close];
|
||
mxRestClient = nil;
|
||
}
|
||
}
|
||
|
||
- (void)loginWithParameters:(NSDictionary*)parameters
|
||
{
|
||
// Add the device name
|
||
NSMutableDictionary *theParameters = [NSMutableDictionary dictionaryWithDictionary:parameters];
|
||
theParameters[@"initial_device_display_name"] = self.deviceDisplayName;
|
||
|
||
mxCurrentOperation = [mxRestClient login:theParameters success:^(NSDictionary *JSONResponse) {
|
||
|
||
MXLoginResponse *loginResponse;
|
||
MXJSONModelSetMXJSONModel(loginResponse, MXLoginResponse, JSONResponse);
|
||
|
||
MXCredentials *credentials = [[MXCredentials alloc] initWithLoginResponse:loginResponse
|
||
andDefaultCredentials:self->mxRestClient.credentials];
|
||
|
||
// Sanity check
|
||
if (!credentials.userId || !credentials.accessToken)
|
||
{
|
||
[self onFailureDuringAuthRequest:[NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[VectorL10n notSupportedYet]}]];
|
||
}
|
||
else
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Login process succeeded");
|
||
|
||
// Report the certificate trusted by user (if any)
|
||
credentials.allowedCertificate = self->mxRestClient.allowedCertificate;
|
||
|
||
[self onSuccessfulLogin:credentials];
|
||
}
|
||
|
||
} failure:^(NSError *error) {
|
||
|
||
[self onFailureDuringAuthRequest:error];
|
||
|
||
}];
|
||
}
|
||
|
||
- (void)registerWithParameters:(NSDictionary*)parameters
|
||
{
|
||
if (registrationTimer)
|
||
{
|
||
[registrationTimer invalidate];
|
||
registrationTimer = nil;
|
||
}
|
||
|
||
// Add the device name
|
||
NSMutableDictionary *theParameters = [NSMutableDictionary dictionaryWithDictionary:parameters];
|
||
theParameters[@"initial_device_display_name"] = self.deviceDisplayName;
|
||
|
||
mxCurrentOperation = [mxRestClient registerWithParameters:theParameters success:^(NSDictionary *JSONResponse) {
|
||
|
||
MXLoginResponse *loginResponse;
|
||
MXJSONModelSetMXJSONModel(loginResponse, MXLoginResponse, JSONResponse);
|
||
|
||
MXCredentials *credentials = [[MXCredentials alloc] initWithLoginResponse:loginResponse
|
||
andDefaultCredentials:self->mxRestClient.credentials];
|
||
|
||
// Sanity check
|
||
if (!credentials.userId || !credentials.accessToken)
|
||
{
|
||
[self onFailureDuringAuthRequest:[NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[VectorL10n notSupportedYet]}]];
|
||
}
|
||
else
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Registration succeeded");
|
||
|
||
// Report the certificate trusted by user (if any)
|
||
credentials.allowedCertificate = self->mxRestClient.allowedCertificate;
|
||
|
||
[self onSuccessfulLogin:credentials];
|
||
}
|
||
|
||
} failure:^(NSError *error) {
|
||
|
||
self->mxCurrentOperation = nil;
|
||
|
||
// Check whether the authentication is pending (for example waiting for email validation)
|
||
MXError *mxError = [[MXError alloc] initWithNSError:error];
|
||
if (mxError && [mxError.errcode isEqualToString:kMXErrCodeStringUnauthorized])
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Wait for email validation");
|
||
|
||
// Postpone a new attempt in 10 sec
|
||
self->registrationTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(registrationTimerFireMethod:) userInfo:parameters repeats:NO];
|
||
}
|
||
else
|
||
{
|
||
// The completed stages should be available in response data in case of unauthorized request.
|
||
NSDictionary *JSONResponse = nil;
|
||
if (error.userInfo[MXHTTPClientErrorResponseDataKey])
|
||
{
|
||
JSONResponse = error.userInfo[MXHTTPClientErrorResponseDataKey];
|
||
}
|
||
|
||
if (JSONResponse)
|
||
{
|
||
MXAuthenticationSession *authSession = [MXAuthenticationSession modelFromJSON:JSONResponse];
|
||
|
||
if (authSession.completed)
|
||
{
|
||
[self->_authenticationActivityIndicator stopAnimating];
|
||
|
||
// Update session identifier in case of change
|
||
self.authInputsView.authSession.session = authSession.session;
|
||
|
||
[self.authInputsView updateAuthSessionWithCompletedStages:authSession.completed didUpdateParameters:^(NSDictionary *parameters, NSError *error) {
|
||
|
||
if (parameters)
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Pursue registration");
|
||
|
||
[self->_authenticationActivityIndicator startAnimating];
|
||
[self registerWithParameters:parameters];
|
||
}
|
||
else
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Failed to update parameters");
|
||
|
||
[self onFailureDuringAuthRequest:error];
|
||
}
|
||
|
||
}];
|
||
|
||
return;
|
||
}
|
||
|
||
[self onFailureDuringAuthRequest:[NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[VectorL10n notSupportedYet]}]];
|
||
}
|
||
else
|
||
{
|
||
[self onFailureDuringAuthRequest:error];
|
||
}
|
||
}
|
||
}];
|
||
}
|
||
|
||
- (void)registrationTimerFireMethod:(NSTimer *)timer
|
||
{
|
||
if (timer == registrationTimer && timer.isValid)
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Retry registration");
|
||
[self registerWithParameters:registrationTimer.userInfo];
|
||
}
|
||
}
|
||
|
||
- (void)resetPasswordWithParameters:(NSDictionary*)parameters
|
||
{
|
||
mxCurrentOperation = [mxRestClient resetPasswordWithParameters:parameters success:^() {
|
||
|
||
MXLogDebug(@"[MXKAuthenticationVC] Reset password succeeded");
|
||
|
||
self->mxCurrentOperation = nil;
|
||
[self->_authenticationActivityIndicator stopAnimating];
|
||
|
||
self->isPasswordReseted = YES;
|
||
|
||
// Force UI update to refresh submit button title.
|
||
self.authType = self->_authType;
|
||
|
||
// Refresh the authentication inputs view on success.
|
||
[self.authInputsView nextStep];
|
||
|
||
} failure:^(NSError *error) {
|
||
|
||
MXError *mxError = [[MXError alloc] initWithNSError:error];
|
||
if (mxError && [mxError.errcode isEqualToString:kMXErrCodeStringUnauthorized])
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Forgot Password: wait for email validation");
|
||
|
||
self->mxCurrentOperation = nil;
|
||
[self->_authenticationActivityIndicator stopAnimating];
|
||
|
||
if (self->alert)
|
||
{
|
||
[self->alert dismissViewControllerAnimated:NO completion:nil];
|
||
}
|
||
|
||
self->alert = [UIAlertController alertControllerWithTitle:[VectorL10n error] message:[VectorL10n authResetPasswordErrorUnauthorized] preferredStyle:UIAlertControllerStyleAlert];
|
||
|
||
[self->alert addAction:[UIAlertAction actionWithTitle:[VectorL10n ok]
|
||
style:UIAlertActionStyleDefault
|
||
handler:^(UIAlertAction * action) {
|
||
|
||
self->alert = nil;
|
||
|
||
}]];
|
||
|
||
|
||
[self presentViewController:self->alert animated:YES completion:nil];
|
||
}
|
||
else if (mxError && [mxError.errcode isEqualToString:kMXErrCodeStringNotFound])
|
||
{
|
||
MXLogDebug(@"[MXKAuthenticationVC] Forgot Password: not found");
|
||
|
||
NSMutableDictionary *userInfo;
|
||
if (error.userInfo)
|
||
{
|
||
userInfo = [NSMutableDictionary dictionaryWithDictionary:error.userInfo];
|
||
}
|
||
else
|
||
{
|
||
userInfo = [NSMutableDictionary dictionary];
|
||
}
|
||
userInfo[NSLocalizedDescriptionKey] = [VectorL10n authResetPasswordErrorNotFound];
|
||
|
||
[self onFailureDuringAuthRequest:[NSError errorWithDomain:kMXNSErrorDomain code:0 userInfo:userInfo]];
|
||
}
|
||
else
|
||
{
|
||
[self onFailureDuringAuthRequest:error];
|
||
}
|
||
|
||
}];
|
||
}
|
||
|
||
- (void)onFailureDuringMXOperation:(NSError*)error
|
||
{
|
||
mxCurrentOperation = nil;
|
||
|
||
[_authenticationActivityIndicator stopAnimating];
|
||
|
||
if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled)
|
||
{
|
||
// Ignore this error
|
||
MXLogDebug(@"[MXKAuthenticationVC] flows request cancelled");
|
||
return;
|
||
}
|
||
|
||
MXLogDebug(@"[MXKAuthenticationVC] Failed to get %@ flows: %@", (_authType == MXKAuthenticationTypeLogin ? @"Login" : @"Register"), error);
|
||
|
||
// Cancel external registration parameters if any
|
||
_externalRegistrationParameters = nil;
|
||
|
||
// Alert user
|
||
NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey];
|
||
if (!title)
|
||
{
|
||
title = [VectorL10n error];
|
||
}
|
||
NSString *msg = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
|
||
|
||
if (alert)
|
||
{
|
||
[alert dismissViewControllerAnimated:NO completion:nil];
|
||
}
|
||
|
||
alert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
|
||
|
||
[alert addAction:[UIAlertAction actionWithTitle:[VectorL10n dismiss]
|
||
style:UIAlertActionStyleDefault
|
||
handler:^(UIAlertAction * action) {
|
||
|
||
self->alert = nil;
|
||
|
||
}]];
|
||
|
||
|
||
[self presentViewController:alert animated:YES completion:nil];
|
||
|
||
// Handle specific error code here
|
||
if ([error.domain isEqualToString:NSURLErrorDomain])
|
||
{
|
||
// Check network reachability
|
||
if (error.code == NSURLErrorNotConnectedToInternet)
|
||
{
|
||
// Add reachability observer in order to launch a new request when network will be available
|
||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReachabilityStatusChange:) name:AFNetworkingReachabilityDidChangeNotification object:nil];
|
||
}
|
||
else if (error.code == kCFURLErrorTimedOut)
|
||
{
|
||
// Send a new request in 2 sec
|
||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||
[self refreshAuthenticationSession];
|
||
});
|
||
}
|
||
else
|
||
{
|
||
// Remove the potential auth inputs view
|
||
self.authInputsView = nil;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Remove the potential auth inputs view
|
||
self.authInputsView = nil;
|
||
}
|
||
|
||
if (!_authInputsView)
|
||
{
|
||
// Display failure reason
|
||
_noFlowLabel.hidden = NO;
|
||
_noFlowLabel.text = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
|
||
if (!_noFlowLabel.text.length)
|
||
{
|
||
_noFlowLabel.text = [VectorL10n loginErrorNoLoginFlow];
|
||
}
|
||
[_retryButton setTitle:[VectorL10n retry] forState:UIControlStateNormal];
|
||
[_retryButton setTitle:[VectorL10n retry] forState:UIControlStateNormal];
|
||
_retryButton.hidden = NO;
|
||
}
|
||
}
|
||
|
||
- (void)onReachabilityStatusChange:(NSNotification *)notif
|
||
{
|
||
AFNetworkReachabilityManager *reachabilityManager = [AFNetworkReachabilityManager sharedManager];
|
||
AFNetworkReachabilityStatus status = reachabilityManager.networkReachabilityStatus;
|
||
|
||
if (status == AFNetworkReachabilityStatusReachableViaWiFi || status == AFNetworkReachabilityStatusReachableViaWWAN)
|
||
{
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
[self refreshAuthenticationSession];
|
||
});
|
||
}
|
||
else if (status == AFNetworkReachabilityStatusNotReachable)
|
||
{
|
||
_noFlowLabel.text = [VectorL10n networkErrorNotReachable];
|
||
}
|
||
}
|
||
|
||
#pragma mark - Keyboard handling
|
||
|
||
- (void)dismissKeyboard
|
||
{
|
||
// Hide the keyboard
|
||
[_authInputsView dismissKeyboard];
|
||
[_homeServerTextField resignFirstResponder];
|
||
[_identityServerTextField resignFirstResponder];
|
||
}
|
||
|
||
#pragma mark - UITextField delegate
|
||
|
||
- (void)onTextFieldChange:(NSNotification *)notif
|
||
{
|
||
_submitButton.enabled = _authInputsView.areAllRequiredFieldsSet;
|
||
|
||
if (notif.object == _homeServerTextField)
|
||
{
|
||
// If any, the current request is obsolete
|
||
[self cancelIdentityServerCheck];
|
||
|
||
[self setIdentityServerHidden:YES];
|
||
}
|
||
}
|
||
|
||
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
|
||
{
|
||
if (textField == _homeServerTextField)
|
||
{
|
||
// Cancel supported AuthFlow refresh if a request is in progress
|
||
[[NSNotificationCenter defaultCenter] removeObserver:self name:AFNetworkingReachabilityDidChangeNotification object:nil];
|
||
|
||
if (mxCurrentOperation)
|
||
{
|
||
// Cancel potential request in progress
|
||
[mxCurrentOperation cancel];
|
||
mxCurrentOperation = nil;
|
||
}
|
||
}
|
||
|
||
return YES;
|
||
}
|
||
|
||
- (void)textFieldDidEndEditing:(UITextField *)textField
|
||
{
|
||
if (textField == _homeServerTextField)
|
||
{
|
||
[self setHomeServerTextFieldText:textField.text];
|
||
}
|
||
else if (textField == _identityServerTextField)
|
||
{
|
||
[self setIdentityServerTextFieldText:textField.text];
|
||
}
|
||
}
|
||
|
||
- (BOOL)textFieldShouldReturn:(UITextField*)textField
|
||
{
|
||
if (textField.returnKeyType == UIReturnKeyDone)
|
||
{
|
||
// "Done" key has been pressed
|
||
[textField resignFirstResponder];
|
||
}
|
||
return YES;
|
||
}
|
||
|
||
#pragma mark - AuthInputsViewDelegate delegate
|
||
|
||
- (void)authInputsView:(MXKAuthInputsView*)authInputsView presentAlertController:(UIAlertController*)inputsAlert
|
||
{
|
||
[self dismissKeyboard];
|
||
[self presentViewController:inputsAlert animated:YES completion:nil];
|
||
}
|
||
|
||
- (void)authInputsViewDidPressDoneKey:(MXKAuthInputsView *)authInputsView
|
||
{
|
||
if (_submitButton.isEnabled)
|
||
{
|
||
// Launch authentication now
|
||
[self onButtonPressed:_submitButton];
|
||
}
|
||
}
|
||
|
||
- (MXRestClient *)authInputsViewThirdPartyIdValidationRestClient:(MXKAuthInputsView *)authInputsView
|
||
{
|
||
return mxRestClient;
|
||
}
|
||
|
||
- (MXIdentityService *)authInputsViewThirdPartyIdValidationIdentityService:(MXIdentityService *)authInputsView
|
||
{
|
||
return self.identityService;
|
||
}
|
||
|
||
#pragma mark - Authentication Fallback
|
||
|
||
- (void)showAuthenticationFallBackView
|
||
{
|
||
[self showAuthenticationFallBackView:authenticationFallback];
|
||
}
|
||
|
||
- (void)showAuthenticationFallBackView:(NSString*)fallbackPage
|
||
{
|
||
_authenticationScrollView.hidden = YES;
|
||
_authFallbackContentView.hidden = NO;
|
||
|
||
// Add a cancel button in case of navigation controller use.
|
||
if (self.navigationController)
|
||
{
|
||
if (!cancelFallbackBarButton)
|
||
{
|
||
cancelFallbackBarButton = [[UIBarButtonItem alloc] initWithTitle:[VectorL10n loginLeaveFallback] style:UIBarButtonItemStylePlain target:self action:@selector(hideRegistrationFallbackView)];
|
||
}
|
||
|
||
// Add cancel button in right bar items
|
||
NSArray *rightBarButtonItems = self.navigationItem.rightBarButtonItems;
|
||
self.navigationItem.rightBarButtonItems = rightBarButtonItems ? [rightBarButtonItems arrayByAddingObject:cancelFallbackBarButton] : @[cancelFallbackBarButton];
|
||
}
|
||
|
||
if (self.softLogoutCredentials)
|
||
{
|
||
// Add device_id as query param of the fallback
|
||
NSURLComponents *components = [[NSURLComponents alloc] initWithString:fallbackPage];
|
||
|
||
NSMutableArray<NSURLQueryItem*> *queryItems = [components.queryItems mutableCopy];
|
||
if (!queryItems)
|
||
{
|
||
queryItems = [NSMutableArray array];
|
||
}
|
||
|
||
[queryItems addObject:[NSURLQueryItem queryItemWithName:@"device_id"
|
||
value:self.softLogoutCredentials.deviceId]];
|
||
|
||
components.queryItems = queryItems;
|
||
|
||
fallbackPage = components.URL.absoluteString;
|
||
}
|
||
|
||
[_authFallbackWebView openFallbackPage:fallbackPage success:^(MXLoginResponse *loginResponse) {
|
||
|
||
MXCredentials *credentials = [[MXCredentials alloc] initWithLoginResponse:loginResponse andDefaultCredentials:self->mxRestClient.credentials];
|
||
|
||
// TODO handle unrecognized certificate (if any) during registration through fallback webview.
|
||
|
||
[self onSuccessfulLogin:credentials];
|
||
}];
|
||
}
|
||
|
||
- (void)hideRegistrationFallbackView
|
||
{
|
||
if (cancelFallbackBarButton)
|
||
{
|
||
NSMutableArray *rightBarButtonItems = [NSMutableArray arrayWithArray: self.navigationItem.rightBarButtonItems];
|
||
[rightBarButtonItems removeObject:cancelFallbackBarButton];
|
||
self.navigationItem.rightBarButtonItems = rightBarButtonItems;
|
||
}
|
||
|
||
[_authFallbackWebView stopLoading];
|
||
_authenticationScrollView.hidden = NO;
|
||
_authFallbackContentView.hidden = YES;
|
||
}
|
||
|
||
#pragma mark - KVO
|
||
|
||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||
{
|
||
if ([@"viewHeightConstraint.constant" isEqualToString:keyPath])
|
||
{
|
||
// Refresh the height of the auth inputs view container.
|
||
CGFloat previousInputsContainerViewHeight = _authInputContainerViewHeightConstraint.constant;
|
||
_authInputContainerViewHeightConstraint.constant = _authInputsView.viewHeightConstraint.constant;
|
||
|
||
// Force to render the view
|
||
[self.view layoutIfNeeded];
|
||
|
||
// Refresh content view height by considering the updated height of inputs container
|
||
_contentViewHeightConstraint.constant += (_authInputContainerViewHeightConstraint.constant - previousInputsContainerViewHeight);
|
||
}
|
||
else
|
||
{
|
||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||
}
|
||
}
|
||
|
||
@end
|