Merge commit '5d625330b03d7fad12bdb5482b3c3fe19c32be88' into feature/basis_merge_1824

# 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
This commit is contained in:
Frank Rotermund
2022-09-19 11:31:20 +02:00
773 changed files with 15324 additions and 7024 deletions
@@ -16,14 +16,14 @@
#import "NSBundle+MatrixKit.h"
#import "NSBundle+MXKLanguage.h"
#import "MXKViewController.h"
#import "MXKAppSettings.h"
@implementation NSBundle (MatrixKit)
+ (NSBundle*)mxk_assetsBundle
{
// Get the bundle within MatrixKit
NSBundle *bundle = [NSBundle mxk_bundleForClass:[MXKViewController class]];
NSBundle *bundle = [NSBundle mxk_bundleForClass:[MXKAppSettings class]];
NSURL *assetsBundleURL = [bundle URLForResource:@"MatrixKitAssets" withExtension:@"bundle"];
return [NSBundle bundleWithURL:assetsBundleURL];
@@ -1,123 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixSDK/MatrixSDK.h>
#import "MXKViewController.h"
#import "MXKAttachment.h"
#import "MXKAttachmentAnimator.h"
@protocol MXKAttachmentsViewControllerDelegate;
/**
This view controller is used to display attachments of a room.
Only one attachment is displayed at once, the user is able to swipe one by one the attachment.
*/
@interface MXKAttachmentsViewController : MXKViewController <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UIDocumentInteractionControllerDelegate, MXKDestinationAttachmentAnimatorDelegate>
@property (nonatomic) IBOutlet UICollectionView *attachmentsCollection;
@property (nonatomic) IBOutlet UINavigationBar *navigationBar;
@property (unsafe_unretained, nonatomic) IBOutlet UIBarButtonItem *backButton;
/**
The attachments array.
*/
@property (nonatomic, readonly) NSArray *attachments;
/**
Tell whether all attachments have been retrieved from the room history (In that case no attachment can be added at the beginning of attachments array).
*/
@property (nonatomic) BOOL complete;
/**
The delegate notified when inputs are ready.
*/
@property (nonatomic, weak) id<MXKAttachmentsViewControllerDelegate> delegate;
#pragma mark - Class methods
/**
Returns the `UINib` object initialized for a `MXKAttachmentsViewController`.
@return The initialized `UINib` object or `nil` if there were errors during initialization
or the nib file could not be located.
@discussion You may override this method to provide a customized nib. If you do,
you should also override `roomViewController` to return your
view controller loaded from your custom nib.
*/
+ (UINib *)nib;
/**
Creates and returns a new `MXKAttachmentsViewController` object.
@discussion This is the designated initializer for programmatic instantiation.
@return An initialized `MXKAttachmentsViewController` object if successful, `nil` otherwise.
*/
+ (instancetype)attachmentsViewController;
/**
Creates and returns a new `MXKAttachmentsViewController` object, also sets sets up environment for animated interactive transitions.
*/
+ (instancetype)animatedAttachmentsViewControllerWithSourceViewController:(UIViewController <MXKSourceAttachmentAnimatorDelegate> *)sourceViewController;
/**
Display attachments of a room.
The provided event id is used to select the attachment to display first. Use nil to unchange the current displayed attachment.
By default the first attachment is displayed.
If the back pagination spinner is currently displayed and provided event id is nil,
the viewer will display the first added attachment during back pagination.
@param attachmentArray the array of attachments (MXKAttachment instances).
@param eventId the identifier of the attachment to display first.
*/
- (void)displayAttachments:(NSArray*)attachmentArray focusOn:(NSString*)eventId;
/**
Action used to handle the `backButton` in the navigation bar.
*/
- (IBAction)onButtonPressed:(id)sender;
@end
@protocol MXKAttachmentsViewControllerDelegate <NSObject>
/**
Ask the delegate for more attachments.
This method is called only if 'complete' is NO.
When some attachments are available, the delegate update the attachmnet list by using
[MXKAttachmentsViewController displayAttachments: focusOn:].
When no new attachment is available, the delegate must update the property 'complete'.
@param attachmentsViewController the attachments view controller.
@param eventId the event identifier of the current first attachment.
@return a boolean which tells whether some new attachments may be added or not.
*/
- (BOOL)attachmentsViewController:(MXKAttachmentsViewController*)attachmentsViewController paginateAttachmentBefore:(NSString*)eventId;
@optional
/**
Informs the delegate that a new attachment has been shown
the parameter eventId is used by the delegate to identify the attachment
*/
- (void)displayedNewAttachmentWithEventId:(NSString *)eventId;
@end
File diff suppressed because it is too large Load Diff
@@ -1,69 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MXKAttachmentsViewController">
<connections>
<outlet property="attachmentsCollection" destination="LOp-cf-Sji" id="4Zx-KZ-fkh"/>
<outlet property="backButton" destination="VjO-K2-a0w" id="VlD-Iw-NT5"/>
<outlet property="navigationBar" destination="orb-yb-k6g" id="ILw-DO-IH2"/>
<outlet property="view" destination="iN0-l3-epB" id="ieV-u7-rXU"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleAspectFit" alwaysBounceHorizontal="YES" pagingEnabled="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="LOp-cf-Sji">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="RrA-v8-THJ">
<size key="itemSize" width="600" height="600"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<connections>
<outlet property="dataSource" destination="-1" id="gLX-KL-R8w"/>
<outlet property="delegate" destination="-1" id="z1d-ep-n4n"/>
</connections>
</collectionView>
<navigationBar contentMode="scaleToFill" translucent="NO" translatesAutoresizingMaskIntoConstraints="NO" id="orb-yb-k6g">
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
<items>
<navigationItem id="lP4-eZ-AQ8">
<barButtonItem key="leftBarButtonItem" image="back_icon.png" style="plain" id="VjO-K2-a0w">
<connections>
<action selector="onButtonPressed:" destination="-1" id="PEa-WB-Up1"/>
</connections>
</barButtonItem>
</navigationItem>
</items>
</navigationBar>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="orb-yb-k6g" firstAttribute="top" secondItem="kHK-qN-rPN" secondAttribute="top" id="GGy-ix-LRO"/>
<constraint firstItem="LOp-cf-Sji" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="RQT-el-TH0"/>
<constraint firstAttribute="trailing" secondItem="LOp-cf-Sji" secondAttribute="trailing" id="Vgl-Wc-CI1"/>
<constraint firstAttribute="trailing" secondItem="orb-yb-k6g" secondAttribute="trailing" id="bFh-tc-Lub"/>
<constraint firstAttribute="bottom" secondItem="LOp-cf-Sji" secondAttribute="bottom" id="d22-2K-BEZ"/>
<constraint firstItem="LOp-cf-Sji" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="jEc-aG-zZn"/>
<constraint firstItem="orb-yb-k6g" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="toe-s8-clE"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<viewLayoutGuide key="safeArea" id="kHK-qN-rPN"/>
</view>
</objects>
<resources>
<image name="back_icon.png" width="13.5" height="23"/>
</resources>
</document>
@@ -30,6 +30,8 @@
#import "MXKSwiftHeader.h"
#import "GeneratedInterface-Swift.h"
@interface MXKAuthenticationViewController ()
{
/**
@@ -904,18 +906,21 @@
// This is required before updating view's textfields (homeserver url...)
[self loadViewIfNeeded];
// Force register mode
self.authType = MXKAuthenticationTypeLogin;
if (softLogoutCredentials)
{
// Force register mode
self.authType = MXKAuthenticationTypeLogin;
[self setHomeServerTextFieldText:softLogoutCredentials.homeServer];
[self setIdentityServerTextFieldText:softLogoutCredentials.identityServer];
[self setHomeServerTextFieldText:softLogoutCredentials.homeServer];
[self setIdentityServerTextFieldText:softLogoutCredentials.identityServer];
// Cancel potential request in progress
[mxCurrentOperation cancel];
mxCurrentOperation = nil;
// Cancel potential request in progress
[mxCurrentOperation cancel];
mxCurrentOperation = nil;
// Remove the current auth inputs view
self.authInputsView = 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;
@@ -1633,7 +1638,7 @@
- (void)updateRESTClient
{
NSString *homeserverURL = _homeServerTextField.text;
NSString *homeserverURL = [HomeserverAddress sanitized:_homeServerTextField.text];
if (homeserverURL.length)
{
@@ -302,11 +302,15 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer
@param oldPassword the old password.
@param newPassword the new password.
@param logoutDevices flag to logout from all devices.
@param success A block object called when the operation succeeds.
@param failure A block object called when the operation fails.
*/
- (void)changePassword:(NSString*)oldPassword with:(NSString*)newPassword success:(void (^)(void))success failure:(void (^)(NSError *error))failure;
- (void)changePassword:(NSString*)oldPassword
with:(NSString*)newPassword
logoutDevices:(BOOL)logoutDevices
success:(void (^)(void))success
failure:(void (^)(NSError *error))failure;
/**
Load the 3PIDs linked to this account.
@@ -146,7 +146,6 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
[self prepareRESTClient];
userPresence = MXPresenceUnknown;
// Refresh device information
[self loadDeviceInformation:nil failure:nil];
@@ -181,7 +180,6 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
[self registerIdentityServiceDidChangeAccessTokenNotification];
userPresence = MXPresenceUnknown;
// Refresh device information
[self loadDeviceInformation:nil failure:nil];
}
@@ -591,12 +589,13 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
}
}
- (void)changePassword:(NSString*)oldPassword with:(NSString*)newPassword success:(void (^)(void))success failure:(void (^)(NSError *error))failure
- (void)changePassword:(NSString*)oldPassword with:(NSString*)newPassword logoutDevices:(BOOL)logoutDevices success:(void (^)(void))success failure:(void (^)(NSError *error))failure
{
if (mxSession)
{
[mxRestClient changePassword:oldPassword
with:newPassword
logoutDevices:logoutDevices
success:^{
if (success) {
@@ -688,12 +687,12 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKAccountUserInfoDidChangeNotification object:self.mxCredentials.userId];
}
failure:^(NSError *error) {
MXLogDebug(@"[MXKAccount] %@: set user presence (%lu) and status message (%@) failed", self.mxCredentials.userId, (unsigned long)self->userPresence, self.userStatusMessage);
MXLogDebug(@"[MXKAccount] %@: set user presence (%lu) failed", self.mxCredentials.userId, (unsigned long)self->userPresence);
}];
}
else if (hideUserPresence)
{
MXLogDebug(@"[MXKAccount] %@: set user presence and status message is disabled.", self.mxCredentials.userId);
MXLogDebug(@"[MXKAccount] %@: set user presence is disabled.", self.mxCredentials.userId);
}
}
@@ -880,7 +879,7 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
- (void)logout:(void (^)(void))completion
{
if (!mxSession)
if (!mxSession || !mxSession.matrixRestClient)
{
MXLogDebug(@"[MXKAccount] logout: Need to open the closed session to make a logout request");
id<MXStore> store = [[[MXKAccountManager sharedManager].storeClass alloc] init];
@@ -967,6 +966,12 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
- (void)softLogout
{
if (_isSoftLogout)
{
// do not close the session if already soft logged out
// it may break the current logout request and resetting session credentials can cause crashes
return;
}
_isSoftLogout = YES;
[[MXKAccountManager sharedManager] saveAccounts];
@@ -48,6 +48,9 @@ extern NSString *const MXKAccountManagerDataType;
*/
@interface MXKAccountManager : NSObject
/// Flag indicating that saving accounts enabled. Defaults to `YES`.
@property (nonatomic, assign, getter=isSavingAccountsEnabled) BOOL savingAccountsEnabled;
/**
The class of store used to open matrix session for the accounts. This class must be conformed to MXStore protocol.
By default this class is MXFileStore.
@@ -72,6 +72,7 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa
{
_storeClass = [MXFileStore class];
_dehydrationService = [MXDehydrationService new];
_savingAccountsEnabled = YES;
// Migrate old account file to new format
[self migrateAccounts];
@@ -108,10 +109,15 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa
#pragma clang diagnostic ignored "-Wdeprecated"
- (void)saveAccounts
{
NSDate *startDate = [NSDate date];
MXLogDebug(@"[MXKAccountManager] saveAccounts...");
if (!self.isSavingAccountsEnabled)
{
MXLogDebug(@"[MXKAccountManager] saveAccounts: saving disabled.");
return;
}
NSDate *startDate = [NSDate date];
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
@@ -86,11 +86,6 @@
*/
@property (nonatomic, readonly) BOOL hasLink;
/**
Whether the data has a thread root in its components.
*/
@property (nonatomic, readonly) BOOL hasThreadRoot;
/**
Event formatter
*/
@@ -48,14 +48,33 @@
self.readReceipts = [NSMutableDictionary dictionary];
// Create the bubble component based on matrix event
MXKRoomBubbleComponent *firstComponent = [[MXKRoomBubbleComponent alloc] initWithEvent:event roomState:roomState eventFormatter:roomDataSource.eventFormatter session:roomDataSource.mxSession];
MXKRoomBubbleComponent *firstComponent = [[MXKRoomBubbleComponent alloc] initWithEvent:event
roomState:roomState
andLatestRoomState:roomDataSource.roomState
eventFormatter:roomDataSource.eventFormatter
session:roomDataSource.mxSession];
if (firstComponent)
{
bubbleComponents = [NSMutableArray array];
[bubbleComponents addObject:firstComponent];
senderId = event.sender;
targetId = [event.type isEqualToString:kMXEventTypeStringRoomMember] ? event.stateKey : nil;
if ([event.type isEqualToString:kMXEventTypeStringRoomMember])
{
MXRoomMemberEventContent *content = [MXRoomMemberEventContent modelFromJSON:event.content];
if (![content.membership isEqualToString:kMXMembershipStringJoin])
{
targetId = event.stateKey;
}
else
{
targetId = event.sender;
}
}
else
{
targetId = nil;
}
roomId = roomDataSource.roomId;
// If `roomScreenUseOnlyLatestUserAvatarAndName`is enabled, the avatar and name are
@@ -110,6 +129,21 @@
bubbleComponents = nil;
}
- (void)refreshProfilesIfNeeded:(MXRoomState *)latestRoomState
{
if (RiotSettings.shared.roomScreenUseOnlyLatestUserAvatarAndName)
{
[self setRoomState:latestRoomState];
}
}
/**
Sets the `MXRoomState` for a buble cell. This allows to adapt the display
of a cell with a different room state than its historical. This won't update critical
flag/status, such as `isEncryptedRoom`.
@param roomState the `MXRoomState` to use for this cell.
*/
- (void)setRoomState:(MXRoomState *)roomState;
{
MXEvent* firstEvent = self.events.firstObject;
@@ -141,7 +175,10 @@
MXKRoomBubbleComponent *roomBubbleComponent = [bubbleComponents objectAtIndex:index];
if ([roomBubbleComponent.event.eventId isEqualToString:eventId])
{
[roomBubbleComponent updateWithEvent:event roomState:roomDataSource.roomState session:self.mxSession];
[roomBubbleComponent updateWithEvent:event
roomState:roomDataSource.roomState
andLatestRoomState:nil
session:self.mxSession];
if (!roomBubbleComponent.textMessage.length)
{
[bubbleComponents removeObjectAtIndex:index];
@@ -467,6 +504,22 @@
}
}
- (BOOL)hasThreadRoot
{
@synchronized (bubbleComponents)
{
for (MXKRoomBubbleComponent *component in bubbleComponents)
{
if (component.thread)
{
return YES;
}
}
}
return NO;
}
#pragma mark -
- (void)invalidateTextLayout
@@ -656,22 +709,6 @@
return NO;
}
- (BOOL)hasThreadRoot
{
@synchronized (bubbleComponents)
{
for (MXKRoomBubbleComponent *component in bubbleComponents)
{
if (component.thread)
{
return YES;
}
}
}
return NO;
}
- (MXKRoomBubbleComponentDisplayFix)displayFix
{
MXKRoomBubbleComponentDisplayFix displayFix = MXKRoomBubbleComponentDisplayFixNone;
@@ -701,6 +738,13 @@
return res;
}
- (BOOL)canInvitePeople
{
NSInteger requiredLevel = roomDataSource.roomState.powerLevels.invite;
NSInteger myLevel = [roomDataSource.roomState.powerLevels powerLevelOfUserWithUserID:roomDataSource.mxSession.myUserId];
return myLevel >= requiredLevel;
}
- (NSArray*)events
{
NSMutableArray* eventsArray;
@@ -112,11 +112,21 @@
*/
@property (nonatomic) BOOL shouldHideSenderInformation;
/**
Flag indicating whether the user can invite people in this room.
*/
@property (nonatomic, readonly) BOOL canInvitePeople;
/**
Tell whether this bubble has nothing to display (neither a message nor an attachment).
*/
@property (nonatomic, readonly) BOOL hasNoDisplay;
/**
Whether the data has a thread root in its components.
*/
@property (nonatomic, readonly) BOOL hasThreadRoot;
/**
The list of events (`MXEvent` instances) handled by this bubble.
*/
@@ -232,13 +242,11 @@
- (instancetype)initWithEvent:(MXEvent*)event andRoomState:(MXRoomState*)roomState andRoomDataSource:(MXKRoomDataSource*)roomDataSource;
/**
Sets the `MXRoomState` for a buble cell. This allows to adapt the display
of a cell with a different room state than its historical. This won't update critical
flag/status, such as `isEncryptedRoom`.
Refresh avatars and display names (AKA profiles) displayed in the cell if needed.
@param roomState the `MXRoomState` to use for this cell.
@param latestRoomState the latest `MXRoomState` from the data source.
*/
- (void)setRoomState:(MXRoomState *)roomState;
- (void)refreshProfilesIfNeeded:(MXRoomState *)latestRoomState;
/**
Update the event because its sent state changed or it is has been redacted.
@@ -78,7 +78,11 @@ static NSAttributedString *messageSeparator = nil;
}
// Create new message component
MXKRoomBubbleComponent *addedComponent = [[MXKRoomBubbleComponent alloc] initWithEvent:event roomState:roomState eventFormatter:roomDataSource.eventFormatter session:self.mxSession];
MXKRoomBubbleComponent *addedComponent = [[MXKRoomBubbleComponent alloc] initWithEvent:event
roomState:roomState
andLatestRoomState:roomDataSource.roomState
eventFormatter:roomDataSource.eventFormatter
session:self.mxSession];
if (addedComponent)
{
[self addComponent:addedComponent];
@@ -115,20 +115,29 @@ typedef enum : NSUInteger {
@param event the event used to compose the bubble component.
@param roomState the room state when the event occured.
@param latestRoomState the latest room state of the room containing this event.
@param eventFormatter object used to format event into displayable string.
@param session the related matrix session.
@return the newly created instance.
*/
- (instancetype)initWithEvent:(MXEvent*)event roomState:(MXRoomState*)roomState eventFormatter:(MXKEventFormatter*)eventFormatter session:(MXSession*)session;
- (instancetype)initWithEvent:(MXEvent*)event
roomState:(MXRoomState*)roomState
andLatestRoomState:(MXRoomState*)latestRoomState
eventFormatter:(MXKEventFormatter*)eventFormatter
session:(MXSession*)session;
/**
Update the event because its sent state changed or it is has been redacted.
@param event the new event data.
@param roomState the up-to-date state of the room.
@param latestRoomState the latest room state of the room containing this event.
@param session the related matrix session.
*/
- (void)updateWithEvent:(MXEvent*)event roomState:(MXRoomState*)roomState session:(MXSession*)session;
- (void)updateWithEvent:(MXEvent*)event
roomState:(MXRoomState*)roomState
andLatestRoomState:(MXRoomState*)latestRoomState
session:(MXSession*)session;
@end
@@ -28,7 +28,11 @@
@implementation MXKRoomBubbleComponent
- (instancetype)initWithEvent:(MXEvent*)event roomState:(MXRoomState*)roomState eventFormatter:(MXKEventFormatter*)eventFormatter session:(MXSession*)session;
- (instancetype)initWithEvent:(MXEvent*)event
roomState:(MXRoomState*)roomState
andLatestRoomState:(MXRoomState*)latestRoomState
eventFormatter:(MXKEventFormatter*)eventFormatter
session:(MXSession*)session;
{
if (self = [super init])
{
@@ -36,7 +40,10 @@
_eventFormatter = eventFormatter;
MXKEventFormatterError error;
NSAttributedString *eventString = [_eventFormatter attributedStringFromEvent:event withRoomState:roomState error:&error];
NSAttributedString *eventString = [_eventFormatter attributedStringFromEvent:event
withRoomState:roomState
andLatestRoomState:latestRoomState
error:&error];
// Store the potential error
event.mxkEventFormatterError = error;
@@ -84,7 +91,10 @@
return self;
}
- (void)updateWithEvent:(MXEvent*)event roomState:(MXRoomState*)roomState session:(MXSession*)session
- (void)updateWithEvent:(MXEvent*)event
roomState:(MXRoomState*)roomState
andLatestRoomState:(MXRoomState*)latestRoomState
session:(MXSession*)session
{
// Report the new event
_event = event;
@@ -101,7 +111,10 @@
_textMessage = nil;
MXKEventFormatterError error;
_attributedTextMessage = [_eventFormatter attributedStringFromEvent:event withRoomState:roomState error:&error];
_attributedTextMessage = [_eventFormatter attributedStringFromEvent:event
withRoomState:roomState
andLatestRoomState:latestRoomState
error:&error];
_showEncryptionBadge = [self shouldShowWarningBadgeForEvent:event roomState:roomState session:session];
@@ -1021,13 +1021,13 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
MXStrongifyAndReturnIfNil(self);
if (event.eventType == MXEventTypeRoomMember && event.isUserProfileChange)
{
[self refreshProfilesIfNeeded];
}
if (MXTimelineDirectionForwards == direction)
{
if (event.eventType == MXEventTypeRoomMember && event.isUserProfileChange)
{
[self refreshProfilesIfNeeded];
}
// Check for local echo suppression
MXEvent *localEcho;
if (self.room.outgoingMessages.count && [event.sender isEqualToString:self.mxSession.myUser.userId])
@@ -1128,6 +1128,8 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
// Update bubble data
NSUInteger remainingEvents = [bubbleData updateEvent:redactionEvent.redacts withEvent:redactedEvent];
[self refreshRepliesWithUpdatedEventId:redactedEvent.eventId];
hasChanged = YES;
// Remove the bubble if there is no more events
@@ -3666,7 +3668,10 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
while ((nextBubbleData = nextBubbleData.nextCollapsableCellData));
// Build the summary string for the series
bubbleData.collapsedAttributedTextMessage = [self.eventFormatter attributedStringFromEvents:events withRoomState:bubbleData.collapseState error:nil];
bubbleData.collapsedAttributedTextMessage = [self.eventFormatter attributedStringFromEvents:events
withRoomState:bubbleData.collapseState
andLatestRoomState:self.roomState
error:nil];
// Release collapseState objects, even the one of collapsableSeriesAtStart.
// We do not need to keep its state because if an collapsable event comes before collapsableSeriesAtStart,
@@ -4242,10 +4247,34 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
}
}
- (BOOL)refreshRepliesWithUpdatedEventId:(NSString*)updatedEventId
{
BOOL hasChanged = NO;
@synchronized (bubbles) {
for (id<MXKRoomBubbleCellDataStoring> bubbleCellData in bubbles)
{
for (MXEvent *event in bubbleCellData.events)
{
if ([event.relatesTo.inReplyTo.eventId isEqual:updatedEventId])
{
[bubbleCellData updateEvent:event.eventId withEvent:event];
[bubbleCellData invalidateTextLayout];
hasChanged = YES;
}
}
}
}
return hasChanged;
}
- (BOOL)updateCellData:(id<MXKRoomBubbleCellDataStoring>)bubbleCellData forEditionWithReplaceEvent:(MXEvent*)replaceEvent andEventId:(NSString*)eventId
{
BOOL hasChanged = NO;
hasChanged = [self refreshRepliesWithUpdatedEventId:eventId];
@synchronized (bubbleCellData)
{
// Retrieve the original event to edit it
@@ -4333,18 +4362,14 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
#pragma mark - Use Only Latest Profiles
/**
Refreshes the avatars and display names if needed. This has no effect
if `roomScreenUseOnlyLatestUserAvatarAndName` is disabled.
Refresh avatars and display names (AKA profiles) if needed.
*/
- (void)refreshProfilesIfNeeded
{
if (RiotSettings.shared.roomScreenUseOnlyLatestUserAvatarAndName)
{
@synchronized (bubbles) {
for (id<MXKRoomBubbleCellDataStoring> bubble in bubbles)
{
[bubble setRoomState:self.roomState];
}
@synchronized (bubbles) {
for (id<MXKRoomBubbleCellDataStoring> bubble in bubbles)
{
[bubble refreshProfilesIfNeeded:self.roomState];
}
}
}
@@ -40,6 +40,10 @@ class MXKSendReplyEventStringLocalizer: NSObject, MXSendReplyEventStringLocalize
func senderSentTheirLocation() -> String {
return VectorL10n.messageReplyToSenderSentTheirLocation
}
func senderSentTheirLiveLocation() -> String {
return VectorL10n.messageReplyToSenderSentTheirLiveLocation
}
func messageToReplyToPrefix() -> String {
return VectorL10n.messageReplyToMessageToReplyToPrefix
@@ -31,17 +31,28 @@
return nil;
}
NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey];
NSString *message = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
if (!title)
NSString *title;
NSString *message;
if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorNotConnectedToInternet)
{
title = [VectorL10n error];
title = [VectorL10n networkOfflineTitle];
message = [VectorL10n networkOfflineMessage];
}
if (!message)
else
{
message = [VectorL10n errorCommonMessage];
title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey];
message = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
if (!title)
{
title = [VectorL10n error];
}
if (!message)
{
message = [VectorL10n errorCommonMessage];
}
}
return [[MXKErrorViewModel alloc] initWithTitle:title message:message];
@@ -182,30 +182,42 @@ typedef enum : NSUInteger {
@param event the event to format.
@param roomState the room state right before the event.
@param latestRoomState the latest room state of the room containing this event.
@param error the error code. In case of formatting error, the formatter may return non nil string as a proposal.
@return the display text for the event.
*/
- (NSString*)stringFromEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState error:(MXKEventFormatterError*)error;
- (NSString*)stringFromEvent:(MXEvent*)event
withRoomState:(MXRoomState*)roomState
andLatestRoomState:(MXRoomState*)latestRoomState
error:(MXKEventFormatterError*)error;
/**
Generate a displayable attributed string representating the event.
@param event the event to format.
@param roomState the room state right before the event.
@param latestRoomState the latest room state of the room containing this event.
@param error the error code. In case of formatting error, the formatter may return non nil string as a proposal.
@return the attributed string for the event.
*/
- (NSAttributedString*)attributedStringFromEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState error:(MXKEventFormatterError*)error;
- (NSAttributedString*)attributedStringFromEvent:(MXEvent*)event
withRoomState:(MXRoomState*)roomState
andLatestRoomState:(MXRoomState*)latestRoomState
error:(MXKEventFormatterError*)error;
/**
Generate a displayable attributed string representating a summary for the provided events.
@param events the series of events to format.
@param roomState the room state right before the first event in the series.
@param latestRoomState the latest room state of the room containing this event.
@param error the error code. In case of formatting error, the formatter may return non nil string as a proposal.
@return the attributed string.
*/
- (NSAttributedString*)attributedStringFromEvents:(NSArray<MXEvent*>*)events withRoomState:(MXRoomState*)roomState error:(MXKEventFormatterError*)error;
- (NSAttributedString*)attributedStringFromEvents:(NSArray<MXEvent*>*)events
withRoomState:(MXRoomState*)roomState
andLatestRoomState:(MXRoomState*)latestRoomState
error:(MXKEventFormatterError*)error;
/**
Render a random string into an attributed string with the font and the text color
@@ -223,22 +235,20 @@ typedef enum : NSUInteger {
@param htmlString the HTLM string to render.
@param event the event associated to the string.
@param roomState the room state right before the event.
@param roomState the room state right before the event. If nil, replies will not get constructed or formatted.
@return an attributed string.
*/
- (NSAttributedString*)renderHTMLString:(NSString*)htmlString forEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState;
- (NSAttributedString*)renderHTMLString:(NSString*)htmlString
forEvent:(MXEvent*)event
withRoomState:(MXRoomState*)roomState
andLatestRoomState:(MXRoomState*)latestRoomState;
/**
Render a random html string into an attributed string with the font and the text color
that correspond to the passed event.
Defines the replacement attributed string for a redacted message.
@param htmlString the HTLM string to render.
@param event the event associated to the string.
@param roomState the room state right before the event.
@param isEditMode wether string will be used for edition in the composer
@return an attributed string.
@return attributed string describing redacted message.
*/
- (NSAttributedString*)renderHTMLString:(NSString*)htmlString forEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState isEditMode:(BOOL)isEditMode;
- (NSAttributedString*)redactedMessageReplacementAttributedString;
/**
Same as [self renderString:forEvent:] but add a prefix.
@@ -28,8 +28,9 @@
#import "MXRoom+Sync.h"
#import "MXKRoomNameStringLocalizer.h"
#import "GeneratedInterface-Swift.h"
static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
static NSString *const kHTMLATagRegexPattern = @"<a href=(?:'|\")(.*?)(?:'|\")>([^<]*)</a>";
@interface MXKEventFormatter ()
{
@@ -43,7 +44,6 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
*/
NSDataDetector *linkDetector;
}
@end
@implementation MXKEventFormatter
@@ -296,10 +296,16 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
#pragma mark - Events to strings conversion methods
- (NSString*)stringFromEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState error:(MXKEventFormatterError*)error
- (NSString*)stringFromEvent:(MXEvent*)event
withRoomState:(MXRoomState*)roomState
andLatestRoomState:(MXRoomState*)latestRoomState
error:(MXKEventFormatterError*)error
{
NSString *stringFromEvent;
NSAttributedString *attributedStringFromEvent = [self attributedStringFromEvent:event withRoomState:roomState error:error];
NSAttributedString *attributedStringFromEvent = [self attributedStringFromEvent:event
withRoomState:roomState
andLatestRoomState:latestRoomState
error:error];
if (*error == MXKEventFormatterErrorNone)
{
stringFromEvent = attributedStringFromEvent.string;
@@ -308,11 +314,14 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
return stringFromEvent;
}
- (NSAttributedString *)attributedStringFromEvent:(MXEvent *)event withRoomState:(MXRoomState *)roomState error:(MXKEventFormatterError *)error
- (NSAttributedString *)attributedStringFromEvent:(MXEvent*)event
withRoomState:(MXRoomState*)roomState
andLatestRoomState:(MXRoomState*)latestRoomState
error:(MXKEventFormatterError *)error
{
// Check we can output the error
NSParameterAssert(error);
*error = MXKEventFormatterErrorNone;
// Filter the events according to their type.
@@ -1347,9 +1356,22 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
}
else if ([msgtype isEqualToString:kMXMessageTypeFile])
{
body = body? body : [VectorL10n noticeFileAttachment];
// Check attachment validity
if (![self isSupportedAttachment:event])
if ([self isSupportedAttachment:event])
{
body = body? body : [VectorL10n noticeFileAttachment];
NSDictionary *fileInfo = contentToUse[@"info"];
if (fileInfo)
{
NSNumber *fileSize = fileInfo[@"size"];
if (fileSize)
{
body = [NSString stringWithFormat:@"%@ (%@)", body, [MXTools fileSizeToString: fileSize.longValue]];
}
}
}
else
{
MXLogDebug(@"[MXKEventFormatter] Warning: Unsupported attachment %@", event.description);
body = [VectorL10n noticeInvalidAttachment];
@@ -1360,7 +1382,10 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
if (isHTML)
{
// Build the attributed string from the HTML string
attributedDisplayText = [self renderHTMLString:body forEvent:event withRoomState:roomState];
attributedDisplayText = [self renderHTMLString:body
forEvent:event
withRoomState:roomState
andLatestRoomState:latestRoomState];
}
else
{
@@ -1606,6 +1631,11 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
displayText = [MXEventContentPollStart modelFromJSON:event.content].question;
break;
}
case MXEventTypeBeaconInfo:
{
displayText = [MXBeaconInfo modelFromJSON:event.content].desc;
break;
}
default:
*error = MXKEventFormatterErrorUnknownEventType;
break;
@@ -1664,7 +1694,10 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
return attributedDisplayText;
}
- (NSAttributedString*)attributedStringFromEvents:(NSArray<MXEvent*>*)events withRoomState:(MXRoomState*)roomState error:(MXKEventFormatterError*)error
- (NSAttributedString*)attributedStringFromEvents:(NSArray<MXEvent*>*)events
withRoomState:(MXRoomState*)roomState
andLatestRoomState:(MXRoomState*)latestRoomState
error:(MXKEventFormatterError*)error
{
// TODO: Do a full summary
return nil;
@@ -1724,7 +1757,8 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
{
// body font is the same with the whole string font, no need to change body font
// apply additional treatments
return [self postRenderAttributedString:str];
[self postRenderAttributedString:str];
return str;
}
NSString *body;
@@ -1741,96 +1775,141 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
{
// body not found in the whole string
// apply additional treatments
return [self postRenderAttributedString:str];
[self postRenderAttributedString:str];
return str;
}
NSMutableAttributedString *mutableStr = [str mutableCopy];
[mutableStr addAttribute:NSFontAttributeName value:fontForBody range:bodyRange];
[str addAttribute:NSFontAttributeName value:fontForBody range:bodyRange];
// apply additional treatments
return [self postRenderAttributedString:mutableStr];
[self postRenderAttributedString:str];
return str;
}
- (NSAttributedString*)renderHTMLString:(NSString*)htmlString forEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState
{
return [self renderHTMLString:htmlString forEvent:event withRoomState:roomState isEditMode:NO];
}
- (NSAttributedString*)renderHTMLString:(NSString*)htmlString forEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState isEditMode:(BOOL)isEditMode
- (NSAttributedString*)renderHTMLString:(NSString*)htmlString
forEvent:(MXEvent*)event
withRoomState:(MXRoomState*)roomState
andLatestRoomState:(MXRoomState*)latestRoomState
{
NSString *html = htmlString;
MXEvent *repliedEvent;
// Special treatment for "In reply to" message
// Note: `isEditMode` fixes an issue where editing a reply would display an "In reply to" span instead of a mention.
if (!isEditMode && (event.isReplyEvent || (!RiotSettings.shared.enableThreads && event.isInThread)))
if (roomState && (event.isReplyEvent || (!RiotSettings.shared.enableThreads && event.isInThread)))
{
repliedEvent = [self->mxSession.store eventWithEventId:event.relatesTo.inReplyTo.eventId inRoom:roomState.roomId];
if (repliedEvent)
{
// Try to construct rich reply.
html = [self buildHTMLStringForEvent:event inReplyToEvent:repliedEvent] ?: html;
}
html = [self renderReplyTo:html withRoomState:roomState];
}
// Apply the css style that corresponds to the event state
UIFont *fontForWholeString = [self fontForEvent:event string:htmlString];
// Do some sanitisation before finalizing the string
MXWeakify(self);
DTHTMLAttributedStringBuilderWillFlushCallback sanitizeCallback = ^(DTHTMLElement *element) {
NSAttributedString *str = [HTMLFormatter formatHTML:html
withAllowedTags:_allowedHTMLTags
font:fontForWholeString
andImageHandler:_htmlImageHandler
extraOptions:@{ DTDefaultTextColor: [self textColorForEvent:event],
DTDefaultStyleSheet: dtCSS }
postFormatOperations:^(NSMutableAttributedString *mutableStr) {
MXStrongifyAndReturnIfNil(self);
[element sanitizeWith:self.allowedHTMLTags bodyFont:fontForWholeString imageHandler:self.htmlImageHandler];
};
[self postFormatMutableAttributedString:mutableStr
forEvent:event
andRepliedEvent:repliedEvent
defaultFont:fontForWholeString];
}];
NSDictionary *options = @{
DTUseiOS6Attributes: @(YES), // Enable it to be able to display the attributed string in a UITextView
DTDefaultFontFamily: fontForWholeString.familyName,
DTDefaultFontName: fontForWholeString.fontName,
DTDefaultFontSize: @(fontForWholeString.pointSize),
DTDefaultTextColor: [self textColorForEvent:event],
DTDefaultLinkDecoration: @(NO),
DTDefaultStyleSheet: dtCSS,
DTWillFlushBlockCallBack: sanitizeCallback
};
return str;
}
// Do not use the default HTML renderer of NSAttributedString because this method
// runs on the UI thread which we want to avoid because renderHTMLString is called
// most of the time from a background thread.
// Use DTCoreText HTML renderer instead.
// Using DTCoreText, which renders static string, helps to avoid code injection attacks
// that could happen with the default HTML renderer of NSAttributedString which is a
// webview.
NSAttributedString *str = [[NSAttributedString alloc] initWithHTMLData:[html dataUsingEncoding:NSUTF8StringEncoding] options:options documentAttributes:NULL];
// Apply additional treatments
str = [self postRenderAttributedString:str];
- (NSAttributedString*)redactedMessageReplacementAttributedString
{
return [[NSAttributedString alloc] initWithString:VectorL10n.eventFormatterMessageDeleted];
}
// Finalize the attributed string by removing DTCoreText artifacts (Trim trailing newlines).
str = [MXKTools removeDTCoreTextArtifacts:str];
/**
Build the HTML body of a reply from its related event (rich replies).
// Finalize HTML blockquote blocks marking
str = [MXKTools removeMarkedBlockquotesArtifacts:str];
@param event the reply event.
@param repliedEvent the event it replies to.
@return an html string containing the updated content of both events.
*/
- (NSString*)buildHTMLStringForEvent:(MXEvent*)event inReplyToEvent:(MXEvent*)repliedEvent
{
NSString *repliedEventContent;
NSString *eventContent;
NSString *html;
UIFont *fontForBody = [self fontForEvent:event string:nil];
if ([fontForWholeString isEqual:fontForBody])
if (repliedEvent.isRedactedEvent)
{
// body font is the same with the whole string font, no need to change body font
return str;
}
NSString *body;
if (event.content[kMXMessageContentKeyNewContent])
{
MXJSONModelSetString(body, event.content[kMXMessageContentKeyNewContent][kMXMessageBodyKey]);
repliedEventContent = nil;
}
else
{
MXJSONModelSetString(body, event.content[kMXMessageBodyKey]);
}
NSRange bodyRange = [str.string rangeOfString:body];
if (bodyRange.location == NSNotFound)
{
// body not found in the whole string
return str;
if (repliedEvent.content[kMXMessageContentKeyNewContent])
{
MXJSONModelSetString(repliedEventContent, repliedEvent.content[kMXMessageContentKeyNewContent][@"formatted_body"]);
if (!repliedEventContent)
{
MXJSONModelSetString(repliedEventContent, repliedEvent.content[kMXMessageContentKeyNewContent][kMXMessageBodyKey]);
}
}
else
{
MXJSONModelSetString(repliedEventContent, repliedEvent.content[@"formatted_body"]);
if (!repliedEventContent)
{
MXJSONModelSetString(repliedEventContent, repliedEvent.content[kMXMessageBodyKey]);
}
}
// No message content in a non-redacted event. Formatter should use fallback.
if (!repliedEventContent)
{
MXLogWarning(@"[MXKEventFormatter] Unable to retrieve content from replied event %@", repliedEvent.description)
return nil;
}
}
NSMutableAttributedString *mutableStr = [str mutableCopy];
[mutableStr addAttribute:NSFontAttributeName value:fontForBody range:bodyRange];
return mutableStr;
if (event.content[kMXMessageContentKeyNewContent])
{
MXJSONModelSetString(eventContent, event.content[kMXMessageContentKeyNewContent][@"formatted_body"]);
if (!eventContent)
{
MXJSONModelSetString(eventContent, event.content[kMXMessageContentKeyNewContent][kMXMessageBodyKey]);
}
}
else
{
MXReplyEventParser *parser = [[MXReplyEventParser alloc] init];
MXReplyEventParts *parts = [parser parse:event];
MXJSONModelSetString(eventContent, parts.formattedBodyParts.replyText)
if (!eventContent)
{
MXJSONModelSetString(eventContent, parts.bodyParts.replyText)
}
}
if (eventContent && repliedEvent.sender)
{
html = [NSString stringWithFormat:@"<mx-reply><blockquote><a href=\"%@\">In reply to</a> <a href=\"%@\">%@</a><br>%@</blockquote></mx-reply>%@",
[MXTools permalinkToEvent:repliedEvent.eventId inRoom:repliedEvent.roomId],
[MXTools permalinkToUserWithUserId:repliedEvent.sender],
repliedEvent.sender,
repliedEventContent,
eventContent];
}
else
{
MXLogWarning(@"[MXKEventFormatter] Unable to build reply event %@", event.description)
}
return html;
}
/**
@@ -1844,6 +1923,14 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
*/
- (NSString*)renderReplyTo:(NSString*)htmlString withRoomState:(MXRoomState*)roomState
{
NSInteger mxReplyEndLocation = [htmlString rangeOfString:@"</mx-reply>"].location;
if (mxReplyEndLocation == NSNotFound)
{
MXLogWarning(@"[MXKEventFormatter] Missing mx-reply block in html string");
return htmlString;
}
NSString *html = htmlString;
static NSRegularExpression *htmlATagRegex;
@@ -1861,7 +1948,7 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
[htmlATagRegex enumerateMatchesInString:html
options:0
range:NSMakeRange(0, html.length)
range:NSMakeRange(0, mxReplyEndLocation)
usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop) {
if (hrefCount > 1)
@@ -1896,7 +1983,7 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
if (senderDisplayName)
{
html = [html stringByReplacingCharactersInRange:userIdRange withString:senderDisplayName];
html = [html stringByReplacingCharactersInRange:userIdRange withString:senderDisplayName.stringByAddingHTMLEntities];
}
}
@@ -1918,11 +2005,55 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
return html;
}
- (NSAttributedString*)postRenderAttributedString:(NSAttributedString*)attributedString
- (void)postFormatMutableAttributedString:(NSMutableAttributedString*)mutableAttributedString
forEvent:(MXEvent*)event
andRepliedEvent:(MXEvent*)repliedEvent
defaultFont:(UIFont*)defaultFont
{
if (!attributedString)
[self postRenderAttributedString:mutableAttributedString];
[MXKTools removeMarkedBlockquotesArtifacts:mutableAttributedString];
if (repliedEvent && repliedEvent.isRedactedEvent)
{
return nil;
// Replace the description of an empty replied event
NSRange nullRange = [mutableAttributedString.string rangeOfString:@"(null)"];
if (nullRange.location != NSNotFound)
{
[mutableAttributedString replaceCharactersInRange:nullRange withAttributedString:[self redactedMessageReplacementAttributedString]];
}
}
UIFont *fontForBody = [self fontForEvent:event string:nil];
if ([defaultFont isEqual:fontForBody])
{
// body font is the same with the whole string font, no need to change body font
return;
}
NSString *body;
if (event.content[kMXMessageContentKeyNewContent])
{
MXJSONModelSetString(body, event.content[kMXMessageContentKeyNewContent][kMXMessageBodyKey]);
}
else
{
MXJSONModelSetString(body, event.content[kMXMessageBodyKey]);
}
NSRange bodyRange = [mutableAttributedString.string rangeOfString:body];
if (bodyRange.location == NSNotFound)
{
// body not found in the whole string
return;
}
[mutableAttributedString addAttribute:NSFontAttributeName value:fontForBody range:bodyRange];
}
- (void)postRenderAttributedString:(NSMutableAttributedString*)mutableAttributedString
{
if (!mutableAttributedString)
{
return;
}
NSInteger enabledMatrixIdsBitMask= 0;
@@ -1957,7 +2088,7 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
enabledMatrixIdsBitMask |= MXKTOOLS_GROUP_IDENTIFIER_BITWISE;
}
return [MXKTools createLinksInAttributedString:attributedString forEnabledMatrixIds:enabledMatrixIdsBitMask];
[MXKTools createLinksInMutableAttributedString:mutableAttributedString forEnabledMatrixIds:enabledMatrixIdsBitMask];
}
- (NSAttributedString *)renderString:(NSString *)string withPrefix:(NSString *)prefix forEvent:(MXEvent *)event
@@ -2038,7 +2169,10 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
// Note that we use the current room state (roomState) because when we display
// users displaynames, we want current displaynames
MXKEventFormatterError error;
NSString *lastMessageString = [self stringFromEvent:event withRoomState:roomState error:&error];
NSString *lastMessageString = [self stringFromEvent:event
withRoomState:roomState
andLatestRoomState:nil
error:&error];
if (0 == lastMessageString.length)
{
+6 -9
View File
@@ -369,19 +369,17 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
- Trim trailing whitespace and newlines in the string content.
- Replace DTImageTextAttachments with a simple NSTextAttachment subclass.
@param attributedString an attributed string.
@return the resulting string.
@param mutableAttributedString a mutable attributed string.
*/
+ (NSAttributedString*)removeDTCoreTextArtifacts:(NSAttributedString*)attributedString;
+ (void)removeDTCoreTextArtifacts:(NSMutableAttributedString*)mutableAttributedString;
/**
Make some matrix identifiers clickable in the string content.
@param attributedString an attributed string.
@param mutableAttributedString a mutable attributed string.
@param enabledMatrixIdsBitMask the bitmask used to list the types of matrix id to process (see MXKTOOLS_XXX__BITWISE).
@return the resulting string.
*/
+ (NSAttributedString*)createLinksInAttributedString:(NSAttributedString*)attributedString forEnabledMatrixIds:(NSInteger)enabledMatrixIdsBitMask;
+ (void)createLinksInMutableAttributedString:(NSMutableAttributedString*)mutableAttributedString forEnabledMatrixIds:(NSInteger)enabledMatrixIdsBitMask;
#pragma mark - HTML processing - blockquote display handling
@@ -398,10 +396,9 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
/**
Removing DTCoreText artifacts used to mark blockquote blocks.
@param attributedString an attributed string.
@return the resulting string.
@param mutableAttributedString a mutable attributed string.
*/
+ (NSAttributedString*)removeMarkedBlockquotesArtifacts:(NSAttributedString*)attributedString;
+ (void)removeMarkedBlockquotesArtifacts:(NSMutableAttributedString*)mutableAttributedString;
/**
Enumerate all sections of the attributed string that refer to an HTML blockquote block.
+29 -42
View File
@@ -939,10 +939,8 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
#pragma mark - HTML processing
+ (NSAttributedString*)removeDTCoreTextArtifacts:(NSAttributedString*)attributedString
+ (void)removeDTCoreTextArtifacts:(NSMutableAttributedString*)mutableAttributedString
{
NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString];
// DTCoreText adds a newline at the end of plain text ( https://github.com/Cocoanetics/DTCoreText/issues/779 )
// or after a blockquote section.
// Trim trailing whitespace and newlines in the string content
@@ -1009,62 +1007,61 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
[mutableAttributedString replaceCharactersInRange:range withAttributedString:attrStringWithImage];
}
}];
return mutableAttributedString;
}
+ (NSAttributedString*)createLinksInAttributedString:(NSAttributedString*)attributedString forEnabledMatrixIds:(NSInteger)enabledMatrixIdsBitMask
+ (void)createLinksInMutableAttributedString:(NSMutableAttributedString*)mutableAttributedString forEnabledMatrixIds:(NSInteger)enabledMatrixIdsBitMask
{
if (!attributedString)
if (!mutableAttributedString)
{
return nil;
return;
}
NSMutableAttributedString *postRenderAttributedString;
// If enabled, make user id clickable
if (enabledMatrixIdsBitMask & MXKTOOLS_USER_IDENTIFIER_BITWISE)
{
[MXKTools createLinksInAttributedString:attributedString matchingRegex:userIdRegex withWorkingAttributedString:&postRenderAttributedString];
[MXKTools createLinksInMutableAttributedString:mutableAttributedString matchingRegex:userIdRegex];
}
// If enabled, make room id clickable
if (enabledMatrixIdsBitMask & MXKTOOLS_ROOM_IDENTIFIER_BITWISE)
{
[MXKTools createLinksInAttributedString:attributedString matchingRegex:roomIdRegex withWorkingAttributedString:&postRenderAttributedString];
[MXKTools createLinksInMutableAttributedString:mutableAttributedString matchingRegex:roomIdRegex];
}
// If enabled, make room alias clickable
if (enabledMatrixIdsBitMask & MXKTOOLS_ROOM_ALIAS_BITWISE)
{
[MXKTools createLinksInAttributedString:attributedString matchingRegex:roomAliasRegex withWorkingAttributedString:&postRenderAttributedString];
[MXKTools createLinksInMutableAttributedString:mutableAttributedString matchingRegex:roomAliasRegex];
}
// If enabled, make event id clickable
if (enabledMatrixIdsBitMask & MXKTOOLS_EVENT_IDENTIFIER_BITWISE)
{
[MXKTools createLinksInAttributedString:attributedString matchingRegex:eventIdRegex withWorkingAttributedString:&postRenderAttributedString];
[MXKTools createLinksInMutableAttributedString:mutableAttributedString matchingRegex:eventIdRegex];
}
// If enabled, make group id clickable
if (enabledMatrixIdsBitMask & MXKTOOLS_GROUP_IDENTIFIER_BITWISE)
{
[MXKTools createLinksInAttributedString:attributedString matchingRegex:groupIdRegex withWorkingAttributedString:&postRenderAttributedString];
[MXKTools createLinksInMutableAttributedString:mutableAttributedString matchingRegex:groupIdRegex];
}
return postRenderAttributedString ? postRenderAttributedString : attributedString;
}
+ (void)createLinksInAttributedString:(NSAttributedString*)attributedString matchingRegex:(NSRegularExpression*)regex withWorkingAttributedString:(NSMutableAttributedString* __autoreleasing *)mutableAttributedString
+ (void)createLinksInMutableAttributedString:(NSMutableAttributedString*)mutableAttributedString matchingRegex:(NSRegularExpression*)regex
{
__block NSArray *linkMatches;
// Enumerate each string matching the regex
[regex enumerateMatchesInString:attributedString.string options:0 range:NSMakeRange(0, attributedString.length) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop) {
[regex enumerateMatchesInString:mutableAttributedString.string
options:0
range:NSMakeRange(0, mutableAttributedString.length)
usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop) {
// Do not create a link if there is already one on the found match
__block BOOL hasAlreadyLink = NO;
[attributedString enumerateAttributesInRange:match.range options:0 usingBlock:^(NSDictionary<NSString *,id> * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
[mutableAttributedString enumerateAttributesInRange:match.range
options:0
usingBlock:^(NSDictionary<NSString *,id> * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
if (attrs[NSLinkAttributeName])
{
@@ -1086,7 +1083,7 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
// Such URL is not valid but web browsers can open them and users C+P them...
// NSDataDetector does not support it but UITextView and UIDataDetectorTypeLink
// detect them when they are displayed. So let the UI create the link at display.
linkMatches = [httpLinksRegex matchesInString:attributedString.string options:0 range:NSMakeRange(0, attributedString.length)];
linkMatches = [httpLinksRegex matchesInString:mutableAttributedString.string options:0 range:NSMakeRange(0, mutableAttributedString.length)];
}
for (NSTextCheckingResult *linkMatch in linkMatches)
@@ -1102,18 +1099,12 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
if (!hasAlreadyLink)
{
// Create the output string only if it is necessary because attributed strings cost CPU
if (!*mutableAttributedString)
{
*mutableAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString];
}
// Make the link clickable
// Caution: We need here to escape the non-ASCII characters (like '#' in room alias)
// to convert the link into a legal URL string.
NSString *link = [attributedString.string substringWithRange:match.range];
NSString *link = [mutableAttributedString.string substringWithRange:match.range];
link = [link stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
[*mutableAttributedString addAttribute:NSLinkAttributeName value:link range:match.range];
[mutableAttributedString addAttribute:NSLinkAttributeName value:link range:match.range];
}
}];
}
@@ -1125,10 +1116,8 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
return [NSString stringWithFormat:@"blockquote {background: #%lX; display: block;}", (unsigned long)[MXKTools rgbValueWithColor:kMXKToolsBlockquoteMarkColor]];
}
+ (NSAttributedString*)removeMarkedBlockquotesArtifacts:(NSAttributedString*)attributedString
+ (void)removeMarkedBlockquotesArtifacts:(NSMutableAttributedString*)mutableAttributedString
{
NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString];
// Enumerate all sections marked thanks to `cssToMarkBlockquotes`
// and apply our own attribute instead.
@@ -1137,10 +1126,10 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
// - or, just define a `NSBackgroundColorAttributeName` attribute
// `DTTextBlocksAttribute` case
[attributedString enumerateAttribute:DTTextBlocksAttribute
inRange:NSMakeRange(0, attributedString.length)
options:0
usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop)
[mutableAttributedString enumerateAttribute:DTTextBlocksAttribute
inRange:NSMakeRange(0, mutableAttributedString.length)
options:0
usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop)
{
if ([value isKindOfClass:NSArray.class])
{
@@ -1161,9 +1150,9 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
NSRange prevRange = NSMakeRange(range.location - 1, 1);
NSRange effectiveRange;
NSParagraphStyle *paragraphStyle = [attributedString attribute:NSParagraphStyleAttributeName
atIndex:prevRange.location
effectiveRange:&effectiveRange];
NSParagraphStyle *paragraphStyle = [mutableAttributedString attribute:NSParagraphStyleAttributeName
atIndex:prevRange.location
effectiveRange:&effectiveRange];
// Check if this is the " " string
if (paragraphStyle && effectiveRange.length == 1 && paragraphStyle.firstLineHeadIndent != 25)
@@ -1197,8 +1186,6 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
[mutableAttributedString addAttribute:kMXKToolsBlockquoteMarkAttribute value:@(YES) range:range];
}
}];
return mutableAttributedString;
}
+ (void)enumerateMarkedBlockquotesInAttributedString:(NSAttributedString*)attributedString usingBlock:(void (^)(NSRange range, BOOL *stop))block
@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -14,19 +13,19 @@
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="5Je-1y-foH" id="hCm-wd-Yup">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="14" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="SBE-PS-i6p">
<rect key="frame" x="58" y="7" width="534" height="30"/>
<rect key="frame" x="58" y="5" width="534" height="34"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" returnKeyType="done"/>
<connections>
<outlet property="delegate" destination="5Je-1y-foH" id="Nie-b5-gB9"/>
</connections>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="584" translatesAutoresizingMaskIntoConstraints="NO" id="DE7-uC-Oz2">
<rect key="frame" x="8" y="11" width="42" height="21"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="584" translatesAutoresizingMaskIntoConstraints="NO" id="DE7-uC-Oz2">
<rect key="frame" x="8" y="11.5" width="42" height="21"/>
<color key="tintColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="10" id="Qzq-Ej-Wdb"/>
@@ -52,6 +51,7 @@
<outlet property="mxkTextFieldLeadingConstraint" destination="h1v-HS-kzp" id="Sx7-JU-SeG"/>
<outlet property="mxkTextFieldTrailingConstraint" destination="7NM-6p-4fc" id="tBw-Gt-9pm"/>
</connections>
<point key="canvasLocation" x="139" y="93"/>
</tableViewCell>
</objects>
</document>
@@ -50,6 +50,7 @@
- (IBAction)textFieldEditingChanged:(id)sender
{
self.mxkButton.enabled = (self.mxkTextField.text.length != 0);
}
@end
@@ -1373,12 +1373,11 @@ NSString* MXKFileSizes_description(MXKFileSizes sizes)
UIPasteboard *pasteboard = MXKPasteboardManager.shared.pasteboard;
if (pasteboard.numberOfItems)
{
for (NSDictionary* dict in pasteboard.items)
for (NSArray<NSString *> *types in [pasteboard pasteboardTypesForItemSet:nil])
{
NSArray* allKeys = dict.allKeys;
for (NSString* key in allKeys)
for (NSString *type in types)
{
NSString* MIMEType = (__bridge_transfer NSString *) UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)key, kUTTagClassMIMEType);
NSString* MIMEType = (__bridge_transfer NSString *) UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)type, kUTTagClassMIMEType);
if ([MIMEType hasPrefix:@"image/"] && [self.delegate respondsToSelector:@selector(roomInputToolbarView:sendImage:)])
{
@@ -135,7 +135,7 @@
// existing user ?
if (user)
{
thumbnailBorderColor = [MXKAccount presenceColor:MXPresenceOffline];
thumbnailBorderColor = [MXKAccount presenceColor:user.presence];
presenceText = [self lastActiveTime];
// Keep last seen range to update it
lastSeenRange = NSMakeRange(self.userLabel.text.length + 2, presenceText.length);
@@ -158,7 +158,7 @@
}
// and the presence text (if any)
if (presenceText && BuildSettings.allowLocalContactPresence)
if (presenceText)
{
NSString* extraText = [NSString stringWithFormat:@"(%@)", presenceText];
self.userLabel.text = [NSString stringWithFormat:@"%@ %@", self.userLabel.text, extraText];