diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index ff81daa7b..56c60a68d 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -9,7 +9,7 @@ on: jobs: integration_tests: name: Integration Tests - runs-on: macos-12 + runs-on: macos-13 concurrency: # Only allow a single run of this workflow on each branch, automatically cancelling older runs. diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 204121490..fd45a8820 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -11,7 +11,7 @@ jobs: if: contains(github.event.pull_request.labels.*.name, 'Trigger-PR-Build') name: Release - runs-on: macos-12 + runs-on: macos-13 concurrency: # Only allow a single run of this workflow on each branch, automatically cancelling older runs. diff --git a/.github/workflows/translations-pr.yml b/.github/workflows/translations-pr.yml index d2363fcc3..9c1bc5b33 100644 --- a/.github/workflows/translations-pr.yml +++ b/.github/workflows/translations-pr.yml @@ -7,7 +7,7 @@ on: jobs: open-translations-pr: - runs-on: macos-12 + runs-on: macos-13 # Skip in forks if: github.repository == 'vector-im/element-x-ios' steps: diff --git a/.github/workflows/ui_tests.yml b/.github/workflows/ui_tests.yml index 8d85f4fd5..0266a3a85 100644 --- a/.github/workflows/ui_tests.yml +++ b/.github/workflows/ui_tests.yml @@ -9,7 +9,7 @@ on: jobs: tests: name: Tests - runs-on: macos-12 + runs-on: macos-13 concurrency: # When running on develop, use the sha to allow all runs of this workflow to run concurrently. diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index eb953e1ca..0410fcf6b 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -11,7 +11,7 @@ on: jobs: tests: name: Tests - runs-on: macos-12 + runs-on: macos-13 concurrency: # When running on develop, use the sha to allow all runs of this workflow to run concurrently. diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 00cfe1bb4..021a59b49 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -138,7 +138,6 @@ "notification_unread_notified_messages_in_room" = "%1$@ in %2$@"; "notification_unread_notified_messages_in_room_and_invitation" = "%1$@ in %2$@ and %3$@"; "preference_rageshake" = "Rageshake to report bug"; -"rageshake_detection_dialog_content" = "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?"; "rageshake_dialog_content" = "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?"; "report_content_explanation" = "This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages."; "report_content_hint" = "Reason for reporting this content"; @@ -177,7 +176,6 @@ "screen_bug_report_include_logs" = "Send logs to help"; "screen_bug_report_include_screenshot" = "Send screenshot"; "screen_bug_report_logs_description" = "To check things work as intended, logs will be sent with your message. These will be private. To just send your message, turn off this setting."; -"screen_bug_report_rash_logs_alert_title" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "screen_change_server_error_invalid_homeserver" = "We couldn't reach this homeserver. Please check that you have entered the homeserver URL correctly. If the URL is correct, contact your homeserver administrator for further help."; "screen_change_server_error_no_sliding_sync_message" = "This server currently doesn’t support sliding sync."; "screen_change_server_form_header" = "Homeserver URL"; @@ -193,7 +191,6 @@ "screen_create_room_public_option_title" = "Public room (anyone)"; "screen_create_room_room_name_label" = "Room name"; "screen_create_room_room_name_placeholder" = "e.g. Product Sprint"; -"screen_create_room_title" = "Create a room"; "screen_create_room_topic_label" = "Topic (optional)"; "screen_create_room_topic_placeholder" = "What is this room about?"; "screen_invites_decline_chat_message" = "Are you sure you want to decline joining %1$@?"; @@ -214,9 +211,13 @@ "screen_onboarding_welcome_subtitle" = "Welcome to the %1$@ Beta. Supercharged, for speed and simplicity."; "screen_onboarding_welcome_title" = "Be in your Element"; "screen_report_content_block_user_hint" = "Check if you want to hide all current and future messages from this user"; +"screen_room_attachment_source_camera" = "Camera"; +"screen_room_attachment_source_camera_photo" = "Take photo"; +"screen_room_attachment_source_camera_video" = "Record a video"; +"screen_room_attachment_source_files" = "Attachment"; +"screen_room_attachment_source_gallery" = "Photo & Video Library"; "screen_room_details_encryption_enabled_subtitle" = "Messages are secured with locks. Only you and the recipients have the unique keys to unlock them."; "screen_room_details_encryption_enabled_title" = "Message encryption enabled"; -"screen_room_details_invite_people_title" = "Invite people"; "screen_room_details_share_room_title" = "Share room"; "screen_room_member_details_block_alert_action" = "Block"; "screen_room_member_details_block_alert_description" = "Blocked users will not be able to send you messages and all message by them will be hidden. You can reverse this action anytime."; @@ -241,10 +242,8 @@ "screen_session_verification_waiting_to_accept_subtitle" = "Accept the request to start the verification process in your other session to continue."; "screen_session_verification_waiting_to_accept_title" = "Waiting to accept request"; "screen_signout_confirmation_dialog_content" = "Are you sure you want to sign out?"; -"screen_signout_confirmation_dialog_submit" = "Sign out"; "screen_signout_confirmation_dialog_title" = "Sign out"; "screen_signout_in_progress_dialog_content" = "Signing out…"; -"screen_signout_preference_item" = "Sign out"; "screen_start_chat_error_starting_chat" = "An error occurred when trying to start a chat"; "screen_start_chat_unknown_profile" = "We can’t validate this user’s Matrix ID. The invite might not be received."; "session_verification_banner_message" = "Looks like you’re using a new device. Verify it’s you to access your encrypted messages."; @@ -312,11 +311,14 @@ "dialog_title_error" = "Error"; "dialog_title_success" = "Success"; "notification_room_action_quick_reply" = "Quick reply"; +"rageshake_detection_dialog_content" = "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?"; "screen_analytics_settings_help_us_improve" = "Help us identify issues and improve %1$@ by sharing anonymous usage data."; "screen_analytics_settings_read_terms" = "You can read all our terms %1$@."; "screen_analytics_settings_read_terms_content_link" = "here"; +"screen_bug_report_rash_logs_alert_title" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "screen_change_server_submit" = "Continue"; "screen_change_server_title" = "Select your server"; +"screen_create_room_title" = "Create a room"; "screen_dm_details_block_alert_action" = "Block"; "screen_dm_details_block_alert_description" = "Blocked users will not be able to send you messages and all message by them will be hidden. You can reverse this action anytime."; "screen_dm_details_block_user" = "Block user"; @@ -328,9 +330,13 @@ "screen_login_submit" = "Continue"; "screen_login_username_hint" = "Username"; "screen_report_content_block_user" = "Block user"; +"screen_room_details_invite_people_title" = "Invite people"; "screen_room_details_leave_room_title" = "Leave room"; "screen_room_details_people_title" = "People"; "screen_room_details_security_title" = "Security"; "screen_room_details_topic_title" = "Topic"; +"screen_room_error_failed_processing_media" = "Failed processing media to upload, please try again."; "screen_session_verification_cancelled_title" = "Verification cancelled"; "screen_session_verification_positive_button_ready" = "Start"; +"screen_signout_confirmation_dialog_submit" = "Sign out"; +"screen_signout_preference_item" = "Sign out"; diff --git a/ElementX/Resources/Localizations/en.lproj/Untranslated.strings b/ElementX/Resources/Localizations/en.lproj/Untranslated.strings index 6bfab1e5d..7cacc3f86 100644 --- a/ElementX/Resources/Localizations/en.lproj/Untranslated.strings +++ b/ElementX/Resources/Localizations/en.lproj/Untranslated.strings @@ -15,11 +15,3 @@ "soft_logout_clear_data_submit" = "Clear all data"; "soft_logout_clear_data_dialog_title" = "Clear data"; "soft_logout_clear_data_dialog_content" = "Clear all data currently stored on this device?\nSign in again to access your account data and messages."; - -// MARK: - Media upload - -"media_upload_camera_picker" = "Camera"; - -"media_upload_photo_and_video_picker" = "Photo & Video Library"; - -"media_upload_document_picker" = "Document"; diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 6f7b8a689..7750f14ab 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -339,7 +339,7 @@ class AppCoordinator: AppCoordinatorProtocol { // MARK: Toasts and loading indicators - static let loadingIndicatorIdentifier = "AppCoordinatorLoading" + private static let loadingIndicatorIdentifier = "AppCoordinatorLoading" private func showLoadingIndicator() { ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 91e1abf7f..95ae56016 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -29,7 +29,6 @@ final class AppSettings { case shouldCollapseRoomStateEvents case startChatFlowEnabled case startChatUserSuggestionsEnabled - case mediaUploadingFlowEnabled case invitesFlowEnabled } @@ -166,12 +165,7 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.startChatUserSuggestionsEnabled, defaultValue: false, storageType: .volatile) var startChatUserSuggestionsEnabled - - // MARK: Media Uploading - - @UserPreference(key: UserDefaultsKeys.mediaUploadingFlowEnabled, defaultValue: false, storageType: .volatile) - var mediaUploadingFlowEnabled - + // MARK: Invites @UserPreference(key: UserDefaultsKeys.invitesFlowEnabled, defaultValue: false, storageType: .userDefaults(store)) diff --git a/ElementX/Sources/Generated/Strings+Untranslated.swift b/ElementX/Sources/Generated/Strings+Untranslated.swift index ec4a4eca2..3fbefde12 100644 --- a/ElementX/Sources/Generated/Strings+Untranslated.swift +++ b/ElementX/Sources/Generated/Strings+Untranslated.swift @@ -10,12 +10,6 @@ import Foundation // swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length // swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces public enum UntranslatedL10n { - /// Camera - public static var mediaUploadCameraPicker: String { return UntranslatedL10n.tr("Untranslated", "media_upload_camera_picker") } - /// Document - public static var mediaUploadDocumentPicker: String { return UntranslatedL10n.tr("Untranslated", "media_upload_document_picker") } - /// Photo & Video Library - public static var mediaUploadPhotoAndVideoPicker: String { return UntranslatedL10n.tr("Untranslated", "media_upload_photo_and_video_picker") } /// Clear all data currently stored on this device? /// Sign in again to access your account data and messages. public static var softLogoutClearDataDialogContent: String { return UntranslatedL10n.tr("Untranslated", "soft_logout_clear_data_dialog_content") } diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index ba1d4324c..88c5b8663 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -556,6 +556,16 @@ public enum L10n { public static var screenReportContentBlockUser: String { return L10n.tr("Localizable", "screen_report_content_block_user") } /// Check if you want to hide all current and future messages from this user public static var screenReportContentBlockUserHint: String { return L10n.tr("Localizable", "screen_report_content_block_user_hint") } + /// Camera + public static var screenRoomAttachmentSourceCamera: String { return L10n.tr("Localizable", "screen_room_attachment_source_camera") } + /// Take photo + public static var screenRoomAttachmentSourceCameraPhoto: String { return L10n.tr("Localizable", "screen_room_attachment_source_camera_photo") } + /// Record a video + public static var screenRoomAttachmentSourceCameraVideo: String { return L10n.tr("Localizable", "screen_room_attachment_source_camera_video") } + /// Attachment + public static var screenRoomAttachmentSourceFiles: String { return L10n.tr("Localizable", "screen_room_attachment_source_files") } + /// Photo & Video Library + public static var screenRoomAttachmentSourceGallery: String { return L10n.tr("Localizable", "screen_room_attachment_source_gallery") } /// Messages are secured with locks. Only you and the recipients have the unique keys to unlock them. public static var screenRoomDetailsEncryptionEnabledSubtitle: String { return L10n.tr("Localizable", "screen_room_details_encryption_enabled_subtitle") } /// Message encryption enabled @@ -572,6 +582,8 @@ public enum L10n { public static var screenRoomDetailsShareRoomTitle: String { return L10n.tr("Localizable", "screen_room_details_share_room_title") } /// Topic public static var screenRoomDetailsTopicTitle: String { return L10n.tr("Localizable", "screen_room_details_topic_title") } + /// Failed processing media to upload, please try again. + public static var screenRoomErrorFailedProcessingMedia: String { return L10n.tr("Localizable", "screen_room_error_failed_processing_media") } /// Block public static var screenRoomMemberDetailsBlockAlertAction: String { return L10n.tr("Localizable", "screen_room_member_details_block_alert_action") } /// Blocked users will not be able to send you messages and all message by them will be hidden. You can reverse this action anytime. diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 8f0be43fd..3200f523a 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -595,6 +595,69 @@ class RoomProxyMock: RoomProxyProtocol { return sendImageUrlThumbnailURLImageInfoReturnValue } } + //MARK: - sendVideo + + var sendVideoUrlThumbnailURLVideoInfoCallsCount = 0 + var sendVideoUrlThumbnailURLVideoInfoCalled: Bool { + return sendVideoUrlThumbnailURLVideoInfoCallsCount > 0 + } + var sendVideoUrlThumbnailURLVideoInfoReceivedArguments: (url: URL, thumbnailURL: URL, videoInfo: VideoInfo)? + var sendVideoUrlThumbnailURLVideoInfoReceivedInvocations: [(url: URL, thumbnailURL: URL, videoInfo: VideoInfo)] = [] + var sendVideoUrlThumbnailURLVideoInfoReturnValue: Result! + var sendVideoUrlThumbnailURLVideoInfoClosure: ((URL, URL, VideoInfo) async -> Result)? + + func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo) async -> Result { + sendVideoUrlThumbnailURLVideoInfoCallsCount += 1 + sendVideoUrlThumbnailURLVideoInfoReceivedArguments = (url: url, thumbnailURL: thumbnailURL, videoInfo: videoInfo) + sendVideoUrlThumbnailURLVideoInfoReceivedInvocations.append((url: url, thumbnailURL: thumbnailURL, videoInfo: videoInfo)) + if let sendVideoUrlThumbnailURLVideoInfoClosure = sendVideoUrlThumbnailURLVideoInfoClosure { + return await sendVideoUrlThumbnailURLVideoInfoClosure(url, thumbnailURL, videoInfo) + } else { + return sendVideoUrlThumbnailURLVideoInfoReturnValue + } + } + //MARK: - sendAudio + + var sendAudioUrlAudioInfoCallsCount = 0 + var sendAudioUrlAudioInfoCalled: Bool { + return sendAudioUrlAudioInfoCallsCount > 0 + } + var sendAudioUrlAudioInfoReceivedArguments: (url: URL, audioInfo: AudioInfo)? + var sendAudioUrlAudioInfoReceivedInvocations: [(url: URL, audioInfo: AudioInfo)] = [] + var sendAudioUrlAudioInfoReturnValue: Result! + var sendAudioUrlAudioInfoClosure: ((URL, AudioInfo) async -> Result)? + + func sendAudio(url: URL, audioInfo: AudioInfo) async -> Result { + sendAudioUrlAudioInfoCallsCount += 1 + sendAudioUrlAudioInfoReceivedArguments = (url: url, audioInfo: audioInfo) + sendAudioUrlAudioInfoReceivedInvocations.append((url: url, audioInfo: audioInfo)) + if let sendAudioUrlAudioInfoClosure = sendAudioUrlAudioInfoClosure { + return await sendAudioUrlAudioInfoClosure(url, audioInfo) + } else { + return sendAudioUrlAudioInfoReturnValue + } + } + //MARK: - sendFile + + var sendFileUrlFileInfoCallsCount = 0 + var sendFileUrlFileInfoCalled: Bool { + return sendFileUrlFileInfoCallsCount > 0 + } + var sendFileUrlFileInfoReceivedArguments: (url: URL, fileInfo: FileInfo)? + var sendFileUrlFileInfoReceivedInvocations: [(url: URL, fileInfo: FileInfo)] = [] + var sendFileUrlFileInfoReturnValue: Result! + var sendFileUrlFileInfoClosure: ((URL, FileInfo) async -> Result)? + + func sendFile(url: URL, fileInfo: FileInfo) async -> Result { + sendFileUrlFileInfoCallsCount += 1 + sendFileUrlFileInfoReceivedArguments = (url: url, fileInfo: fileInfo) + sendFileUrlFileInfoReceivedInvocations.append((url: url, fileInfo: fileInfo)) + if let sendFileUrlFileInfoClosure = sendFileUrlFileInfoClosure { + return await sendFileUrlFileInfoClosure(url, fileInfo) + } else { + return sendFileUrlFileInfoReturnValue + } + } //MARK: - editMessage var editMessageOriginalCallsCount = 0 diff --git a/ElementX/Sources/Other/AccessibilityIdentifiers.swift b/ElementX/Sources/Other/AccessibilityIdentifiers.swift index 39f472efd..ed9b49fc4 100644 --- a/ElementX/Sources/Other/AccessibilityIdentifiers.swift +++ b/ElementX/Sources/Other/AccessibilityIdentifiers.swift @@ -89,6 +89,7 @@ struct A11yIdentifiers { struct RoomScreen { let name = "room-name" let avatar = "room-avatar" + let attachmentPicker = "room-attachment_picker" } struct RoomDetailsScreen { diff --git a/ElementX/Sources/Other/Extensions/NSItemProvider.swift b/ElementX/Sources/Other/Extensions/NSItemProvider.swift new file mode 100644 index 000000000..3de31a1cd --- /dev/null +++ b/ElementX/Sources/Other/Extensions/NSItemProvider.swift @@ -0,0 +1,26 @@ +// +// Copyright 2023 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +extension NSItemProvider { + var isSupportedForPasteOrDrop: Bool { + !registeredContentTypes + .compactMap(\.preferredMIMEType) + .filter { $0.hasPrefix("image/") || $0.hasPrefix("video/") || $0.hasPrefix("application/") } + .isEmpty + } +} diff --git a/ElementX/Sources/Screens/Authentication/AuthenticationCoordinator.swift b/ElementX/Sources/Screens/Authentication/AuthenticationCoordinator.swift index c1a083d15..42d6ebaa1 100644 --- a/ElementX/Sources/Screens/Authentication/AuthenticationCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/AuthenticationCoordinator.swift @@ -127,7 +127,7 @@ class AuthenticationCoordinator: CoordinatorProtocol { navigationStackCoordinator.push(coordinator) } - static let loadingIndicatorIdentifier = "AuthenticationCoordinatorLoading" + private static let loadingIndicatorIdentifier = "AuthenticationCoordinatorLoading" private func startLoading() { ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift index cae2a931c..e266164c8 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift @@ -89,7 +89,7 @@ final class LoginScreenCoordinator: CoordinatorProtocol { // MARK: - Private - static let loadingIndicatorIdentifier = "LoginCoordinatorLoading" + private static let loadingIndicatorIdentifier = "LoginCoordinatorLoading" private func startLoading(isInteractionBlocking: Bool) { if isInteractionBlocking { diff --git a/ElementX/Sources/Screens/Authentication/SoftLogoutScreen/SoftLogoutScreenCoordinator.swift b/ElementX/Sources/Screens/Authentication/SoftLogoutScreen/SoftLogoutScreenCoordinator.swift index 668fafde0..d4b1c45a1 100644 --- a/ElementX/Sources/Screens/Authentication/SoftLogoutScreen/SoftLogoutScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/SoftLogoutScreen/SoftLogoutScreenCoordinator.swift @@ -95,7 +95,7 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol { // MARK: - Private - static let loadingIndicatorIdentifier = "SoftLogoutLoading" + private static let loadingIndicatorIdentifier = "SoftLogoutLoading" /// Show an activity indicator whilst loading. @MainActor private func startLoading() { diff --git a/ElementX/Sources/Screens/BugReportScreen/BugReportScreenCoordinator.swift b/ElementX/Sources/Screens/BugReportScreen/BugReportScreenCoordinator.swift index cd3d50c6e..51fbe1cc0 100644 --- a/ElementX/Sources/Screens/BugReportScreen/BugReportScreenCoordinator.swift +++ b/ElementX/Sources/Screens/BugReportScreen/BugReportScreenCoordinator.swift @@ -83,7 +83,7 @@ final class BugReportScreenCoordinator: CoordinatorProtocol { // MARK: - Private - static let loadingIndicatorIdentifier = "BugReportLoading" + private static let loadingIndicatorIdentifier = "BugReportLoading" private func startLoading(label: String = L10n.commonLoading, progressPublisher: ProgressPublisher) { parameters.userIndicatorController?.submitIndicator( diff --git a/ElementX/Sources/Screens/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index 9058bab12..17dcc8e82 100644 --- a/ElementX/Sources/Screens/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -26,7 +26,6 @@ struct DeveloperOptionsScreenViewStateBindings { var shouldCollapseRoomStateEvents: Bool var startChatFlowEnabled: Bool var startChatUserSuggestionsEnabled: Bool - var mediaUploadFlowEnabled: Bool var invitesFlowEnabled: Bool } @@ -34,6 +33,5 @@ enum DeveloperOptionsScreenViewAction { case changedShouldCollapseRoomStateEvents case changedStartChatFlowEnabled case changedStartChatUserSuggestionsEnabled - case changedMediaUploadFlowEnabled case changedInvitesFlowEnabled } diff --git a/ElementX/Sources/Screens/DeveloperOptionsScreen/DeveloperOptionsScreenViewModel.swift b/ElementX/Sources/Screens/DeveloperOptionsScreen/DeveloperOptionsScreenViewModel.swift index 794971dcb..a34683a20 100644 --- a/ElementX/Sources/Screens/DeveloperOptionsScreen/DeveloperOptionsScreenViewModel.swift +++ b/ElementX/Sources/Screens/DeveloperOptionsScreen/DeveloperOptionsScreenViewModel.swift @@ -25,7 +25,6 @@ class DeveloperOptionsScreenViewModel: DeveloperOptionsScreenViewModelType, Deve let bindings = DeveloperOptionsScreenViewStateBindings(shouldCollapseRoomStateEvents: ServiceLocator.shared.settings.shouldCollapseRoomStateEvents, startChatFlowEnabled: ServiceLocator.shared.settings.startChatFlowEnabled, startChatUserSuggestionsEnabled: ServiceLocator.shared.settings.startChatUserSuggestionsEnabled, - mediaUploadFlowEnabled: ServiceLocator.shared.settings.mediaUploadingFlowEnabled, invitesFlowEnabled: ServiceLocator.shared.settings.invitesFlowEnabled) let state = DeveloperOptionsScreenViewState(bindings: bindings) @@ -44,8 +43,6 @@ class DeveloperOptionsScreenViewModel: DeveloperOptionsScreenViewModelType, Deve ServiceLocator.shared.settings.startChatFlowEnabled = state.bindings.startChatFlowEnabled case .changedStartChatUserSuggestionsEnabled: ServiceLocator.shared.settings.startChatUserSuggestionsEnabled = state.bindings.startChatUserSuggestionsEnabled - case .changedMediaUploadFlowEnabled: - ServiceLocator.shared.settings.mediaUploadingFlowEnabled = state.bindings.mediaUploadFlowEnabled case .changedInvitesFlowEnabled: ServiceLocator.shared.settings.invitesFlowEnabled = state.bindings.invitesFlowEnabled } diff --git a/ElementX/Sources/Screens/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index 7e2741bf5..accdc5c28 100644 --- a/ElementX/Sources/Screens/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -43,14 +43,7 @@ struct DeveloperOptionsScreen: View { .onChange(of: context.startChatUserSuggestionsEnabled) { _ in context.send(viewAction: .changedStartChatUserSuggestionsEnabled) } - - Toggle(isOn: $context.mediaUploadFlowEnabled) { - Text("Show Media Uploading flow") - } - .onChange(of: context.mediaUploadFlowEnabled) { _ in - context.send(viewAction: .changedMediaUploadFlowEnabled) - } - + Toggle(isOn: $context.invitesFlowEnabled) { Text("Show Invites flow") } diff --git a/ElementX/Sources/Screens/MediaPickerPreviewScreen/MediaPickerPreviewScreenCoordinator.swift b/ElementX/Sources/Screens/MediaPickerPreviewScreen/MediaPickerPreviewScreenCoordinator.swift deleted file mode 100644 index d28d489d6..000000000 --- a/ElementX/Sources/Screens/MediaPickerPreviewScreen/MediaPickerPreviewScreenCoordinator.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// Copyright 2022 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import SwiftUI - -struct MediaPickerPreviewScreenCoordinatorParameters { - let url: URL - let title: String? -} - -enum MediaPickerPreviewScreenCoordinatorAction { - case send - case cancel -} - -final class MediaPickerPreviewScreenCoordinator: CoordinatorProtocol { - private let parameters: MediaPickerPreviewScreenCoordinatorParameters - - private var viewModel: MediaPickerPreviewScreenViewModelProtocol - private let callback: (MediaPickerPreviewScreenCoordinatorAction) -> Void - - init(parameters: MediaPickerPreviewScreenCoordinatorParameters, callback: @escaping (MediaPickerPreviewScreenCoordinatorAction) -> Void) { - self.parameters = parameters - self.callback = callback - - viewModel = MediaPickerPreviewScreenViewModel(url: parameters.url, title: parameters.title) - } - - func start() { - viewModel.callback = { [weak self] action in - switch action { - case .send: - self?.callback(.send) - case .cancel: - self?.callback(.cancel) - } - } - } - - func toPresentable() -> AnyView { - AnyView(MediaPickerPreviewScreen(context: viewModel.context)) - } -} diff --git a/ElementX/Sources/Screens/MediaPickerPreviewScreen/MediaPickerPreviewScreenViewModel.swift b/ElementX/Sources/Screens/MediaPickerPreviewScreen/MediaPickerPreviewScreenViewModel.swift deleted file mode 100644 index fd7f57d1d..000000000 --- a/ElementX/Sources/Screens/MediaPickerPreviewScreen/MediaPickerPreviewScreenViewModel.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright 2022 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import SwiftUI - -typealias MediaPickerPreviewScreenViewModelType = StateStoreViewModel - -class MediaPickerPreviewScreenViewModel: MediaPickerPreviewScreenViewModelType, MediaPickerPreviewScreenViewModelProtocol { - var callback: ((MediaPickerPreviewScreenViewModelAction) -> Void)? - - init(url: URL, title: String?) { - super.init(initialViewState: MediaPickerPreviewScreenViewState(url: url, title: title)) - } - - override func process(viewAction: MediaPickerPreviewScreenViewAction) { - switch viewAction { - case .send: - callback?(.send) - case .cancel: - callback?(.cancel) - } - } -} diff --git a/ElementX/Sources/Screens/MediaPickerScreen/CameraPicker.swift b/ElementX/Sources/Screens/MediaPickerScreen/CameraPicker.swift index 27fa54f86..05f351555 100644 --- a/ElementX/Sources/Screens/MediaPickerScreen/CameraPicker.swift +++ b/ElementX/Sources/Screens/MediaPickerScreen/CameraPicker.swift @@ -70,7 +70,7 @@ struct CameraPicker: UIViewControllerRepresentable { return } - let fileName = "\(Date.now.formatted(date: .abbreviated, time: .shortened)).jpg" + let fileName = "\(Date.now.formatted(.iso8601.dateSeparator(.omitted).timeSeparator(.omitted))).jpg" do { let url = try FileManager.default.writeDataToTemporaryDirectory(data: jpegData, fileName: fileName) diff --git a/ElementX/Sources/Screens/MediaPickerScreen/MediaPickerScreenCoordinator.swift b/ElementX/Sources/Screens/MediaPickerScreen/MediaPickerScreenCoordinator.swift index 21c3ad970..950f1d034 100644 --- a/ElementX/Sources/Screens/MediaPickerScreen/MediaPickerScreenCoordinator.swift +++ b/ElementX/Sources/Screens/MediaPickerScreen/MediaPickerScreenCoordinator.swift @@ -25,14 +25,15 @@ enum MediaPickerScreenSource { enum MediaPickerScreenCoordinatorAction { case selectMediaAtURL(URL) case cancel - case error(Error?) } class MediaPickerScreenCoordinator: CoordinatorProtocol { + private weak var userIndicatorController: UserIndicatorControllerProtocol? private let source: MediaPickerScreenSource private let callback: ((MediaPickerScreenCoordinatorAction) -> Void)? - init(source: MediaPickerScreenSource, callback: @escaping (MediaPickerScreenCoordinatorAction) -> Void) { + init(userIndicatorController: UserIndicatorControllerProtocol, source: MediaPickerScreenSource, callback: @escaping (MediaPickerScreenCoordinatorAction) -> Void) { + self.userIndicatorController = userIndicatorController self.source = source self.callback = callback } @@ -52,7 +53,8 @@ class MediaPickerScreenCoordinator: CoordinatorProtocol { case .cancel: self?.callback?(.cancel) case .error(let error): - self?.callback?(.error(error)) + MXLog.error("Failed selecting media from the photo library with error: \(error)") + self?.showError() case .selectFile(let url): self?.callback?(.selectMediaAtURL(url)) } @@ -65,7 +67,8 @@ class MediaPickerScreenCoordinator: CoordinatorProtocol { case .cancel: self.callback?(.cancel) case .error(let error): - self.callback?(.error(error)) + MXLog.error("Failed selecting media from the document picker with error: \(error)") + self.showError() case .selectFile(let url): self.callback?(.selectMediaAtURL(url)) } @@ -79,11 +82,16 @@ class MediaPickerScreenCoordinator: CoordinatorProtocol { case .cancel: self?.callback?(.cancel) case .error(let error): - self?.callback?(.error(error)) + MXLog.error("Failed selecting media from the camera picker with error: \(error)") + self?.showError() case .selectFile(let url): self?.callback?(.selectMediaAtURL(url)) } } .background(.black, ignoresSafeAreaEdges: .bottom) } + + private func showError() { + userIndicatorController?.submitIndicator(UserIndicator(title: L10n.screenMediaPickerErrorFailedSelection)) + } } diff --git a/ElementX/Sources/Screens/MediaPickerScreen/PhotoLibraryPicker.swift b/ElementX/Sources/Screens/MediaPickerScreen/PhotoLibraryPicker.swift index b069de75d..9ff15e037 100644 --- a/ElementX/Sources/Screens/MediaPickerScreen/PhotoLibraryPicker.swift +++ b/ElementX/Sources/Screens/MediaPickerScreen/PhotoLibraryPicker.swift @@ -20,7 +20,12 @@ import SwiftUI enum PhotoLibraryPickerAction { case selectFile(URL) case cancel - case error(Error?) + case error(PhotoLibraryPickerError) +} + +enum PhotoLibraryPickerError: Error { + case failedLoadingFileRepresentation(Error?) + case failedCopyingFile } struct PhotoLibraryPicker: UIViewControllerRepresentable { @@ -63,7 +68,9 @@ struct PhotoLibraryPicker: UIViewControllerRepresentable { provider.loadFileRepresentation(forTypeIdentifier: "public.item") { [weak self] url, error in guard let url else { - self?.photoLibraryPicker.callback(.error(error)) + Task { @MainActor in + self?.photoLibraryPicker.callback(.error(.failedLoadingFileRepresentation(error))) + } return } @@ -76,7 +83,9 @@ struct PhotoLibraryPicker: UIViewControllerRepresentable { self?.photoLibraryPicker.callback(.selectFile(newURL)) } } catch { - self?.photoLibraryPicker.callback(.error(error)) + Task { @MainActor in + self?.photoLibraryPicker.callback(.error(.failedCopyingFile)) + } } } } diff --git a/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenCoordinator.swift b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenCoordinator.swift new file mode 100644 index 000000000..dbef11d8a --- /dev/null +++ b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenCoordinator.swift @@ -0,0 +1,57 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +struct MediaUploadPreviewScreenCoordinatorParameters { + weak var userIndicatorController: UserIndicatorControllerProtocol? + let roomProxy: RoomProxyProtocol + let mediaUploadingPreprocessor: MediaUploadingPreprocessor + let title: String? + let url: URL +} + +enum MediaUploadPreviewScreenCoordinatorAction { + case dismiss +} + +final class MediaUploadPreviewScreenCoordinator: CoordinatorProtocol { + private var viewModel: MediaUploadPreviewScreenViewModelProtocol + private let callback: (MediaUploadPreviewScreenCoordinatorAction) -> Void + + init(parameters: MediaUploadPreviewScreenCoordinatorParameters, callback: @escaping (MediaUploadPreviewScreenCoordinatorAction) -> Void) { + self.callback = callback + + viewModel = MediaUploadPreviewScreenViewModel(userIndicatorController: parameters.userIndicatorController, + roomProxy: parameters.roomProxy, + mediaUploadingPreprocessor: parameters.mediaUploadingPreprocessor, + title: parameters.title, + url: parameters.url) + } + + func start() { + viewModel.callback = { [weak self] action in + switch action { + case .dismiss: + self?.callback(.dismiss) + } + } + } + + func toPresentable() -> AnyView { + AnyView(MediaUploadPreviewScreen(context: viewModel.context)) + } +} diff --git a/ElementX/Sources/Screens/MediaPickerPreviewScreen/MediaPickerPreviewScreenModels.swift b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenModels.swift similarity index 79% rename from ElementX/Sources/Screens/MediaPickerPreviewScreen/MediaPickerPreviewScreenModels.swift rename to ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenModels.swift index 806dcbe92..974dfb574 100644 --- a/ElementX/Sources/Screens/MediaPickerPreviewScreen/MediaPickerPreviewScreenModels.swift +++ b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenModels.swift @@ -16,17 +16,16 @@ import Foundation -enum MediaPickerPreviewScreenViewModelAction { - case send - case cancel +enum MediaUploadPreviewScreenViewModelAction { + case dismiss } -struct MediaPickerPreviewScreenViewState: BindableState { +struct MediaUploadPreviewScreenViewState: BindableState { let url: URL let title: String? } -enum MediaPickerPreviewScreenViewAction { +enum MediaUploadPreviewScreenViewAction { case send case cancel } diff --git a/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModel.swift b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModel.swift new file mode 100644 index 000000000..392eed690 --- /dev/null +++ b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModel.swift @@ -0,0 +1,105 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +typealias MediaUploadPreviewScreenViewModelType = StateStoreViewModel + +class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType, MediaUploadPreviewScreenViewModelProtocol { + private weak var userIndicatorController: UserIndicatorControllerProtocol? + private let roomProxy: RoomProxyProtocol + private let mediaUploadingPreprocessor: MediaUploadingPreprocessor + private let url: URL + + var callback: ((MediaUploadPreviewScreenViewModelAction) -> Void)? + + init(userIndicatorController: UserIndicatorControllerProtocol?, + roomProxy: RoomProxyProtocol, + mediaUploadingPreprocessor: MediaUploadingPreprocessor, + title: String?, + url: URL) { + self.userIndicatorController = userIndicatorController + self.roomProxy = roomProxy + self.mediaUploadingPreprocessor = mediaUploadingPreprocessor + self.url = url + + super.init(initialViewState: MediaUploadPreviewScreenViewState(url: url, title: title)) + } + + override func process(viewAction: MediaUploadPreviewScreenViewAction) { + switch viewAction { + case .send: + Task { + startLoading() + defer { + stopLoading() + } + + switch await mediaUploadingPreprocessor.processMedia(at: url) { + case .success(let mediaInfo): + switch await sendAttachment(mediaInfo: mediaInfo) { + case .success: + callback?(.dismiss) + case .failure(let error): + MXLog.error("Failed sending attachment with error: \(error)") + showError(label: L10n.screenMediaUploadPreviewErrorFailedSending) + } + + case .failure(let error): + MXLog.error("Failed processing media to upload with error: \(error)") + showError(label: L10n.screenMediaUploadPreviewErrorFailedProcessing) + } + } + + case .cancel: + callback?(.dismiss) + } + } + + // MARK: - Private + + private func sendAttachment(mediaInfo: MediaInfo) async -> Result { + switch mediaInfo { + case let .image(imageURL, thumbnailURL, imageInfo): + return await roomProxy.sendImage(url: imageURL, thumbnailURL: thumbnailURL, imageInfo: imageInfo) + case let .video(videoURL, thumbnailURL, videoInfo): + return await roomProxy.sendVideo(url: videoURL, thumbnailURL: thumbnailURL, videoInfo: videoInfo) + case let .audio(audioURL, audioInfo): + return await roomProxy.sendAudio(url: audioURL, audioInfo: audioInfo) + case let .file(fileURL, fileInfo): + return await roomProxy.sendFile(url: fileURL, fileInfo: fileInfo) + } + } + + private static let loadingIndicatorIdentifier = "MediaUploadPreviewLoading" + + private func startLoading() { + userIndicatorController?.submitIndicator( + UserIndicator(id: Self.loadingIndicatorIdentifier, + type: .modal, + title: L10n.commonLoading, + persistent: true) + ) + } + + private func stopLoading() { + userIndicatorController?.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + } + + private func showError(label: String) { + userIndicatorController?.submitIndicator(UserIndicator(title: label)) + } +} diff --git a/ElementX/Sources/Screens/MediaPickerPreviewScreen/MediaPickerPreviewScreenViewModelProtocol.swift b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModelProtocol.swift similarity index 78% rename from ElementX/Sources/Screens/MediaPickerPreviewScreen/MediaPickerPreviewScreenViewModelProtocol.swift rename to ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModelProtocol.swift index 520d6da79..a4f034420 100644 --- a/ElementX/Sources/Screens/MediaPickerPreviewScreen/MediaPickerPreviewScreenViewModelProtocol.swift +++ b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModelProtocol.swift @@ -17,7 +17,7 @@ import Foundation @MainActor -protocol MediaPickerPreviewScreenViewModelProtocol { - var callback: ((MediaPickerPreviewScreenViewModelAction) -> Void)? { get set } - var context: MediaPickerPreviewScreenViewModelType.Context { get } +protocol MediaUploadPreviewScreenViewModelProtocol { + var callback: ((MediaUploadPreviewScreenViewModelAction) -> Void)? { get set } + var context: MediaUploadPreviewScreenViewModelType.Context { get } } diff --git a/ElementX/Sources/Screens/MediaPickerPreviewScreen/View/MediaPickerPreviewScreen.swift b/ElementX/Sources/Screens/MediaUploadPreviewScreen/View/MediaUploadPreviewScreen.swift similarity index 73% rename from ElementX/Sources/Screens/MediaPickerPreviewScreen/View/MediaPickerPreviewScreen.swift rename to ElementX/Sources/Screens/MediaUploadPreviewScreen/View/MediaUploadPreviewScreen.swift index d0dd52097..28bea67e1 100644 --- a/ElementX/Sources/Screens/MediaPickerPreviewScreen/View/MediaPickerPreviewScreen.swift +++ b/ElementX/Sources/Screens/MediaUploadPreviewScreen/View/MediaUploadPreviewScreen.swift @@ -17,18 +17,17 @@ import QuickLook import SwiftUI -struct MediaPickerPreviewScreen: View { - @ObservedObject var context: MediaPickerPreviewScreenViewModel.Context +struct MediaUploadPreviewScreen: View { + @ObservedObject var context: MediaUploadPreviewScreenViewModel.Context var body: some View { - NavigationStack { - PreviewView(context: context, - fileURL: context.viewState.url, - title: context.viewState.title) - .id(UUID()) - .ignoresSafeArea(edges: .bottom) - .toolbar { toolbar } - } + PreviewView(context: context, + fileURL: context.viewState.url, + title: context.viewState.title) + .id(UUID()) + .ignoresSafeArea(edges: .bottom) + .toolbar { toolbar } + .interactiveDismissDisabled() } @ToolbarContentBuilder @@ -47,7 +46,7 @@ struct MediaPickerPreviewScreen: View { } private struct PreviewView: UIViewControllerRepresentable { - let context: MediaPickerPreviewScreenViewModel.Context + let context: MediaUploadPreviewScreenViewModel.Context let fileURL: URL let title: String? @@ -102,9 +101,13 @@ private class PreviewItem: NSObject, QLPreviewItem { // MARK: - Previews -struct MediaPickerPreviewScreen_Previews: PreviewProvider { - static let viewModel = MediaPickerPreviewScreenViewModel(url: URL.picturesDirectory, title: nil) +struct MediaUploadPreviewScreen_Previews: PreviewProvider { + static let viewModel = MediaUploadPreviewScreenViewModel(userIndicatorController: MockUserIndicatorController(), + roomProxy: RoomProxyMock(), + mediaUploadingPreprocessor: MediaUploadingPreprocessor(), + title: nil, + url: URL.picturesDirectory) static var previews: some View { - MediaPickerPreviewScreen(context: viewModel.context) + MediaUploadPreviewScreen(context: viewModel.context) } } diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift index 02c25bb75..74bf97240 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift @@ -68,6 +68,8 @@ final class RoomScreenCoordinator: CoordinatorProtocol { self.displayMediaPickerWithSource(.photoLibrary) case .displayDocumentPicker: self.displayMediaPickerWithSource(.documents) + case .displayMediaUploadPreviewScreen(let url): + self.displayMediaUploadPreviewScreenForFile(at: url) } } } @@ -84,44 +86,43 @@ final class RoomScreenCoordinator: CoordinatorProtocol { // MARK: - Private private func displayMediaPickerWithSource(_ source: MediaPickerScreenSource) { - let mediaPickerCoordinator = MediaPickerScreenCoordinator(source: source) { [weak self] action in + let stackCoordinator = NavigationStackCoordinator() + let userIndicatorController = UserIndicatorController(rootCoordinator: stackCoordinator) + + let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: userIndicatorController, source: source) { [weak self] action in switch action { case .cancel: self?.navigationStackCoordinator.setSheetCoordinator(nil) - case .error: - break case .selectMediaAtURL(let url): - let mediaPickerPreviewScreenCoordinator = MediaPickerPreviewScreenCoordinator(parameters: .init(url: url, title: url.lastPathComponent)) { action in - switch action { - case .send: - Task { - let preprocessor = MediaUploadingPreprocessor() - switch await preprocessor.processMedia(at: url) { - case .success(let mediaInfo): - MXLog.info(mediaInfo) - - switch mediaInfo { - case let .image(imageURL, thumbnailURL, imageInfo): - let _ = await self?.parameters.roomProxy.sendImage(url: imageURL, thumbnailURL: thumbnailURL, imageInfo: imageInfo) - default: - break - } - case .failure(let error): - MXLog.error("Failed processing media to upload with error: \(error)") - } - - self?.navigationStackCoordinator.setSheetCoordinator(nil) - } - case .cancel: - self?.navigationStackCoordinator.setSheetCoordinator(nil) - } - } - - self?.navigationStackCoordinator.setSheetCoordinator(mediaPickerPreviewScreenCoordinator) + self?.displayMediaUploadPreviewScreenForFile(at: url) } } - navigationStackCoordinator.setSheetCoordinator(mediaPickerCoordinator) + stackCoordinator.setRootCoordinator(mediaPickerCoordinator) + + navigationStackCoordinator.setSheetCoordinator(userIndicatorController) + } + + private func displayMediaUploadPreviewScreenForFile(at url: URL) { + let stackCoordinator = NavigationStackCoordinator() + let userIndicatorController = UserIndicatorController(rootCoordinator: stackCoordinator) + + let parameters = MediaUploadPreviewScreenCoordinatorParameters(userIndicatorController: userIndicatorController, + roomProxy: parameters.roomProxy, + mediaUploadingPreprocessor: MediaUploadingPreprocessor(), + title: url.lastPathComponent, + url: url) + + let mediaUploadPreviewScreenCoordinator = MediaUploadPreviewScreenCoordinator(parameters: parameters) { [weak self] action in + switch action { + case .dismiss: + self?.navigationStackCoordinator.setSheetCoordinator(nil) + } + } + + stackCoordinator.setRootCoordinator(mediaUploadPreviewScreenCoordinator) + + navigationStackCoordinator.setSheetCoordinator(userIndicatorController) } private func displayFilePreview(for file: MediaFileHandleProxy, with title: String?) { diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift index df2c9a3a1..10ed181f7 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift @@ -26,6 +26,7 @@ enum RoomScreenViewModelAction { case displayCameraPicker case displayMediaPicker case displayDocumentPicker + case displayMediaUploadPreviewScreen(url: URL) } enum RoomScreenComposerMode: Equatable { @@ -62,6 +63,8 @@ enum RoomScreenViewAction { case displayCameraPicker case displayMediaPicker case displayDocumentPicker + + case handlePasteOrDrop(provider: NSItemProvider) } struct RoomScreenViewState: BindableState { @@ -73,7 +76,6 @@ struct RoomScreenViewState: BindableState { var isBackPaginating = false var showLoading = false var timelineStyle: TimelineStyle - var mediaUploadingFlowEnabled: Bool var bindings: RoomScreenViewStateBindings diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index afee03aee..427246233 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -39,7 +39,6 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol roomTitle: roomName ?? "Unknown room 💥", roomAvatarURL: roomAvatarUrl, timelineStyle: ServiceLocator.shared.settings.timelineStyle, - mediaUploadingFlowEnabled: ServiceLocator.shared.settings.mediaUploadingFlowEnabled, bindings: .init(composerText: "", composerFocused: false)), imageProvider: mediaProvider) @@ -74,11 +73,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol ServiceLocator.shared.settings.$timelineStyle .weakAssign(to: \.state.timelineStyle, on: self) .store(in: &cancellables) - - ServiceLocator.shared.settings.$mediaUploadingFlowEnabled - .weakAssign(to: \.state.mediaUploadingFlowEnabled, on: self) - .store(in: &cancellables) - + buildTimelineViews() } @@ -122,6 +117,8 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol callback?(.displayMediaPicker) case .displayDocumentPicker: callback?(.displayDocumentPicker) + case .handlePasteOrDrop(let provider): + handlePasteOrDrop(provider) } } @@ -334,6 +331,60 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol state.composerMode = .default } } + + // Pasting and dropping + + private func handlePasteOrDrop(_ provider: NSItemProvider) { + guard let type = provider.registeredContentTypes.first, + let preferredExtension = type.preferredFilenameExtension else { + MXLog.error("Invalid NSItemProvider: \(provider)") + displayError(.toast(L10n.screenRoomErrorFailedProcessingMedia)) + return + } + + let providerSuggestedName = provider.suggestedName + let providerDescription = provider.description + + _ = provider.loadDataRepresentation(for: type) { data, error in + Task { @MainActor in + let loadingIndicatorIdentifier = UUID().uuidString + ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(id: loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading, persistent: true)) + defer { + ServiceLocator.shared.userIndicatorController.retractIndicatorWithId(loadingIndicatorIdentifier) + } + + if let error { + self.displayError(.toast(L10n.screenRoomErrorFailedProcessingMedia)) + MXLog.error("Failed processing NSItemProvider: \(providerDescription) with error: \(error)") + return + } + + guard let data else { + self.displayError(.toast(L10n.screenRoomErrorFailedProcessingMedia)) + MXLog.error("Invalid NSItemProvider data: \(providerDescription)") + return + } + + do { + let url = try await Task.detached { + if let filename = providerSuggestedName { + let hasExtension = !(filename as NSString).pathExtension.isEmpty + let filename = hasExtension ? filename : "\(filename).\(preferredExtension)" + return try FileManager.default.writeDataToTemporaryDirectory(data: data, fileName: filename) + } else { + let filename = "\(UUID().uuidString).\(preferredExtension)" + return try FileManager.default.writeDataToTemporaryDirectory(data: data, fileName: filename) + } + }.value + + self.callback?(.displayMediaUploadPreviewScreen(url: url)) + } catch { + self.displayError(.toast(L10n.screenRoomErrorFailedProcessingMedia)) + MXLog.error("Failed storing NSItemProvider data \(providerDescription) with error: \(error)") + } + } + } + } } // MARK: - Mocks diff --git a/ElementX/Sources/Screens/RoomScreen/View/MessageComposer.swift b/ElementX/Sources/Screens/RoomScreen/View/MessageComposer.swift index 932802efd..1e1c773ed 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/MessageComposer.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/MessageComposer.swift @@ -22,7 +22,8 @@ struct MessageComposer: View { let sendingDisabled: Bool let type: RoomScreenComposerMode - let sendAction: () -> Void + let sendAction: EnterKeyHandler + let pasteAction: PasteHandler let replyCancellationAction: () -> Void let editCancellationAction: () -> Void @@ -39,7 +40,8 @@ struct MessageComposer: View { focused: $focused, isMultiline: $isMultiline, maxHeight: 300, - onEnterKeyHandler: sendAction) + enterKeyHandler: sendAction, + pasteHandler: pasteAction) .tint(.element.brand) .padding(.vertical, 10) @@ -173,6 +175,7 @@ struct MessageComposer_Previews: PreviewProvider { sendingDisabled: true, type: .default, sendAction: { }, + pasteAction: { _ in }, replyCancellationAction: { }, editCancellationAction: { }) @@ -181,6 +184,7 @@ struct MessageComposer_Previews: PreviewProvider { sendingDisabled: false, type: .default, sendAction: { }, + pasteAction: { _ in }, replyCancellationAction: { }, editCancellationAction: { }) @@ -189,6 +193,7 @@ struct MessageComposer_Previews: PreviewProvider { sendingDisabled: false, type: .default, sendAction: { }, + pasteAction: { _ in }, replyCancellationAction: { }, editCancellationAction: { }) @@ -197,6 +202,7 @@ struct MessageComposer_Previews: PreviewProvider { sendingDisabled: false, type: .default, sendAction: { }, + pasteAction: { _ in }, replyCancellationAction: { }, editCancellationAction: { }) @@ -206,6 +212,7 @@ struct MessageComposer_Previews: PreviewProvider { type: .reply(id: UUID().uuidString, displayName: "John Doe"), sendAction: { }, + pasteAction: { _ in }, replyCancellationAction: { }, editCancellationAction: { }) @@ -214,6 +221,7 @@ struct MessageComposer_Previews: PreviewProvider { sendingDisabled: false, type: .edit(originalItemId: UUID().uuidString), sendAction: { }, + pasteAction: { _ in }, replyCancellationAction: { }, editCancellationAction: { }) } diff --git a/ElementX/Sources/Screens/RoomScreen/View/MessageComposerTextField.swift b/ElementX/Sources/Screens/RoomScreen/View/MessageComposerTextField.swift index 58af79037..f4b11cd8e 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/MessageComposerTextField.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/MessageComposerTextField.swift @@ -16,7 +16,8 @@ import SwiftUI -typealias OnEnterKeyHandler = () -> Void +typealias EnterKeyHandler = () -> Void +typealias PasteHandler = (NSItemProvider) -> Void struct MessageComposerTextField: View { let placeholder: String @@ -25,7 +26,8 @@ struct MessageComposerTextField: View { @Binding var isMultiline: Bool let maxHeight: CGFloat - let onEnterKeyHandler: OnEnterKeyHandler + let enterKeyHandler: EnterKeyHandler + let pasteHandler: PasteHandler private var showingPlaceholder: Bool { text.isEmpty @@ -40,7 +42,8 @@ struct MessageComposerTextField: View { focused: $focused, isMultiline: $isMultiline, maxHeight: maxHeight, - onEnterKeyHandler: onEnterKeyHandler) + enterKeyHandler: enterKeyHandler, + pasteHandler: pasteHandler) .accessibilityLabel(placeholder) .background(placeholderView, alignment: .topLeading) } @@ -64,15 +67,16 @@ private struct UITextViewWrapper: UIViewRepresentable { let maxHeight: CGFloat - let onEnterKeyHandler: OnEnterKeyHandler + let enterKeyHandler: EnterKeyHandler + let pasteHandler: PasteHandler private let font = UIFont.preferredFont(forTextStyle: .body) func makeUIView(context: UIViewRepresentableContext) -> UITextView { - let textView = TextViewWithKeyDetection() + let textView = ElementTextView() textView.isMultiline = $isMultiline textView.delegate = context.coordinator - textView.keyDelegate = context.coordinator + textView.elementDelegate = context.coordinator textView.textColor = .element.primaryContent textView.isEditable = true textView.font = font @@ -126,25 +130,29 @@ private struct UITextViewWrapper: UIViewRepresentable { Coordinator(text: $text, focused: $focused, maxHeight: maxHeight, - onEnterKeyHandler: onEnterKeyHandler) + enterKeyHandler: enterKeyHandler, + pasteHandler: pasteHandler) } - final class Coordinator: NSObject, UITextViewDelegate, TextViewWithKeyDetectionDelegate { + final class Coordinator: NSObject, UITextViewDelegate, ElementTextViewDelegate { private var text: Binding private var focused: Binding private let maxHeight: CGFloat - private let onEnterKeyHandler: OnEnterKeyHandler + private let enterKeyHandler: EnterKeyHandler + private let pasteHandler: PasteHandler init(text: Binding, focused: Binding, maxHeight: CGFloat, - onEnterKeyHandler: @escaping OnEnterKeyHandler) { + enterKeyHandler: @escaping EnterKeyHandler, + pasteHandler: @escaping PasteHandler) { self.text = text self.focused = focused self.maxHeight = maxHeight - self.onEnterKeyHandler = onEnterKeyHandler + self.enterKeyHandler = enterKeyHandler + self.pasteHandler = pasteHandler } func textViewDidChange(_ textView: UITextView) { @@ -159,23 +167,28 @@ private struct UITextViewWrapper: UIViewRepresentable { focused.wrappedValue = false } - func enterKeyWasPressed(textView: UITextView) { - onEnterKeyHandler() + func textViewDidReceiveEnterKeyPress(_ textView: UITextView) { + enterKeyHandler() } - func shiftEnterKeyPressed(textView: UITextView) { + func textViewDidReceiveShiftEnterKeyPress(_ textView: UITextView) { textView.insertText("\n") } + + func textView(_ textView: UITextView, didReceivePasteWith provider: NSItemProvider) { + pasteHandler(provider) + } } } -private protocol TextViewWithKeyDetectionDelegate: AnyObject { - func enterKeyWasPressed(textView: UITextView) - func shiftEnterKeyPressed(textView: UITextView) +private protocol ElementTextViewDelegate: AnyObject { + func textViewDidReceiveShiftEnterKeyPress(_ textView: UITextView) + func textViewDidReceiveEnterKeyPress(_ textView: UITextView) + func textView(_ textView: UITextView, didReceivePasteWith provider: NSItemProvider) } -private class TextViewWithKeyDetection: UITextView { - weak var keyDelegate: TextViewWithKeyDetectionDelegate? +private class ElementTextView: UITextView { + weak var elementDelegate: ElementTextViewDelegate? var isMultiline: Binding? @@ -185,11 +198,11 @@ private class TextViewWithKeyDetection: UITextView { } @objc func shiftEnterKeyPressed(sender: UIKeyCommand) { - keyDelegate?.shiftEnterKeyPressed(textView: self) + elementDelegate?.textViewDidReceiveShiftEnterKeyPress(self) } @objc func enterKeyPressed(sender: UIKeyCommand) { - keyDelegate?.enterKeyWasPressed(textView: self) + elementDelegate?.textViewDidReceiveEnterKeyPress(self) } override func layoutSubviews() { @@ -208,6 +221,31 @@ private class TextViewWithKeyDetection: UITextView { } } } + + // Pasting support + + override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { + if super.canPerformAction(action, withSender: sender) { + return true + } + + guard action == #selector(paste(_:)) else { + return false + } + + return UIPasteboard.general.itemProviders.first?.isSupportedForPasteOrDrop ?? false + } + + override func paste(_ sender: Any?) { + super.paste(sender) + + guard let provider = UIPasteboard.general.itemProviders.first, + provider.isSupportedForPasteOrDrop else { + return + } + + elementDelegate?.textView(self, didReceivePasteWith: provider) + } } struct MessageComposerTextField_Previews: PreviewProvider { @@ -236,7 +274,8 @@ struct MessageComposerTextField_Previews: PreviewProvider { focused: $focused, isMultiline: $isMultiline, maxHeight: 300, - onEnterKeyHandler: { }) + enterKeyHandler: { }, + pasteHandler: { _ in }) } } } diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomAttachmentPicker.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomAttachmentPicker.swift new file mode 100644 index 000000000..288562989 --- /dev/null +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomAttachmentPicker.swift @@ -0,0 +1,98 @@ +// +// Copyright 2023 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +struct RoomAttachmentPicker: View { + @ObservedObject var context: RoomScreenViewModel.Context + + @State private var showAttachmentPopover = false + @State private var sheetContentHeight = CGFloat(0) + + var body: some View { + Button { + showAttachmentPopover = true + } label: { + Image(systemName: "plus.circle.fill") + .font(.compound.headingLG) + .foregroundColor(.element.accent) + } + .accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPicker) + .popover(isPresented: $showAttachmentPopover) { + VStack(alignment: .leading, spacing: 0.0) { + Button { + showAttachmentPopover = false + context.send(viewAction: .displayMediaPicker) + } label: { + PickerLabel(title: L10n.screenRoomAttachmentSourceGallery, systemImageName: "photo.fill") + } + + Button { + showAttachmentPopover = false + context.send(viewAction: .displayDocumentPicker) + } label: { + PickerLabel(title: L10n.screenRoomAttachmentSourceFiles, systemImageName: "paperclip") + } + + Button { + showAttachmentPopover = false + context.send(viewAction: .displayCameraPicker) + } label: { + PickerLabel(title: L10n.screenRoomAttachmentSourceCamera, systemImageName: "camera.fill") + } + } + .background { + // This is done in the background otherwise GeometryReader tends to expand to + // all the space given to it like color or shape. + GeometryReader { proxy in + Color.clear + .onAppear { + sheetContentHeight = proxy.size.height + } + } + } + .presentationDetents([.height(sheetContentHeight)]) + .presentationDragIndicator(.visible) + .tint(.element.accent) + } + } + + private struct PickerLabel: View { + let title: String + let systemImageName: String + + var body: some View { + Label(title, systemImage: systemImageName) + .labelStyle(EqualIconWidthLabelStyle()) + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity, alignment: .leading) + .padding() + } + } +} + +private struct EqualIconWidthLabelStyle: LabelStyle { + @ScaledMetric private var menuIconSize = 24.0 + + func makeBody(configuration: Configuration) -> some View { + HStack { + configuration + .icon + .frame(width: menuIconSize, height: menuIconSize) + configuration.title + } + } +} diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift index 0f34f3fd7..40649a509 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift @@ -19,16 +19,14 @@ import SwiftUI struct RoomScreen: View { @ObservedObject var context: RoomScreenViewModel.Context @State private var showReactionsMenuForItemId = "" + @State private var dragOver = false var body: some View { timeline .background(Color.element.background.ignoresSafeArea()) // Kills the toolbar translucency. .safeAreaInset(edge: .bottom, spacing: 0) { HStack(spacing: 4.0) { - if context.viewState.mediaUploadingFlowEnabled { - sendAttachmentButton - } - + RoomAttachmentPicker(context: context) messageComposer } .padding() @@ -48,6 +46,15 @@ struct RoomScreen: View { guard !Task.isCancelled else { return } context.send(viewAction: .markRoomAsRead) } + .onDrop(of: ["public.item"], isTargeted: $dragOver) { providers -> Bool in + guard let provider = providers.first, + provider.isSupportedForPasteOrDrop else { + return false + } + + context.send(viewAction: .handlePasteOrDrop(provider: provider)) + return true + } } private var timeline: some View { @@ -64,6 +71,8 @@ struct RoomScreen: View { sendingDisabled: context.viewState.sendButtonDisabled, type: context.viewState.composerMode) { sendMessage() + } pasteAction: { provider in + context.send(viewAction: .handlePasteOrDrop(provider: provider)) } replyCancellationAction: { context.send(viewAction: .cancelReply) } editCancellationAction: { @@ -113,30 +122,6 @@ struct RoomScreen: View { } } - private var sendAttachmentButton: some View { - Menu { - Button { - context.send(viewAction: .displayDocumentPicker) - } label: { - Label(UntranslatedL10n.mediaUploadDocumentPicker, systemImage: "doc") - } - Button { - context.send(viewAction: .displayMediaPicker) - } label: { - Label(UntranslatedL10n.mediaUploadPhotoAndVideoPicker, systemImage: "photo") - } - Button { - context.send(viewAction: .displayCameraPicker) - } label: { - Label(UntranslatedL10n.mediaUploadCameraPicker, systemImage: "camera") - } - } label: { - Image(systemName: "plus.circle") - .font(.compound.headingLG) - .foregroundColor(.element.brand) - } - } - private func sendMessage() { guard !context.viewState.sendButtonDisabled else { return } context.send(viewAction: .sendMessage) diff --git a/ElementX/Sources/Screens/RoomScreen/View/Timeline/ImageRoomTimelineView.swift b/ElementX/Sources/Screens/RoomScreen/View/Timeline/ImageRoomTimelineView.swift index a33bc91ea..15d9b1e59 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Timeline/ImageRoomTimelineView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Timeline/ImageRoomTimelineView.swift @@ -28,7 +28,7 @@ struct ImageRoomTimelineView: View { imageProvider: context.imageProvider) { placeholder } - .frame(maxHeight: min(300, timelineItem.height ?? .infinity)) + .frame(maxHeight: min(300, max(100, timelineItem.height ?? .infinity))) .aspectRatio(timelineItem.aspectRatio, contentMode: .fit) } } diff --git a/ElementX/Sources/Screens/RoomScreen/View/Timeline/StickerRoomTimelineView.swift b/ElementX/Sources/Screens/RoomScreen/View/Timeline/StickerRoomTimelineView.swift index 1e8b146bd..5a42edcc6 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Timeline/StickerRoomTimelineView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Timeline/StickerRoomTimelineView.swift @@ -28,7 +28,7 @@ struct StickerRoomTimelineView: View { imageProvider: context.imageProvider) { placeholder } - .frame(maxHeight: min(300, timelineItem.height ?? .infinity)) + .frame(maxHeight: min(300, max(100, timelineItem.height ?? .infinity))) .aspectRatio(timelineItem.aspectRatio, contentMode: .fit) } .accessibilityLabel(timelineItem.body) diff --git a/ElementX/Sources/Screens/RoomScreen/View/Timeline/VideoRoomTimelineView.swift b/ElementX/Sources/Screens/RoomScreen/View/Timeline/VideoRoomTimelineView.swift index c46f99866..885f662ba 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Timeline/VideoRoomTimelineView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Timeline/VideoRoomTimelineView.swift @@ -24,7 +24,7 @@ struct VideoRoomTimelineView: View { var body: some View { TimelineStyler(timelineItem: timelineItem) { thumbnail - .frame(maxHeight: min(300, timelineItem.height ?? .infinity)) + .frame(maxHeight: min(300, max(100, timelineItem.height ?? .infinity))) .aspectRatio(timelineItem.aspectRatio, contentMode: .fit) } } diff --git a/ElementX/Sources/Screens/StartChatScreen/StartChatScreenViewModel.swift b/ElementX/Sources/Screens/StartChatScreen/StartChatScreenViewModel.swift index b938eed3a..671806d38 100644 --- a/ElementX/Sources/Screens/StartChatScreen/StartChatScreenViewModel.swift +++ b/ElementX/Sources/Screens/StartChatScreen/StartChatScreenViewModel.swift @@ -150,7 +150,7 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie // MARK: Loading indicator - static let loadingIndicatorIdentifier = "StartChatLoading" + private static let loadingIndicatorIdentifier = "StartChatLoading" private func showLoadingIndicator() { userIndicatorController?.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, diff --git a/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift b/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift index 47b084008..cdd04180d 100644 --- a/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift +++ b/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift @@ -40,7 +40,7 @@ enum MediaInfo { case image(imageURL: URL, thumbnailURL: URL, imageInfo: ImageInfo) case video(videoURL: URL, thumbnailURL: URL, videoInfo: VideoInfo) case audio(audioURL: URL, audioInfo: AudioInfo) - case file(FileInfo) + case file(fileURL: URL, fileInfo: FileInfo) } private struct ImageProcessingInfo { @@ -205,7 +205,7 @@ struct MediaUploadingPreprocessor { let fileSize = try? UInt64(FileManager.default.sizeForItem(at: url)) let fileInfo = FileInfo(mimetype: mimeType, size: fileSize, thumbnailInfo: nil, thumbnailSource: nil) - return .success(.file(fileInfo)) + return .success(.file(fileURL: url, fileInfo: fileInfo)) } // MARK: Images @@ -217,32 +217,30 @@ struct MediaUploadingPreprocessor { /// - Returns: the URL for the modified image and its size as an `ImageProcessingResult` private func stripLocationFromImage(at url: URL, type: UTType, mimeType: String) async -> Result { guard let originalData = NSData(contentsOf: url), - let originalCGImage = UIImage(data: originalData as Data)?.cgImage, + let originalImage = UIImage(data: originalData as Data), let imageSource = CGImageSourceCreateWithData(originalData, nil) else { return .failure(.failedStrippingLocationData) } - guard let originalMetadata = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) else { - MXLog.info("No metadata found. Returning original image") - return .success(.init(url: url, height: Double(originalCGImage.height), width: Double(originalCGImage.width), mimeType: mimeType, blurhash: nil)) + guard let originalMetadata = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil), + (originalMetadata as NSDictionary).value(forKeyPath: "\(kCGImagePropertyGPSDictionary)") != nil else { + MXLog.info("No GPS metadata found. Returning original image") + return .success(.init(url: url, height: Double(originalImage.size.height), width: Double(originalImage.size.width), mimeType: mimeType, blurhash: nil)) } - guard let adjustedMetadata = (originalMetadata as NSDictionary).mutableCopy() as? NSMutableDictionary else { - return .failure(.failedStrippingLocationData) - } - - adjustedMetadata.setValue(nil, forKeyPath: "\(kCGImagePropertyGPSDictionary)") + let count = CGImageSourceGetCount(imageSource) + let metadataKeysToRemove = [kCGImagePropertyGPSDictionary: kCFNull] let data = NSMutableData() - guard let destination = CGImageDestinationCreateWithData(data as CFMutableData, type.identifier as CFString, 1, nil) else { + guard let destination = CGImageDestinationCreateWithData(data as CFMutableData, type.identifier as CFString, count, nil) else { return .failure(.failedStrippingLocationData) } - CGImageDestinationAddImage(destination, originalCGImage, adjustedMetadata) + CGImageDestinationAddImageFromSource(destination, imageSource, 0, metadataKeysToRemove as NSDictionary) CGImageDestinationFinalize(destination) do { try data.write(to: url) - return .success(.init(url: url, height: Double(originalCGImage.height), width: Double(originalCGImage.width), mimeType: mimeType, blurhash: nil)) + return .success(.init(url: url, height: Double(originalImage.size.height), width: Double(originalImage.size.width), mimeType: mimeType, blurhash: nil)) } catch { return .failure(.failedStrippingLocationData) } diff --git a/ElementX/Sources/Services/Room/RoomProxy.swift b/ElementX/Sources/Services/Room/RoomProxy.swift index 422bfe760..c7a25067d 100644 --- a/ElementX/Sources/Services/Room/RoomProxy.swift +++ b/ElementX/Sources/Services/Room/RoomProxy.swift @@ -227,20 +227,67 @@ class RoomProxy: RoomProxyProtocol { } func sendImage(url: URL, thumbnailURL: URL, imageInfo: ImageInfo) async -> Result { - .failure(.failedSendingMedia) -// sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) -// defer { -// sendMessageBackgroundTask?.stop() -// } -// -// return await Task.dispatch(on: userInitiatedDispatchQueue) { -// do { -// try self.room.sendImage(url: url.path(), thumbnailUrl: thumbnailURL.path(), imageInfo: imageInfo) -// return .success(()) -// } catch { -// return .failure(.failedEditingMessage) -// } -// } + sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) + defer { + sendMessageBackgroundTask?.stop() + } + + return await Task.dispatch(on: userInitiatedDispatchQueue) { + do { + try self.room.sendImage(url: url.path(), thumbnailUrl: thumbnailURL.path(), imageInfo: imageInfo) + return .success(()) + } catch { + return .failure(.failedSendingMedia) + } + } + } + + func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo) async -> Result { + sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) + defer { + sendMessageBackgroundTask?.stop() + } + + return await Task.dispatch(on: userInitiatedDispatchQueue) { + do { + try self.room.sendVideo(url: url.path(), thumbnailUrl: thumbnailURL.path(), videoInfo: videoInfo) + return .success(()) + } catch { + return .failure(.failedSendingMedia) + } + } + } + + func sendAudio(url: URL, audioInfo: AudioInfo) async -> Result { + sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) + defer { + sendMessageBackgroundTask?.stop() + } + + return await Task.dispatch(on: userInitiatedDispatchQueue) { + do { + try self.room.sendAudio(url: url.path(), audioInfo: audioInfo) + return .success(()) + } catch { + return .failure(.failedSendingMedia) + } + } + } + + func sendFile(url: URL, fileInfo: FileInfo) async -> Result { + sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) + defer { + sendMessageBackgroundTask?.stop() + } + + return await Task.dispatch(on: userInitiatedDispatchQueue) { + do { + try self.room.sendFile(url: url.path(), fileInfo: fileInfo) + return .success(()) + } catch { + return .failure(.failedSendingMedia) + } + } } func editMessage(_ newMessage: String, original eventID: String) async -> Result { diff --git a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift index 0fb7f9811..bf5c0e50b 100644 --- a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift @@ -76,6 +76,12 @@ protocol RoomProxyProtocol { func sendReaction(_ reaction: String, to eventID: String) async -> Result func sendImage(url: URL, thumbnailURL: URL, imageInfo: ImageInfo) async -> Result + + func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo) async -> Result + + func sendAudio(url: URL, audioInfo: AudioInfo) async -> Result + + func sendFile(url: URL, fileInfo: FileInfo) async -> Result func editMessage(_ newMessage: String, original eventID: String) async -> Result diff --git a/UITests/Sources/MediaPickerPreviewScreenUITests.swift b/UITests/Sources/MediaUploadPreviewScreenUITests.swift similarity index 92% rename from UITests/Sources/MediaPickerPreviewScreenUITests.swift rename to UITests/Sources/MediaUploadPreviewScreenUITests.swift index 1b9fe37ac..5d7d7c4fa 100644 --- a/UITests/Sources/MediaPickerPreviewScreenUITests.swift +++ b/UITests/Sources/MediaUploadPreviewScreenUITests.swift @@ -17,4 +17,4 @@ import ElementX import XCTest -class MediaPickerPreviewScreenUITests: XCTestCase { } +class MediaUploadPreviewScreenUITests: XCTestCase { } diff --git a/UITests/Sources/UserSessionScreenTests.swift b/UITests/Sources/UserSessionScreenTests.swift index 00d011fe2..726bb85a2 100644 --- a/UITests/Sources/UserSessionScreenTests.swift +++ b/UITests/Sources/UserSessionScreenTests.swift @@ -33,5 +33,9 @@ class UserSessionScreenTests: XCTestCase { try await Task.sleep(for: .seconds(1)) app.assertScreenshot(.userSessionScreen, step: 2) + + app.buttons[A11yIdentifiers.roomScreen.attachmentPicker].tap() + + app.assertScreenshot(.userSessionScreen, step: 3) } } diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomEncryptedWithAvatar.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomEncryptedWithAvatar.png index 15b9bf833..4a75f1ad8 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomEncryptedWithAvatar.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomEncryptedWithAvatar.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5cb9894274818fc787e8368059ee250b1116b86c007d9e6120c0cdfb662a9b1f -size 225450 +oid sha256:6fb7862fb96c989a30077ea6a91c1b550b6bff2dec6095ecba54813a4e87a91e +size 226885 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutBottom-0.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutBottom-0.png index b5a23ed2e..75d531b44 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutBottom-0.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutBottom-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e530096c62b5eed931df88a4a0a229130c2b3007cee614706f007a81e5fbf76 -size 277101 +oid sha256:9f18fbb5e3246cfcc7d73448dd28c88ffd2cec2de5143e151d36228f54286bc8 +size 279203 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutBottom-1.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutBottom-1.png index 4006394d2..fa66d294d 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutBottom-1.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutBottom-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ec5385d592d1067b7f8afda9dfa56ec21a94d54eb7c3351078ce9ff1b64624c -size 286210 +oid sha256:0542a656e0a35a24e668367fae91963f5d9fa742d530051748ed6221b8a2bf5a +size 287156 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutMiddle-0.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutMiddle-0.png index f47b43d42..ff0a5173d 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutMiddle-0.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutMiddle-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c79ad9e4df49dfbd266248571d082e98852d1a2a02d743a413281a3b08dd3068 -size 285382 +oid sha256:eb1c54d3f5315093be786051ad4e53579c2c51f4b79b1bb1f21baea95a20f1f5 +size 287904 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutMiddle-1.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutMiddle-1.png index f1d7bf8ca..1fbd695f2 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutMiddle-1.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutMiddle-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac2bfda0c854002deb4b220fd95588a711d5bc80832cf51dcd68d1b834215883 -size 298817 +oid sha256:376ec8525fcc28da894f231b6538923feeba0a296b531d0b8d214bba769a94ae +size 301762 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutTop.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutTop.png index 5538c48a9..3a182b987 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutTop.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomLayoutTop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30ae9c3b1d985b20bcb233a6509fcacd20b889935a210afab837ce2849954d81 -size 263931 +oid sha256:2781f8b12a7c359ed0aba73b2601d713a152a4c6fdd1a76115d6c214c1bf4a8c +size 265581 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomPlainNoAvatar.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomPlainNoAvatar.png index 6b16e5d06..6870f9548 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomPlainNoAvatar.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomPlainNoAvatar.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d1aab4750d55c1c59b9da255f5ce28f7d49d85bac9f5776faaef25991b2a6d2 -size 225335 +oid sha256:c0a6edbb32843b33c62cb0ab10a0c461c0704bb75c61e20f37a7a57c517a3a52 +size 226770 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimeline.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimeline.png index 0696766d7..cb053c84c 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimeline.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimeline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73b62c2a828f6ba3a2c30f7d1301125b8a585e4b3541cdd126a30503085d1c89 -size 94176 +oid sha256:f62df61b8daf07b747463042153b0726d0aff05b439978524316ebac52229ecc +size 95571 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png index bc9c2b692..badee3699 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c544755cdbe2f6f3bfb6529660f6ccec8664dac5e43e6bfbea873750500952b5 -size 115325 +oid sha256:a03a58ed11fef52767c27fbf6475f8ca91de31ecc0543e8861a27f30b7b62793 +size 116521 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimelineLargePagination.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimelineLargePagination.png index 85f8e1227..4c875cf31 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimelineLargePagination.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimelineLargePagination.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e14f8fae9d1b7dcf6add882eb5f064ca6de62bdc5b919c3f122c9d3cb0d7028 -size 282835 +oid sha256:a630be82b6722a76407e608018e75b1798a218bcf7aaf25e5908c6f96862e274 +size 284402 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreen-1.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreen-1.png index 2d0572a67..56feee0e2 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreen-1.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreen-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a223ca272eb48269cc08ddba430fd86b3fe42bd60ce8c1bd3c26578e15ab9b4d -size 154469 +oid sha256:efb8498c7f77bb8df8992eedd914b371ac5c7ed8351fc5c392e2b58b5f614663 +size 154248 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreen-2.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreen-2.png index a9b16478b..9b562fb5a 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreen-2.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreen-2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:610b10157a0ee583bc273031002d39894e5355f30568a287d502d93e7a327c2f -size 313574 +oid sha256:bf27bfef228ca1b4b0c043cd8d5034b61b3f4780475f5bc2ebe43273d288a54b +size 315147 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreen-3.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreen-3.png new file mode 100644 index 000000000..65695f64d --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreen-3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0970c28baf874cd88eddf8e3da907dde4e9702ea34c2cdef1138c911b7f570a0 +size 382923 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomEncryptedWithAvatar.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomEncryptedWithAvatar.png index 962f5ef4b..9d604a631 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomEncryptedWithAvatar.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomEncryptedWithAvatar.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ddfcc3aa97708a061e822dd6c3230c57582ccbdf3d5d3a53a30008743ac5a5d4 -size 346656 +oid sha256:a66665604f91d490c85acaba6941de9dbc4ca772bd079eabbd78efc821841886 +size 349022 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutBottom-0.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutBottom-0.png index 2929228a6..07596936f 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutBottom-0.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutBottom-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:307248a447fe5928ca89b0d2fd8157e246d91c62544e045a6ff21d159dcb544e -size 286725 +oid sha256:0b430246471c4855584c8d12ef502e3e5c589b4c8c379a1e7451ea5fc0667d16 +size 289146 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutBottom-1.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutBottom-1.png index dac2ead40..1c4bcdcd0 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutBottom-1.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutBottom-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d8cc9c1c14b8e6d5e1b2565bb72b0df204b66247f6e6c691084bc12dad8005a -size 282994 +oid sha256:de61686d433b8b952c1e59d46d426886d8d309fd51e05e675bbbef19302e9d38 +size 283677 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutMiddle-0.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutMiddle-0.png index c994375ef..f78a68acc 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutMiddle-0.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutMiddle-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b91eeb19fe0f391850071b61630c6cd4fa9e933ac58b64eda464ae9448d9ad2 -size 310727 +oid sha256:db9a008bb006b51adcc1bb2be1d0fe33c95b21f8047eb2f3bbdccbf81de2b7c4 +size 313462 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutMiddle-1.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutMiddle-1.png index 6ff1ddb54..79fba5592 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutMiddle-1.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutMiddle-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2530011c5753433e2f339d37df7b8ac73b00d17cb0123762bf0c2286dce423d -size 293299 +oid sha256:3ef5c1c5f10457803390c17cc3fd6dde17d8703e8b12bdbba385d506298bf604 +size 296864 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutTop.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutTop.png index d2ffc7312..eb30e1627 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutTop.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomLayoutTop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea7e78a3ebda280ac973dc508b3b0252fe2f2a34278a28f3445165eaa3d503c6 -size 239496 +oid sha256:4f20d74ceaa84e10c81ec2be3a2a8cd272f7beeeb9dc1c2938ff24b838d4c4dd +size 241625 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomPlainNoAvatar.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomPlainNoAvatar.png index 346979886..0047f15d9 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomPlainNoAvatar.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomPlainNoAvatar.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2eb94dab1ca62737f3f061e0d1db36402792e9233357175cf7dab3f1829ab63e -size 346412 +oid sha256:cda777f0e512812d9f7fcd35558c8d80e04f920076378a6877e026aabfec8a18 +size 348778 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimeline.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimeline.png index af11be0b6..c9c396dd2 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimeline.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimeline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:685d500c8651d82669558f86392a772ea26822fe3b9a1412cc27abffc305d29c -size 146765 +oid sha256:5a56ebd80da6117480d23ca1ed8014ce47c45535955918680952ebf73f9116a4 +size 147486 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png index 0cb98c923..6363d28a4 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c827050844076da8a5258bb25a8b15df22331bb72c4138af1bd7b437d175f5fa -size 180091 +oid sha256:f2a7499922b564356c7b979a70822ef661a2e0ab683a287ea36d45b10b8b9def +size 181749 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimelineLargePagination.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimelineLargePagination.png index c118a1c9f..2a1b29bc0 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimelineLargePagination.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimelineLargePagination.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:203b0151d0d7c6992814526a0e5f8515b62bcb0c16f1747cdaf5ef9bbb95f2af -size 298720 +oid sha256:c89e1ad93fc384c9f411dc7c30c5b3f62f07728e73d833755fa52d5079dcfa98 +size 301035 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreen-1.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreen-1.png index e63e44a41..7fd445b9a 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreen-1.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreen-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f004a204fa567dcf4fb84656c76f6bd6df014c5ae835ab12c5255b7038896c56 -size 124514 +oid sha256:7943a36b71c55df9a3d079afc47c85ce28e7df85f51cef7e4a162fb27fd31d3b +size 125709 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreen-2.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreen-2.png index 0e8c6abfe..5a7321e5b 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreen-2.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreen-2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:902cb334bc4e2575aaeb6aea3362d9bf9c0b0fc445b0fa3c34ce0ea4cba5e48f -size 289982 +oid sha256:79a626a822188e1feecac4489e341155ecbeac9106537186f48a765c5ba093b5 +size 290518 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreen-3.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreen-3.png new file mode 100644 index 000000000..78edb9b8c --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreen-3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b34012b9a354f4ce79fe0cb2c37c31bb5a6e80cef2e6277de57cd2bab6a1789b +size 276121 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomEncryptedWithAvatar.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomEncryptedWithAvatar.png index 02dea2dc1..ebae358e2 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomEncryptedWithAvatar.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomEncryptedWithAvatar.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a34d12d562035bcfdfb7f3db59f3c5ad39a177a975e1cd308432ed26f05c8d82 -size 226595 +oid sha256:2733eeae5ecc6a5bf22e7a7e5426ddec69a78ed0952c4b7220e5910ccaae7ae9 +size 228223 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutBottom-0.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutBottom-0.png index 67577ab5c..1e416544e 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutBottom-0.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutBottom-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d075bddff47c4046cb35503d423b86aab817c2019a0c42eaa6c6f38d7dca4181 -size 278074 +oid sha256:19998062e4ccdab92a50fe021810ebe89d582a3b59b3d17c11199d60c0898730 +size 279683 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutBottom-1.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutBottom-1.png index 0821d3553..54fdd32ed 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutBottom-1.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutBottom-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0246bc88c988c41e203c47ab9cabd13ca3277a2b940eca722e2024e7348119f7 -size 287184 +oid sha256:92fafbe4bd93ab79c4e3dcdac286040290136ba38dfdf9810d2f5bda06831f30 +size 289204 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutMiddle-0.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutMiddle-0.png index f3d195f1b..b84df72eb 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutMiddle-0.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutMiddle-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2917480684b8e48c6ae753a438c05cce44ae362947dee8658325749d506ca27c -size 286818 +oid sha256:f3bdcf39e0536fd9627e375f4b3b5b2cddf9bc64e54ea991f3a7e702d0f40f44 +size 288374 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutMiddle-1.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutMiddle-1.png index de47f2e27..95a24f251 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutMiddle-1.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutMiddle-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7973327e95a3ebcdd6bfe90ca56ed2d966fe9f53dda5098ddaad43e8b1269fa5 -size 300470 +oid sha256:071fc0a6b8f82c7b755beef281cb8d9f3b193002f33df0fddaab82bc3e27c84c +size 301337 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutTop.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutTop.png index a45a88a0b..d4f0e5e3e 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutTop.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomLayoutTop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20ea438ea8e6e2a3c0e0ce311bf5db75b629b981ea4f2d12a289a2df8f6c4413 -size 264433 +oid sha256:61c050c837145b2b526e914fd60f2a8f4a99f2eef59b88135e8753e76973b17c +size 265983 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomPlainNoAvatar.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomPlainNoAvatar.png index 5f4487a53..49f668584 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomPlainNoAvatar.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomPlainNoAvatar.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc4586d13995d4626856f66cc56437811de7e183e1eacf762877007c2ad8dd4a -size 226475 +oid sha256:385cc5ee090f85667a70689fc46a212d0dce020d2b381b8fc29c1d678a27ba22 +size 228111 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomSmallTimeline.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomSmallTimeline.png index b3cd5f553..310942671 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomSmallTimeline.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomSmallTimeline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28d48a3e95753033af12d4ced277763352c7469bebd34e83b5340830d38d88e5 -size 94563 +oid sha256:6123d80a2e17087302f0703483235e55ca10a1b1d145ef24af17e1d70ddc16f2 +size 96064 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png index 47d07fc49..ec084f59b 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6bc5567c37cf3729e5aff93297fbe47cbf51302d57e3bcafa7550ddd26f4fdb -size 115469 +oid sha256:b99b3cc08cfc075ce0e1227c120664fe0180e44c37254907630316b64837f5de +size 116997 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomSmallTimelineLargePagination.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomSmallTimelineLargePagination.png index e24af770d..fdbb0cea5 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomSmallTimelineLargePagination.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.roomSmallTimelineLargePagination.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b6e9e48310fe7b00d107a56e3e7d0dc8cc45fade058c7f5a8d2718050b2e816 -size 283307 +oid sha256:d4d7392606a1eeb82c847ad170e6b0d7e0059eb65b1b7d02bb61a8a9188d17ad +size 284875 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreen-1.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreen-1.png index ca8e75276..ba46e90ad 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreen-1.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreen-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57fe94d6a90b4bee1e4e63baee4b4fe9a5470fb9c33ae98db3aa488cd3eedd65 -size 156069 +oid sha256:bd3e0b1dee7ecfaf609cbdb0cf4f23f5223f1e8be35ba7f3546ddc4fca8cb88a +size 156167 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreen-2.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreen-2.png index 936fcff77..fbad3387f 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreen-2.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreen-2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b684cb5897525d433e9693a2f243a3dcb42403c5d02a1ed8ffcc4d3b03e49c2 -size 315804 +oid sha256:1b1161bb1ad7fc1db9c996247a82bf62a18519dfa061669deef9c0557d92b845 +size 317302 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreen-3.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreen-3.png new file mode 100644 index 000000000..e210eb7eb --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreen-3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5be7ed8b335671e421a89cc6bfbd2d2e297d090dda6cd6cefa4a01727e29600a +size 372076 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomEncryptedWithAvatar.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomEncryptedWithAvatar.png index 8a8bb56f5..b0fa66641 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomEncryptedWithAvatar.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomEncryptedWithAvatar.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b97fe382eb5918747ae2475718adcfc4a4b000a2a5053201d4c2b8c1f675421 -size 347284 +oid sha256:15b2a6aaa9b256eaaa580843fd089825067ff1501a78376a4504d7bfcc690493 +size 349600 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutBottom-0.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutBottom-0.png index 6b6620dab..d4d712b10 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutBottom-0.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutBottom-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64f0b68593f9e4c3abc475aa0b4469edbf16d735bb56f2e5288179cdfe273679 -size 287438 +oid sha256:141a671733a34c53f69f3dc54138af9728de5bf6dce1745fa3f2f1a39a5b022f +size 289721 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutBottom-1.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutBottom-1.png index 0e9b60547..f28625448 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutBottom-1.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutBottom-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3cee2ea0dda464cbaef1ae9b0d9a03dbd5af98274a490ceef3343dc0c92d2f41 -size 283893 +oid sha256:cab131f2cb02a1406928be3b7a2e926d4f861a2ca2df4bd0df33330246a3d505 +size 286283 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutMiddle-0.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutMiddle-0.png index eab0e56d5..b3660a648 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutMiddle-0.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutMiddle-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d03bc917291037f8daea11687d48a0043bd9cae93d8dd0527936657e77a4b40 -size 311816 +oid sha256:3bffe545a2633bd37a91289115718ccc3ddf97ea96a0d5a1c96d9a3b10c08455 +size 313986 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutMiddle-1.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutMiddle-1.png index 9f35c54f1..20707e8b0 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutMiddle-1.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutMiddle-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4cc60e1c748a48f579f657bdbf63b9141171e29c3a964ced38f848dce21192ee -size 295106 +oid sha256:10a42f8557a3022090df12e1553f2f0b785bb9256dd27a8b218c1ec101b634b9 +size 296693 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutTop.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutTop.png index ae29fed96..a964c2a34 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutTop.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomLayoutTop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ee52ccb6cb98d52ca7e24c6c81b66a6b35374fb308cc65d4c64fdce6a051ed2 -size 239928 +oid sha256:f3b361a6c045cf067e788ff7726e5ad821c4d57796a40fcbc17296002955fab6 +size 242272 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomPlainNoAvatar.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomPlainNoAvatar.png index 7f5157573..7c3d34324 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomPlainNoAvatar.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomPlainNoAvatar.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9164bbfd4316c2f83ead183e8ca54c3443269ede10614e8709c8b3ba21bf449 -size 347040 +oid sha256:5a936985a9dfc97a33afc709404cad0661a9801f23dd64f56a334c81da981c23 +size 349356 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomSmallTimeline.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomSmallTimeline.png index 50006e6ef..3acbf2819 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomSmallTimeline.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomSmallTimeline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ed637439d696f6591a1799389c0aedce1a827e636de5ddda26674f20b1edd09 -size 147189 +oid sha256:49070bf6438d39cf70eed78f4fcfee31824ec97f2b80dffea5b4557827336667 +size 148046 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png index 187ed100b..dd285235b 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98dba29c2b606c86771f7617f913d943de9aa8dc267fff374dd3f4c578b27d2c -size 180186 +oid sha256:724f9e598a2e0ae454536de50cca057eb6f3d880b845fdac876c7a34d500c5a5 +size 182302 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomSmallTimelineLargePagination.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomSmallTimelineLargePagination.png index 63baf286c..19cfb1783 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomSmallTimelineLargePagination.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.roomSmallTimelineLargePagination.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b12aff9447978bf7908acf27770bfcd5786b36168a354377dabefe93c77e672 -size 299340 +oid sha256:d4e2d7ccab6079a25d9a7692f2aad2e6bc82f4643f4a8703b92cc561f5723528 +size 301620 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreen-1.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreen-1.png index 02c8f1420..5e1a82f41 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreen-1.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreen-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ed2cf85759dba6561502d72b6c46ed0c4e7d40a65de066c5fc1346f567707ab -size 128637 +oid sha256:4752cf1669531b5faa275e79dcb44cbcdb9c320867ed944b1cd966f379cef2db +size 128774 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreen-2.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreen-2.png index b08262436..be1ce4aa1 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreen-2.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreen-2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:657d8fac3e3ad59e7bd304f92dd944591e3ded2d30ef44901c20e4755f280b42 -size 289714 +oid sha256:e9fc9261b97d9e240b92b5e9dd741c934b783059f50b3aa8fb10c292272b5aa0 +size 290789 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreen-3.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreen-3.png new file mode 100644 index 000000000..edbd0b4f7 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreen-3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71b7f71e0b5118067b932b3151bc1bddb4831657e958428536b3dbdd2b1dc211 +size 283228 diff --git a/UnitTests/Sources/MediaPickerPreviewScreenViewModelTests.swift b/UnitTests/Sources/MediaUploadPreviewScreenViewModelTests.swift similarity index 91% rename from UnitTests/Sources/MediaPickerPreviewScreenViewModelTests.swift rename to UnitTests/Sources/MediaUploadPreviewScreenViewModelTests.swift index 6210b788f..f48ce16dd 100644 --- a/UnitTests/Sources/MediaPickerPreviewScreenViewModelTests.swift +++ b/UnitTests/Sources/MediaUploadPreviewScreenViewModelTests.swift @@ -19,4 +19,4 @@ import XCTest @testable import ElementX @MainActor -class MediaPickerPreviewScreenViewModelTests: XCTestCase { } +class MediaUploadPreviewScreenViewModelTests: XCTestCase { }