From 76a64567c60b72499c6462f80b8a3129aeb490e2 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 11 Dec 2024 11:15:48 +0200 Subject: [PATCH 1/4] Move the voice message views to where they belong --- ElementX.xcodeproj/project.pbxproj | 24 +++++++------------ .../VoiceMessageRoomTimelineView.swift | 0 .../View}/VoiceMessageRoomPlaybackView.swift | 0 3 files changed, 8 insertions(+), 16 deletions(-) rename ElementX/Sources/{Services/Timeline/TimelineItems/Items/Messages/VoiceMessages => Screens/Timeline/View/TimelineItemViews}/VoiceMessageRoomTimelineView.swift (100%) rename ElementX/Sources/{Services/Timeline/TimelineItems/Items/Messages/VoiceMessages => Screens/Timeline/View}/VoiceMessageRoomPlaybackView.swift (100%) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 4e30b4a023..41b3302b55 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -99,6 +99,7 @@ 1146E9EDCF8344F7D6E0D553 /* MockCoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0376C429FAB1687C3D905F3E /* MockCoder.swift */; }; 119AE9A3FC6E0606C1146528 /* NotificationSettingsEditScreenRoomCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97F8963B14EB0AF3940DDBF /* NotificationSettingsEditScreenRoomCell.swift */; }; 11A6B8E3CBDBF0A4107FF4CE /* OnboardingFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3285BD95B564CA2A948E511 /* OnboardingFlowCoordinator.swift */; }; + 1224084B7E289E0830BA2C54 /* VoiceMessageRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6A293D06BAB2B7A17D9314B /* VoiceMessageRoomTimelineView.swift */; }; 126EE01D8BEAEF26105D83C5 /* RoomDetailsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1A5FEF17ED7E6176D922D4F /* RoomDetailsScreen.swift */; }; 128FFD8A3D85845F9A927F47 /* PollRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF8548D48512127CCC17C520 /* PollRoomTimelineView.swift */; }; 12C867E85E6D12EEDFD0B127 /* CustomStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C4762F8D6112E43117DB2F /* CustomStringConvertible.swift */; }; @@ -416,6 +417,7 @@ 558F37B1A8F2C4CC9B1ACEDA /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = 3262F08E1C3483C22A7A319F /* Compound */; }; 55CDD3968D95D1A820B5491E /* PlaceholderAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */; }; 55D18AA4F4A2257642EBDB94 /* GlobalSearchScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38354164AF59C5006CD05878 /* GlobalSearchScreenViewModel.swift */; }; + 55DF6DEEF2CEEF40F84B53B0 /* VoiceMessageRoomPlaybackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3B41C36800DD4558D7BDA7 /* VoiceMessageRoomPlaybackView.swift */; }; 562EFB9AB62B38830D9AA778 /* TimelineMediaFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 933B074F006F8E930DB98B4E /* TimelineMediaFrame.swift */; }; 564910A38858306301C1C21E /* DeactivateAccountScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A1009E4A78F86DA42E1EAF0 /* DeactivateAccountScreenCoordinator.swift */; }; 564BF06B3E93D6DD55F903B2 /* CreateRoomCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C618CA2B6C8758B06C88013C /* CreateRoomCoordinator.swift */; }; @@ -660,7 +662,6 @@ 874FEFB9D4A4AF447E0E086E /* AuthenticationStartScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0F7CCC4A9D1927223F559D5 /* AuthenticationStartScreenViewModelProtocol.swift */; }; 877D3CE8680536DB430DE6A2 /* TimelineItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E48C91C8BE55CAE1A3DBC3BC /* TimelineItemIdentifier.swift */; }; 878070573C7BF19E735707B4 /* RoomTimelineItemProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DE8D25D6A91030175D52A20 /* RoomTimelineItemProperties.swift */; }; - 87B4E59A4467F8EC75F82372 /* VoiceMessageRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70A50C41C5871B4DB905E7E /* VoiceMessageRoomTimelineView.swift */; }; 87CEA3E07B602705BC2D2A20 /* ClientBuilderHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC0CD1CAFD3F8B057F9AEA5 /* ClientBuilderHook.swift */; }; 8810A2A30A68252EBB54EE05 /* HomeScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BC7CA1BC1041E93077BBA1 /* HomeScreenModels.swift */; }; 88356DE7F2AD243AB10C7B7A /* Signposter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 752A0EB49BF5BCEA37EDF7A3 /* Signposter.swift */; }; @@ -828,7 +829,6 @@ A851635B3255C6DC07034A12 /* RoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */; }; A8FA7671948E3DF27F320026 /* BugReportFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7367B3B9A8CAF902220F31D1 /* BugReportFlowCoordinator.swift */; }; A93661C962B12942C08864B6 /* SwiftOGG in Frameworks */ = {isa = PBXBuildFile; productRef = 391D11F92DFC91666AA1503F /* SwiftOGG */; }; - A9482B967FC85DA611514D35 /* VoiceMessageRoomPlaybackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CCD41CD67DB5DA0D436BFE9 /* VoiceMessageRoomPlaybackView.swift */; }; A969147E0EEE0E27EE226570 /* MediaUploadPreviewScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47F29139BC2A804CE5E0757E /* MediaUploadPreviewScreenViewModel.swift */; }; A975D60EA49F6AF73308809F /* RoomMembersListScreenMemberCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC03209FDE8CE0810617BFFF /* RoomMembersListScreenMemberCell.swift */; }; A9A5801D5EE3D4D91F6DDADB /* AnalyticsSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C2527813FDAE23E72A9063 /* AnalyticsSettingsScreenViewModelTests.swift */; }; @@ -1588,7 +1588,6 @@ 3C368CAB3063EF275357ECD4 /* LoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModel.swift; sourceTree = ""; }; 3C3ADF21BE301D0DA48F2A7E /* ShareExtensionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareExtensionView.swift; sourceTree = ""; }; 3C3E67E09FE5A35D73818C39 /* AppLockScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenModels.swift; sourceTree = ""; }; - 3CCD41CD67DB5DA0D436BFE9 /* VoiceMessageRoomPlaybackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomPlaybackView.swift; sourceTree = ""; }; 3CCE3636E3D01477C8B2E9D0 /* ReportContentScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreenModels.swift; sourceTree = ""; }; 3CFD5EB0B0EEA4549FB49784 /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = ""; }; 3D1D4A6D451F43A03CACD01D /* PINTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PINTextField.swift; sourceTree = ""; }; @@ -2029,6 +2028,7 @@ 9C7F7DE62D33C6A26CBFCD72 /* IntegrationTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = IntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9CE3C90E487B255B735D73C8 /* RoomScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModel.swift; sourceTree = ""; }; 9CF1EE0AA78470C674554262 /* PillTextAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillTextAttachment.swift; sourceTree = ""; }; + 9E3B41C36800DD4558D7BDA7 /* VoiceMessageRoomPlaybackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomPlaybackView.swift; sourceTree = ""; }; 9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIConstants.swift; sourceTree = ""; }; 9E8F4D7D61B80EBD5CB92F8A /* KnockedRoomProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockedRoomProxyMock.swift; sourceTree = ""; }; 9EB9BA2F30EB8C33226D8FF1 /* UserSessionStoreMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStoreMock.swift; sourceTree = ""; }; @@ -2146,9 +2146,9 @@ B63B69F9A2BC74DD40DC75C8 /* AdvancedSettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModel.swift; sourceTree = ""; }; B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetails.swift; sourceTree = ""; }; B655A536341D2695158C6664 /* AuthenticationClientBuilderFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationClientBuilderFactory.swift; sourceTree = ""; }; + B6A293D06BAB2B7A17D9314B /* VoiceMessageRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomTimelineView.swift; sourceTree = ""; }; B6E4AB573FAEBB7B853DD04C /* AppHooks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppHooks.swift; sourceTree = ""; }; B6E89E530A8E92EC44301CA1 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = ""; }; - B70A50C41C5871B4DB905E7E /* VoiceMessageRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomTimelineView.swift; sourceTree = ""; }; B73587C2E3CF5998361AE516 /* HomeScreenRoomTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRoomTests.swift; sourceTree = ""; }; B746EFA112532A7B701FB914 /* RoomNotificationSettingsCustomSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsCustomSectionView.swift; sourceTree = ""; }; B7884BD256C091EB511B2EDF /* AppLockSetupPINScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenViewModelProtocol.swift; sourceTree = ""; }; @@ -3302,15 +3302,6 @@ path = Helpers; sourceTree = ""; }; - 3A542DF1C3BB67D829DFDC40 /* VoiceMessages */ = { - isa = PBXGroup; - children = ( - 3CCD41CD67DB5DA0D436BFE9 /* VoiceMessageRoomPlaybackView.swift */, - B70A50C41C5871B4DB905E7E /* VoiceMessageRoomTimelineView.swift */, - ); - path = VoiceMessages; - sourceTree = ""; - }; 3AD37D7DDF9904587601239D /* AppLockScreen */ = { isa = PBXGroup; children = ( @@ -4465,7 +4456,6 @@ F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */, 8C8616254EE40CA8BA5E9BC2 /* VideoRoomTimelineItemContent.swift */, D529B976F8B2AA654D923422 /* VoiceMessageRoomTimelineItem.swift */, - 3A542DF1C3BB67D829DFDC40 /* VoiceMessages */, ); path = Messages; sourceTree = ""; @@ -5307,6 +5297,7 @@ 44ECC9D66400727DFFEE12E8 /* TimelineStartRoomTimelineView.swift */, C9E535B3388755B65C34CD10 /* UnsupportedRoomTimelineView.swift */, ED0CBEAB5F796BEFBAF7BB6A /* VideoRoomTimelineView.swift */, + B6A293D06BAB2B7A17D9314B /* VoiceMessageRoomTimelineView.swift */, ); path = TimelineItemViews; sourceTree = ""; @@ -5829,6 +5820,7 @@ D53FCCE44F96E0BC411A6CF0 /* TimelineSenderAvatarView.swift */, 93C713D124FE915ABF47A6B7 /* TimelineView.swift */, 81F0325E252B057FAEEE1B2D /* TypingIndicatorView.swift */, + 9E3B41C36800DD4558D7BDA7 /* VoiceMessageRoomPlaybackView.swift */, 505AE6F89590187813390D12 /* ItemMenu */, C13DBC8C4A879A5A9C781BBD /* Polls */, B470504BE2DC95FAC94FDD79 /* ReadReceipts */, @@ -7484,9 +7476,9 @@ C405528EB4BBEA93579050EE /* VoiceMessageRecordingButton.swift in Sources */, E0C167D41A48EDB30B447DE3 /* VoiceMessageRecordingComposer.swift in Sources */, 756EA0D663261889EF64E6D4 /* VoiceMessageRecordingView.swift in Sources */, - A9482B967FC85DA611514D35 /* VoiceMessageRoomPlaybackView.swift in Sources */, + 55DF6DEEF2CEEF40F84B53B0 /* VoiceMessageRoomPlaybackView.swift in Sources */, 024E70451A7CD9E4E034D8A9 /* VoiceMessageRoomTimelineItem.swift in Sources */, - 87B4E59A4467F8EC75F82372 /* VoiceMessageRoomTimelineView.swift in Sources */, + 1224084B7E289E0830BA2C54 /* VoiceMessageRoomTimelineView.swift in Sources */, CA12AE0DCD57D49CD96C699A /* WaveformCursorView.swift in Sources */, 63CDC201A5980F304F6D0A1C /* WaveformInteractionModifier.swift in Sources */, B773ACD8881DB18E876D950C /* WaveformSource.swift in Sources */, diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VoiceMessageRoomTimelineView.swift similarity index 100% rename from ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift rename to ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VoiceMessageRoomTimelineView.swift diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift b/ElementX/Sources/Screens/Timeline/View/VoiceMessageRoomPlaybackView.swift similarity index 100% rename from ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift rename to ElementX/Sources/Screens/Timeline/View/VoiceMessageRoomPlaybackView.swift From ea889bedcafca048f9264a9574e9c43ccc96c547 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 11 Dec 2024 12:41:27 +0200 Subject: [PATCH 2/4] Add separate struct for each media events timeline view --- ElementX.xcodeproj/project.pbxproj | 32 +++++ .../AudioMediaEventsTimelineView.swift | 59 ++++++++ .../FileMediaEventsTimelineView.swift | 129 ++++++++++++++++++ .../ImageMediaEventsTimelineView.swift | 111 +++++++++++++++ .../SeparatorMediaEventsTimelineView.swift | 30 ++++ .../VideoMediaEventsTimelineView.swift | 114 ++++++++++++++++ .../VoiceMediaEventsRoomTimelineView.swift | 88 ++++++++++++ .../Sources/GeneratedPreviewTests.swift | 36 +++++ 8 files changed, 599 insertions(+) create mode 100644 ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/AudioMediaEventsTimelineView.swift create mode 100644 ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/FileMediaEventsTimelineView.swift create mode 100644 ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift create mode 100644 ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift create mode 100644 ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift create mode 100644 ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VoiceMediaEventsRoomTimelineView.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 41b3302b55..106e2fc54a 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -58,6 +58,7 @@ 077CB230153E072C94B1E6C3 /* AppAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D65BCC659FD9087E49B3C25 /* AppAppearance.swift */; }; 07CC13C5729C24255348CBBD /* ElementCallWidgetDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 309AD8BAE6437C31BA7157BF /* ElementCallWidgetDriver.swift */; }; 07F6382E29845D235BFA3308 /* DeactivateAccountScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE78CAD0B964C66FD06EF83E /* DeactivateAccountScreenModels.swift */; }; + 08547E55DD3686A84550996D /* SeparatorMediaEventsTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13F354AD441E2FD83DED89AF /* SeparatorMediaEventsTimelineView.swift */; }; 086D01E79C8E8D3F004FAF21 /* AudioPlayerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC9104846487244648D32C6D /* AudioPlayerProtocol.swift */; }; 08CB4BD12CEEDE6AAE4A18DD /* WindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035177BCD8E8308B098AC3C2 /* WindowManager.swift */; }; 095C0ACFC234E0550A6404C5 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FC803282F9268D49F4ABF14 /* AppCoordinator.swift */; }; @@ -331,6 +332,7 @@ 43F35A7E5703D64DB0519C59 /* ServerSelectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD469F7513574341181F7EAA /* ServerSelectionScreen.swift */; }; 440123E29E2F9B001A775BBE /* TimelineItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D505843AB66822EB91F0DF0 /* TimelineItemProxy.swift */; }; 44121202B4A260C98BF615A7 /* RoomMembersListScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5B7A755E985FA14469E86B2 /* RoomMembersListScreenUITests.swift */; }; + 446BCD2D0AE27E0CFD1BDC8F /* ImageMediaEventsTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FF584D757E768EA7776A532 /* ImageMediaEventsTimelineView.swift */; }; 44BDD670FF9095ACE240A3A2 /* VoiceMessageMediaManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC4F10BDD56FA77FEC742333 /* VoiceMessageMediaManagerTests.swift */; }; 44DA28B1E1F9C97C5795F7B3 /* AppLockSetupUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A1BBEF7318CA6B6ACCF4AE /* AppLockSetupUITests.swift */; }; 44F0E1B576C7599DF8022071 /* WysiwygComposer in Frameworks */ = {isa = PBXBuildFile; productRef = CA07D57389DACE18AEB6A5E2 /* WysiwygComposer */; }; @@ -410,6 +412,7 @@ 53C1E7F6A7D6409D89F36ED7 /* AggregatedReactionMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */; }; 53DEF39F0C4DE02E3FC56D91 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 800631D7250B7F93195035F1 /* KeychainAccess */; }; 53F1196F9C69512306A2693F /* TextRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C19F54A0C4FC9AB7ABD583 /* TextRoomTimelineItemContent.swift */; }; + 54915485A721D79B94CE31A4 /* AudioMediaEventsTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A191B05A8E3FE8DADC1588F /* AudioMediaEventsTimelineView.swift */; }; 54AE8860D668AFD96E7E177B /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; }; 54C774874BED4A8FAD1F22FE /* AnalyticsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77B3D4950F1707E66E4A45A /* AnalyticsConfiguration.swift */; }; 5518DA4A6C9B4FC4B497EA9A /* LogViewerScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B795AAAB7B8747FE2FF311 /* LogViewerScreenModels.swift */; }; @@ -442,6 +445,7 @@ 5BC6C4ADFE7F2A795ECDE130 /* SecureBackupKeyBackupScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2D4EEBE8C098BBADD10939 /* SecureBackupKeyBackupScreenCoordinator.swift */; }; 5C02841B2A86327B2C377682 /* NotificationConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C830A64609CBD152F06E0457 /* NotificationConstants.swift */; }; 5C164551F7D26E24F09083D3 /* StaticLocationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C616D90B1E2F033CAA325439 /* StaticLocationScreenViewModelProtocol.swift */; }; + 5C33976A720B64094CBC56B1 /* VideoMediaEventsTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B1FB56520A847DD2532D5C8 /* VideoMediaEventsTimelineView.swift */; }; 5C61810ED7B7CB48346B1B9D /* portrait_test_video.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = E5E7D4EE7CA295E5039FDA21 /* portrait_test_video.mp4 */; }; 5C8804B4F25903516E2DAB81 /* RoomInfoProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A66E8BC8D9AE4A08EFB2DF /* RoomInfoProxy.swift */; }; 5D27B6537591471A42C89027 /* EmoteRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450E04B2A976CC4C8CC1807C /* EmoteRoomTimelineItem.swift */; }; @@ -1119,6 +1123,7 @@ EA01A06EEDFEF4AE7652E5F3 /* NSRegularExpresion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BAC0F6C9644336E9567EE6 /* NSRegularExpresion.swift */; }; EA6613B29BA671F39CE1B1D2 /* ConfirmationDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = B383DCD3DCB19E00FD478A5F /* ConfirmationDialog.swift */; }; EA78A7512AFB1E5451744EB1 /* AppRouteURLParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E461B3C8BBBFCA400B268D14 /* AppRouteURLParserTests.swift */; }; + EA8D941771E762A5D3D7FA0D /* FileMediaEventsTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430C73079A84654BF46A7FF5 /* FileMediaEventsTimelineView.swift */; }; EA974337FA7D040E7C74FE6E /* RoomDetailsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFE1922F39398ABFB36DF3F /* RoomDetailsViewModelTests.swift */; }; EAB3C1F0BC7F671ED8BDF82D /* CompletionSuggestionServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ECF11669EF253E98AA2977A /* CompletionSuggestionServiceProtocol.swift */; }; EAC6FE2CD4F50A43068ADCD8 /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 9573B94B1C86C6DF751AF3FD /* SwiftState */; }; @@ -1162,6 +1167,7 @@ F253AAB4C8F06208173C9C4A /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; }; F255083E18CDBFDF7E640FB1 /* Avatars.swift in Sources */ = {isa = PBXBuildFile; fileRef = C142248014E08E885E323E56 /* Avatars.swift */; }; F2D5C0E1351DA7BD16867629 /* TimelineStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD4823EAB4B4E8BAB4F6B8C /* TimelineStyle.swift */; }; + F36D048546583D5A9A887D3E /* VoiceMediaEventsRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A0394706AE65DBC57E1FE3 /* VoiceMediaEventsRoomTimelineView.swift */; }; F37629BAA5E8F50AAF2A131D /* SoftLogoutScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB7BAD55A4E2B8E5828CD64C /* SoftLogoutScreenViewModel.swift */; }; F38D32C1B0232AAFE6A0822C /* ExtensionLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A8571A8A071FB41778C016 /* ExtensionLogger.swift */; }; F3E2D3F7ACDED65A4E5CD8DE /* RoomMembersListScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF79FB25E2D4BD6F50CE7C9 /* RoomMembersListScreenViewModel.swift */; }; @@ -1352,6 +1358,7 @@ 086C19086DD16E9B38E25954 /* ReportContentViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentViewModelTests.swift; sourceTree = ""; }; 095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProviderProtocol.swift; sourceTree = ""; }; 099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiProviderTests.swift; sourceTree = ""; }; + 0A191B05A8E3FE8DADC1588F /* AudioMediaEventsTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioMediaEventsTimelineView.swift; sourceTree = ""; }; 0A3E77399BD262D301451BF2 /* RoomDetailsEditScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreenCoordinator.swift; sourceTree = ""; }; 0A459AE4B6566B2FA99E86B2 /* TimelineItemBubbledStylerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemBubbledStylerView.swift; sourceTree = ""; }; 0B0E0B55E2EE75AF67029924 /* SwipeToReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeToReplyView.swift; sourceTree = ""; }; @@ -1396,6 +1403,7 @@ 136F80A613B55BDD071DCEA5 /* JoinRoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenModels.swift; sourceTree = ""; }; 13802897C7AFA360EA74C0B0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; 13BE9781699FB510E9263192 /* AppSettingsHook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsHook.swift; sourceTree = ""; }; + 13F354AD441E2FD83DED89AF /* SeparatorMediaEventsTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorMediaEventsTimelineView.swift; sourceTree = ""; }; 1423AB065857FA546444DB15 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = ""; }; 1454CF3AABD242F55C8A2615 /* InviteUsersScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreenModels.swift; sourceTree = ""; }; 1511B1DCECC0DC75EB267328 /* KnockRequestsListScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockRequestsListScreen.swift; sourceTree = ""; }; @@ -1454,6 +1462,7 @@ 1F7C6DDBB5D12F6EF6A3D6E1 /* CollapsibleReactionLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleReactionLayout.swift; sourceTree = ""; }; 1FAF8C2226A57B9AB7446B31 /* AppLockSetupPINScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenCoordinator.swift; sourceTree = ""; }; 1FD51B4D5173F7FC886F5360 /* NoticeRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItemContent.swift; sourceTree = ""; }; + 1FF584D757E768EA7776A532 /* ImageMediaEventsTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageMediaEventsTimelineView.swift; sourceTree = ""; }; 200626E8353AB2729444F991 /* preview_image.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = preview_image.jpg; sourceTree = ""; }; 201305507D7DFD16E544563A /* EmojiLoaderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiLoaderProtocol.swift; sourceTree = ""; }; 203D1ACC20287F8986C959D3 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; @@ -1517,6 +1526,7 @@ 2AE807361805463F5AEDD1CA /* VoiceMessagePreviewComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessagePreviewComposer.swift; sourceTree = ""; }; 2AE83A3DD63BCFBB956FE5CB /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nl; path = nl.lproj/Localizable.stringsdict; sourceTree = ""; }; 2AF715D4FD4710EBB637D661 /* SettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenViewModelProtocol.swift; sourceTree = ""; }; + 2B1FB56520A847DD2532D5C8 /* VideoMediaEventsTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoMediaEventsTimelineView.swift; sourceTree = ""; }; 2BA894BC09972DC45E497D37 /* TimelineInteractionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineInteractionHandler.swift; sourceTree = ""; }; 2BB385E148DE55C85C0A02D6 /* SoftLogoutScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreenModels.swift; sourceTree = ""; }; 2BDB3E65A79779EDA5D33D8A /* AudioPlayerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerState.swift; sourceTree = ""; }; @@ -1623,6 +1633,7 @@ 42236480CF0431535EBE8387 /* CustomLayoutLabelStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomLayoutLabelStyle.swift; sourceTree = ""; }; 42C64A14EE89928207E3B42B /* AnalyticsSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenModels.swift; sourceTree = ""; }; 42C8C368A611B9CB79C7F5FA /* RoomPollsHistoryScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreen.swift; sourceTree = ""; }; + 430C73079A84654BF46A7FF5 /* FileMediaEventsTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileMediaEventsTimelineView.swift; sourceTree = ""; }; 434522ED2BDED08759048077 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; 436A0D98D372B17EAE9AA999 /* GlobalSearchScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSearchScreenModels.swift; sourceTree = ""; }; 43A84EE187D0C772E18A4E39 /* VoiceMessageCacheProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageCacheProtocol.swift; sourceTree = ""; }; @@ -2293,6 +2304,7 @@ D5338450E6783A576B5C16DD /* StickerRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerRoomTimelineView.swift; sourceTree = ""; }; D53FCCE44F96E0BC411A6CF0 /* TimelineSenderAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineSenderAvatarView.swift; sourceTree = ""; }; D54E12B98252F6C527E31FEE /* MediaUploadPreviewScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelProtocol.swift; sourceTree = ""; }; + D5A0394706AE65DBC57E1FE3 /* VoiceMediaEventsRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMediaEventsRoomTimelineView.swift; sourceTree = ""; }; D5B4932E4EFBC8FAC10972CD /* UserProfileScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileScreenCoordinator.swift; sourceTree = ""; }; D5EA0312A6262484AA393AC9 /* CompletionSuggestionServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionSuggestionServiceTests.swift; sourceTree = ""; }; D622EC7898469BB1D0881CDD /* PollFormScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreen.swift; sourceTree = ""; }; @@ -3665,6 +3677,19 @@ path = ItemMenu; sourceTree = ""; }; + 50D53998001EC408A51D6509 /* TimelineViews */ = { + isa = PBXGroup; + children = ( + 0A191B05A8E3FE8DADC1588F /* AudioMediaEventsTimelineView.swift */, + 430C73079A84654BF46A7FF5 /* FileMediaEventsTimelineView.swift */, + 1FF584D757E768EA7776A532 /* ImageMediaEventsTimelineView.swift */, + 13F354AD441E2FD83DED89AF /* SeparatorMediaEventsTimelineView.swift */, + 2B1FB56520A847DD2532D5C8 /* VideoMediaEventsTimelineView.swift */, + D5A0394706AE65DBC57E1FE3 /* VoiceMediaEventsRoomTimelineView.swift */, + ); + path = TimelineViews; + sourceTree = ""; + }; 52AA75722911233E40A3B366 /* Scripts */ = { isa = PBXGroup; children = ( @@ -5423,6 +5448,7 @@ isa = PBXGroup; children = ( 002399C6CB875C4EBB01CBC0 /* MediaEventsTimelineScreen.swift */, + 50D53998001EC408A51D6509 /* TimelineViews */, ); path = View; sourceTree = ""; @@ -6706,6 +6732,7 @@ A6DEC1ADEC8FEEC206A0FA37 /* AttributedStringBuilderProtocol.swift in Sources */, 2DA27D78560D5F79B917E163 /* AudioConverter.swift in Sources */, F3F38062C6CA21CF403C5C90 /* AudioConverterProtocol.swift in Sources */, + 54915485A721D79B94CE31A4 /* AudioMediaEventsTimelineView.swift in Sources */, 46C9F8FE3810A04A005FE16B /* AudioPlayer.swift in Sources */, 086D01E79C8E8D3F004FAF21 /* AudioPlayerProtocol.swift in Sources */, 1471A080552631358D152C18 /* AudioPlayerState.swift in Sources */, @@ -6872,6 +6899,7 @@ 63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */, 36206F74DDEBF9BEAF6A6A1F /* ExtensionLogger.swift in Sources */, 5F06AD3C66884CE793AE6119 /* FileManager.swift in Sources */, + EA8D941771E762A5D3D7FA0D /* FileMediaEventsTimelineView.swift in Sources */, D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */, 37D789F24199B32E3FD1AA7B /* FileRoomTimelineItemContent.swift in Sources */, 64EE9D2CF7AD02EE53983CE1 /* FileRoomTimelineView.swift in Sources */, @@ -6915,6 +6943,7 @@ AADE7C2497A7B55D8BED7BD6 /* IdentityConfirmedScreenViewModelProtocol.swift in Sources */, FFD52DCDA6962055A363CC8F /* IdentityResetHandleSDKMock.swift in Sources */, BA31448FBD9697F8CB9A83CD /* ImageCache.swift in Sources */, + 446BCD2D0AE27E0CFD1BDC8F /* ImageMediaEventsTimelineView.swift in Sources */, 7CD16990BA843BE9ED639129 /* ImageRoomTimelineItem.swift in Sources */, B796A25F282C0A340D1B9C12 /* ImageRoomTimelineItemContent.swift in Sources */, E2D57361B835E4D2230960E6 /* ImageRoomTimelineView.swift in Sources */, @@ -7292,6 +7321,7 @@ DA7E867F5EAFF8E20B2EE3B6 /* SecureBackupScreenModels.swift in Sources */, 7BF368A78E6D9AFD222F25AF /* SecureBackupScreenViewModel.swift in Sources */, AC90434798E7894370E80E66 /* SecureBackupScreenViewModelProtocol.swift in Sources */, + 08547E55DD3686A84550996D /* SeparatorMediaEventsTimelineView.swift in Sources */, 14E99D27628B1A6F0CB46FEA /* SeparatorRoomTimelineItem.swift in Sources */, 5341D48F833E3E30F16FA2A3 /* SeparatorRoomTimelineView.swift in Sources */, 2E8C6672D0EE7D5B1BEDB8E2 /* ServerConfirmationScreen.swift in Sources */, @@ -7460,11 +7490,13 @@ 7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */, 79D57E9AE03A2DC689D14EA2 /* UserSessionStoreMock.swift in Sources */, AC69B6DF15FC451AB2945036 /* UserSessionStoreProtocol.swift in Sources */, + 5C33976A720B64094CBC56B1 /* VideoMediaEventsTimelineView.swift in Sources */, F07D88421A9BC4D03D4A5055 /* VideoRoomTimelineItem.swift in Sources */, 1A83DD22F3E6F76B13B6E2F9 /* VideoRoomTimelineItemContent.swift in Sources */, 2CA61BB208CD82EBDB58CD13 /* VideoRoomTimelineView.swift in Sources */, 6FC10A00D268FCD48B631E37 /* ViewFrameReader.swift in Sources */, EED33AFD9334EFD7398707A6 /* VisualListItem.swift in Sources */, + F36D048546583D5A9A887D3E /* VoiceMediaEventsRoomTimelineView.swift in Sources */, 1318721F4E5F307586D98112 /* VoiceMessageButton.swift in Sources */, 4681820102DAC8BA586357D4 /* VoiceMessageCache.swift in Sources */, 4F2DF6138E87A4B8C2488CA3 /* VoiceMessageCacheProtocol.swift in Sources */, diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/AudioMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/AudioMediaEventsTimelineView.swift new file mode 100644 index 0000000000..0a488c2e17 --- /dev/null +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/AudioMediaEventsTimelineView.swift @@ -0,0 +1,59 @@ +// +// Copyright 2023, 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import SwiftUI + +struct AudioMediaEventsTimelineView: View { + let timelineItem: AudioRoomTimelineItem + + var body: some View { + TimelineStyler(timelineItem: timelineItem) { + MediaFileRoomTimelineContent(timelineItemID: timelineItem.id, + filename: timelineItem.content.filename, + fileSize: timelineItem.content.fileSize, + caption: timelineItem.content.caption, + formattedCaption: timelineItem.content.formattedCaption, + additionalWhitespaces: timelineItem.additionalWhitespaces(), + isAudioFile: true) + .accessibilityLabel(L10n.commonAudio) + } + } +} + +struct AudioMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { + static let viewModel = TimelineViewModel.mock + + static var previews: some View { + VStack(spacing: 20) { + AudioRoomTimelineView(timelineItem: makeItem(filename: "audio.ogg", + fileSize: 2 * 1024 * 1024)) + + AudioRoomTimelineView(timelineItem: makeItem(filename: "Best Song Ever.mp3", + fileSize: 7 * 1024 * 1024, + caption: "This song rocks!")) + } + .environmentObject(viewModel.context) + } + + static func makeItem(filename: String, fileSize: UInt, caption: String? = nil) -> AudioRoomTimelineItem { + .init(id: .randomEvent, + timestamp: .mock, + isOutgoing: false, + isEditable: false, + canBeRepliedTo: true, + isThreaded: false, + sender: .init(id: "Bob"), + content: .init(filename: filename, + caption: caption, + duration: 300, + waveform: nil, + source: nil, + fileSize: fileSize, + contentType: nil)) + } +} diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/FileMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/FileMediaEventsTimelineView.swift new file mode 100644 index 0000000000..8978c927e1 --- /dev/null +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/FileMediaEventsTimelineView.swift @@ -0,0 +1,129 @@ +// +// Copyright 2022-2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Compound +import SwiftUI + +struct FileMediaEventsTimelineView: View { + let timelineItem: FileRoomTimelineItem + + var body: some View { + TimelineStyler(timelineItem: timelineItem) { + MediaFileMediaEventsTimelineContent(timelineItemID: timelineItem.id, + filename: timelineItem.content.filename, + fileSize: timelineItem.content.fileSize, + caption: timelineItem.content.caption, + formattedCaption: timelineItem.content.formattedCaption, + additionalWhitespaces: timelineItem.additionalWhitespaces()) + .accessibilityLabel(L10n.commonFile) + } + } +} + +// MARK: Content + +struct MediaFileMediaEventsTimelineContent: View { + @Environment(\.timelineContext) private var context + + let timelineItemID: TimelineItemIdentifier + let filename: String + let fileSize: UInt? + let caption: String? + let formattedCaption: AttributedString? + let additionalWhitespaces: Int + var isAudioFile = false + + var icon: KeyPath { + isAudioFile ? \.audio : \.attachment + } + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + filePreview + .onTapGesture { + context?.send(viewAction: .mediaTapped(itemID: timelineItemID)) + } + + if let formattedCaption { + FormattedBodyText(attributedString: formattedCaption, + additionalWhitespacesCount: additionalWhitespaces) + } else if let caption { + FormattedBodyText(text: caption, + additionalWhitespacesCount: additionalWhitespaces) + } + } + } + + var filePreview: some View { + Label { + HStack(spacing: 4) { + Text(filename) + .truncationMode(.middle) + + if let fileSize { + Text("(\(fileSize.formatted(.byteCount(style: .file))))") + .layoutPriority(1) // We want the filename to truncate rather than the size. + } + } + .font(.compound.bodyLG) + .foregroundStyle(.compound.textPrimary) + .lineLimit(1) + } icon: { + CompoundIcon(icon, size: .xSmall, relativeTo: .body) + .foregroundColor(.compound.iconPrimary) + .scaledPadding(8) + .background(.compound.iconOnSolidPrimary, in: Circle()) + } + .labelStyle(.custom(spacing: 8, alignment: .center)) + .padding(.horizontal, 4) // Add to the styler's padding of 8, as we use the default insets for the caption. + } +} + +// MARK: - Previews + +struct FileMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { + static let viewModel = TimelineViewModel.mock + + static var previews: some View { + VStack(spacing: 20.0) { + FileMediaEventsTimelineView(timelineItem: makeItem(filename: "document.pdf")) + + FileMediaEventsTimelineView(timelineItem: makeItem(filename: "document.pdf", + fileSize: 3 * 1024 * 1024)) + + FileMediaEventsTimelineView(timelineItem: makeItem(filename: "spreadsheet.xlsx", + fileSize: 17 * 1024, + caption: "The important figures you asked me to send over.")) + + FileMediaEventsTimelineView(timelineItem: makeItem(filename: "document.txt", + fileSize: 456, + caption: "Plain caption", + formattedCaption: "Formatted caption")) + } + .environmentObject(viewModel.context) + } + + static func makeItem(filename: String, + fileSize: UInt? = nil, + caption: String? = nil, + formattedCaption: AttributedString? = nil) -> FileRoomTimelineItem { + .init(id: .randomEvent, + timestamp: .mock, + isOutgoing: false, + isEditable: false, + canBeRepliedTo: true, + isThreaded: false, + sender: .init(id: "Bob"), + content: .init(filename: filename, + caption: caption, + formattedCaption: formattedCaption, + source: nil, + fileSize: fileSize, + thumbnailSource: nil, + contentType: nil)) + } +} diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift new file mode 100644 index 0000000000..2cdfe4104b --- /dev/null +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift @@ -0,0 +1,111 @@ +// +// Copyright 2022-2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import SwiftUI + +struct ImageMediaEventsTimelineView: View { + @Environment(\.timelineContext) private var context + let timelineItem: ImageRoomTimelineItem + + var hasMediaCaption: Bool { timelineItem.content.caption != nil } + + var body: some View { + TimelineStyler(timelineItem: timelineItem) { + VStack(alignment: .leading, spacing: 4) { + loadableImage + .accessibilityElement(children: .ignore) + .accessibilityLabel(L10n.commonImage) + // This clip shape is distinct from the one in the styler as that one + // operates on the entire message so wouldn't round the bottom corners. + .clipShape(RoundedRectangle(cornerRadius: hasMediaCaption ? 6 : 0)) + .onTapGesture { + context?.send(viewAction: .mediaTapped(itemID: timelineItem.id)) + } + + if let attributedCaption = timelineItem.content.formattedCaption { + FormattedBodyText(attributedString: attributedCaption, + additionalWhitespacesCount: timelineItem.additionalWhitespaces(), + boostEmojiSize: true) + } else if let caption = timelineItem.content.caption { + FormattedBodyText(text: caption, + additionalWhitespacesCount: timelineItem.additionalWhitespaces(), + boostEmojiSize: true) + } + } + } + } + + @ViewBuilder + private var loadableImage: some View { + if timelineItem.content.contentType == .gif { + LoadableImage(mediaSource: timelineItem.content.imageInfo.source, + mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), + blurhash: timelineItem.content.blurhash, + size: timelineItem.content.imageInfo.size, + mediaProvider: context?.mediaProvider) { + placeholder + } + .timelineMediaFrame(imageInfo: timelineItem.content.imageInfo) + } else { + LoadableImage(mediaSource: timelineItem.content.thumbnailInfo?.source ?? timelineItem.content.imageInfo.source, + mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), + blurhash: timelineItem.content.blurhash, + size: timelineItem.content.thumbnailInfo?.size ?? timelineItem.content.imageInfo.size, + mediaProvider: context?.mediaProvider) { + placeholder + } + .timelineMediaFrame(imageInfo: timelineItem.content.thumbnailInfo ?? timelineItem.content.imageInfo) + } + } + + private var placeholder: some View { + Rectangle() + .foregroundColor(timelineItem.isOutgoing ? .compound._bgBubbleOutgoing : .compound._bgBubbleIncoming) + .opacity(0.3) + } +} + +struct ImageMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { + static let viewModel = TimelineViewModel.mock + + static var previews: some View { + ScrollView { + VStack(spacing: 20.0) { + ImageMediaEventsTimelineView(timelineItem: makeTimelineItem()) + ImageMediaEventsTimelineView(timelineItem: makeTimelineItem(isEdited: true)) + + // Blur hashed item? + + ImageMediaEventsTimelineView(timelineItem: makeTimelineItem(caption: "This is a great image 😎")) + ImageMediaEventsTimelineView(timelineItem: makeTimelineItem(caption: "This is a great image with a really long multiline caption.", + isEdited: true)) + } + } + .environmentObject(viewModel.context) + .environment(\.timelineContext, viewModel.context) + .previewLayout(.fixed(width: 390, height: 1200)) + .padding(.bottom, 20) + } + + private static func makeTimelineItem(caption: String? = nil, isEdited: Bool = false) -> ImageRoomTimelineItem { + ImageRoomTimelineItem(id: .randomEvent, + timestamp: .mock, + isOutgoing: false, + isEditable: false, + canBeRepliedTo: true, + isThreaded: false, + sender: .init(id: "Bob"), + content: .init(filename: "image.jpg", + caption: caption, + imageInfo: .mockImage, + thumbnailInfo: .mockThumbnail, + blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW", + contentType: .jpeg), + properties: .init(isEdited: isEdited)) + } +} diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift new file mode 100644 index 0000000000..5ccb38cbb7 --- /dev/null +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift @@ -0,0 +1,30 @@ +// +// Copyright 2022-2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import SwiftUI + +struct SeparatorMediaEventsTimelineView: View { + let timelineItem: SeparatorRoomTimelineItem + + var body: some View { + Text(timelineItem.timestamp.formatted(date: .complete, time: .omitted)) + .font(.compound.bodySMSemibold) + .foregroundColor(.compound.textPrimary) + .frame(maxWidth: .infinity) + .multilineTextAlignment(.center) + .padding(.horizontal, 36.0) + .padding(.vertical, 8.0) + } +} + +struct SeparatorMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { + static var previews: some View { + let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Separator")), + timestamp: .mock) + SeparatorMediaEventsTimelineView(timelineItem: item) + } +} diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift new file mode 100644 index 0000000000..f66db26a3b --- /dev/null +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift @@ -0,0 +1,114 @@ +// +// Copyright 2022-2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Compound +import SwiftUI + +struct VideoMediaEventsTimelineView: View { + @Environment(\.timelineContext) private var context + let timelineItem: VideoRoomTimelineItem + + private var hasMediaCaption: Bool { timelineItem.content.caption != nil } + + var body: some View { + TimelineStyler(timelineItem: timelineItem) { + VStack(alignment: .leading, spacing: 4) { + thumbnail + .timelineMediaFrame(imageInfo: timelineItem.content.thumbnailInfo) + .accessibilityElement(children: .ignore) + .accessibilityLabel(L10n.commonVideo) + // This clip shape is distinct from the one in the styler as that one + // operates on the entire message so wouldn't round the bottom corners. + .clipShape(RoundedRectangle(cornerRadius: hasMediaCaption ? 6 : 0)) + .onTapGesture { + context?.send(viewAction: .mediaTapped(itemID: timelineItem.id)) + } + + if let attributedCaption = timelineItem.content.formattedCaption { + FormattedBodyText(attributedString: attributedCaption, + additionalWhitespacesCount: timelineItem.additionalWhitespaces(), + boostEmojiSize: true) + } else if let caption = timelineItem.content.caption { + FormattedBodyText(text: caption, + additionalWhitespacesCount: timelineItem.additionalWhitespaces(), + boostEmojiSize: true) + } + } + } + } + + @ViewBuilder + var thumbnail: some View { + if let thumbnailSource = timelineItem.content.thumbnailInfo?.source { + LoadableImage(mediaSource: thumbnailSource, + mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), + blurhash: timelineItem.content.blurhash, + size: timelineItem.content.thumbnailInfo?.size, + mediaProvider: context?.mediaProvider) { imageView in + imageView + .overlay { playIcon } + } placeholder: { + placeholder + } + } else { + playIcon + } + } + + var playIcon: some View { + Image(systemName: "play.circle.fill") + .resizable() + .frame(width: 50, height: 50) + .background(.ultraThinMaterial, in: Circle()) + .foregroundColor(.white) + } + + var placeholder: some View { + Rectangle() + .foregroundColor(timelineItem.isOutgoing ? .compound._bgBubbleOutgoing : .compound._bgBubbleIncoming) + .opacity(0.3) + } +} + +struct VideoMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { + static let viewModel = TimelineViewModel.mock + + static var previews: some View { + ScrollView { + VStack(spacing: 20.0) { + VideoMediaEventsTimelineView(timelineItem: makeTimelineItem()) + VideoMediaEventsTimelineView(timelineItem: makeTimelineItem(isEdited: true)) + + // Blurhash item? + + VideoMediaEventsTimelineView(timelineItem: makeTimelineItem(caption: "This is a great image 😎")) + VideoMediaEventsTimelineView(timelineItem: makeTimelineItem(caption: "This is a great image with a really long multiline caption", + isEdited: true)) + } + } + .environmentObject(viewModel.context) + .environment(\.timelineContext, viewModel.context) + .previewLayout(.fixed(width: 390, height: 975)) + .padding(.bottom, 20) + } + + private static func makeTimelineItem(caption: String? = nil, isEdited: Bool = false) -> VideoRoomTimelineItem { + VideoRoomTimelineItem(id: .randomEvent, + timestamp: .mock, + isOutgoing: false, + isEditable: false, + canBeRepliedTo: true, + isThreaded: false, + sender: .init(id: "Bob"), + content: .init(filename: "video.mp4", + caption: caption, + videoInfo: .mockVideo, + thumbnailInfo: .mockVideoThumbnail, + blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"), + properties: .init(isEdited: isEdited)) + } +} diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VoiceMediaEventsRoomTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VoiceMediaEventsRoomTimelineView.swift new file mode 100644 index 0000000000..0538c76321 --- /dev/null +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VoiceMediaEventsRoomTimelineView.swift @@ -0,0 +1,88 @@ +// +// Copyright 2023, 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import SwiftUI + +struct VoiceMessageMediaEventsTimelineView: View { + @Environment(\.timelineContext) private var context + private let timelineItem: VoiceMessageRoomTimelineItem + private let playerState: AudioPlayerState + @State private var resumePlaybackAfterScrubbing = false + + init(timelineItem: VoiceMessageRoomTimelineItem, playerState: AudioPlayerState) { + self.timelineItem = timelineItem + self.playerState = playerState + } + + var body: some View { + TimelineStyler(timelineItem: timelineItem) { + VoiceMessageRoomPlaybackView(playerState: playerState, + onPlayPause: onPlaybackPlayPause, + onSeek: { onPlaybackSeek($0) }, + onScrubbing: { onPlaybackScrubbing($0) }) + .fixedSize(horizontal: false, vertical: true) + .frame(maxWidth: 400) + } + } + + private func onPlaybackPlayPause() { + context?.send(viewAction: .handleAudioPlayerAction(.playPause(itemID: timelineItem.id))) + } + + private func onPlaybackSeek(_ progress: Double) { + context?.send(viewAction: .handleAudioPlayerAction(.seek(itemID: timelineItem.id, progress: progress))) + } + + private func onPlaybackScrubbing(_ dragging: Bool) { + if dragging { + if playerState.playbackState == .playing { + resumePlaybackAfterScrubbing = true + context?.send(viewAction: .handleAudioPlayerAction(.playPause(itemID: timelineItem.id))) + } + } else { + if resumePlaybackAfterScrubbing { + context?.send(viewAction: .handleAudioPlayerAction(.playPause(itemID: timelineItem.id))) + resumePlaybackAfterScrubbing = false + } + } + } +} + +struct VoiceMessageMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { + static let viewModel = TimelineViewModel.mock + static let timelineItemIdentifier = TimelineItemIdentifier.randomEvent + static let voiceRoomTimelineItem = VoiceMessageRoomTimelineItem(id: timelineItemIdentifier, + timestamp: .mock, + isOutgoing: false, + isEditable: false, + canBeRepliedTo: true, + isThreaded: false, + sender: .init(id: "Bob"), + content: .init(filename: "audio.ogg", + duration: 300, + waveform: EstimatedWaveform.mockWaveform, + source: nil, + fileSize: nil, + contentType: nil)) + + static let playerState = AudioPlayerState(id: .timelineItemIdentifier(timelineItemIdentifier), + title: L10n.commonVoiceMessage, + duration: 10.0, + waveform: EstimatedWaveform.mockWaveform, + progress: 0.4) + + static var previews: some View { + body.environmentObject(viewModel.context) + .previewDisplayName("Bubble") + } + + static var body: some View { + VoiceMessageMediaEventsTimelineView(timelineItem: voiceRoomTimelineItem, playerState: playerState) + .fixedSize(horizontal: false, vertical: true) + } +} diff --git a/PreviewTests/Sources/GeneratedPreviewTests.swift b/PreviewTests/Sources/GeneratedPreviewTests.swift index b7c803082e..4dc11560a7 100644 --- a/PreviewTests/Sources/GeneratedPreviewTests.swift +++ b/PreviewTests/Sources/GeneratedPreviewTests.swift @@ -53,6 +53,12 @@ extension PreviewTests { } } + func test_audioMediaEventsTimelineView() { + for preview in AudioMediaEventsTimelineView_Previews._allPreviews { + assertSnapshots(matching: preview) + } + } + func test_audioRoomTimelineView() { for preview in AudioRoomTimelineView_Previews._allPreviews { assertSnapshots(matching: preview) @@ -179,6 +185,12 @@ extension PreviewTests { } } + func test_fileMediaEventsTimelineView() { + for preview in FileMediaEventsTimelineView_Previews._allPreviews { + assertSnapshots(matching: preview) + } + } + func test_fileRoomTimelineView() { for preview in FileRoomTimelineView_Previews._allPreviews { assertSnapshots(matching: preview) @@ -281,6 +293,12 @@ extension PreviewTests { } } + func test_imageMediaEventsTimelineView() { + for preview in ImageMediaEventsTimelineView_Previews._allPreviews { + assertSnapshots(matching: preview) + } + } + func test_imageRoomTimelineView() { for preview in ImageRoomTimelineView_Previews._allPreviews { assertSnapshots(matching: preview) @@ -767,6 +785,12 @@ extension PreviewTests { } } + func test_separatorMediaEventsTimelineView() { + for preview in SeparatorMediaEventsTimelineView_Previews._allPreviews { + assertSnapshots(matching: preview) + } + } + func test_separatorRoomTimelineView() { for preview in SeparatorRoomTimelineView_Previews._allPreviews { assertSnapshots(matching: preview) @@ -995,6 +1019,12 @@ extension PreviewTests { } } + func test_videoMediaEventsTimelineView() { + for preview in VideoMediaEventsTimelineView_Previews._allPreviews { + assertSnapshots(matching: preview) + } + } + func test_videoRoomTimelineView() { for preview in VideoRoomTimelineView_Previews._allPreviews { assertSnapshots(matching: preview) @@ -1013,6 +1043,12 @@ extension PreviewTests { } } + func test_voiceMessageMediaEventsTimelineView() { + for preview in VoiceMessageMediaEventsTimelineView_Previews._allPreviews { + assertSnapshots(matching: preview) + } + } + func test_voiceMessagePreviewComposer() { for preview in VoiceMessagePreviewComposer_Previews._allPreviews { assertSnapshots(matching: preview) From 693792916e6fd2b3a3c87ede8a18b900883d52c7 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 11 Dec 2024 17:40:45 +0200 Subject: [PATCH 3/4] Add support for all the different media gallery message types and get the files section working. --- ElementX.xcodeproj/project.pbxproj | 12 -- .../SwiftUI/Layout/TimelineMediaFrame.swift | 5 + .../MediaEventsTimelineScreenModels.swift | 2 + .../MediaEventsTimelineScreenViewModel.swift | 6 + .../View/MediaEventsTimelineScreen.swift | 147 ++++++++---------- .../AudioMediaEventsTimelineView.swift | 59 ------- .../FileMediaEventsTimelineView.swift | 129 --------------- .../ImageMediaEventsTimelineView.swift | 44 +----- .../SeparatorMediaEventsTimelineView.swift | 19 ++- .../VideoMediaEventsTimelineView.swift | 33 +--- .../VoiceMediaEventsRoomTimelineView.swift | 88 ----------- .../Sources/GeneratedPreviewTests.swift | 18 --- ...geMediaEventsTimelineView-iPad-en-GB.1.png | 3 + ...eMediaEventsTimelineView-iPad-pseudo.1.png | 3 + ...iaEventsTimelineView-iPhone-16-en-GB.1.png | 3 + ...aEventsTimelineView-iPhone-16-pseudo.1.png | 3 + ...orMediaEventsTimelineView-iPad-en-GB.1.png | 3 + ...rMediaEventsTimelineView-iPad-pseudo.1.png | 3 + ...iaEventsTimelineView-iPhone-16-en-GB.1.png | 3 + ...aEventsTimelineView-iPhone-16-pseudo.1.png | 3 + ...eoMediaEventsTimelineView-iPad-en-GB.1.png | 3 + ...oMediaEventsTimelineView-iPad-pseudo.1.png | 3 + ...iaEventsTimelineView-iPhone-16-en-GB.1.png | 3 + ...aEventsTimelineView-iPhone-16-pseudo.1.png | 3 + 24 files changed, 141 insertions(+), 457 deletions(-) delete mode 100644 ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/AudioMediaEventsTimelineView.swift delete mode 100644 ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/FileMediaEventsTimelineView.swift delete mode 100644 ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VoiceMediaEventsRoomTimelineView.swift create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPad-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPad-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPhone-16-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPhone-16-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-pseudo.1.png diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 106e2fc54a..3faa37e368 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -412,7 +412,6 @@ 53C1E7F6A7D6409D89F36ED7 /* AggregatedReactionMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */; }; 53DEF39F0C4DE02E3FC56D91 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 800631D7250B7F93195035F1 /* KeychainAccess */; }; 53F1196F9C69512306A2693F /* TextRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C19F54A0C4FC9AB7ABD583 /* TextRoomTimelineItemContent.swift */; }; - 54915485A721D79B94CE31A4 /* AudioMediaEventsTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A191B05A8E3FE8DADC1588F /* AudioMediaEventsTimelineView.swift */; }; 54AE8860D668AFD96E7E177B /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; }; 54C774874BED4A8FAD1F22FE /* AnalyticsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77B3D4950F1707E66E4A45A /* AnalyticsConfiguration.swift */; }; 5518DA4A6C9B4FC4B497EA9A /* LogViewerScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B795AAAB7B8747FE2FF311 /* LogViewerScreenModels.swift */; }; @@ -1123,7 +1122,6 @@ EA01A06EEDFEF4AE7652E5F3 /* NSRegularExpresion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BAC0F6C9644336E9567EE6 /* NSRegularExpresion.swift */; }; EA6613B29BA671F39CE1B1D2 /* ConfirmationDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = B383DCD3DCB19E00FD478A5F /* ConfirmationDialog.swift */; }; EA78A7512AFB1E5451744EB1 /* AppRouteURLParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E461B3C8BBBFCA400B268D14 /* AppRouteURLParserTests.swift */; }; - EA8D941771E762A5D3D7FA0D /* FileMediaEventsTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430C73079A84654BF46A7FF5 /* FileMediaEventsTimelineView.swift */; }; EA974337FA7D040E7C74FE6E /* RoomDetailsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFE1922F39398ABFB36DF3F /* RoomDetailsViewModelTests.swift */; }; EAB3C1F0BC7F671ED8BDF82D /* CompletionSuggestionServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ECF11669EF253E98AA2977A /* CompletionSuggestionServiceProtocol.swift */; }; EAC6FE2CD4F50A43068ADCD8 /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 9573B94B1C86C6DF751AF3FD /* SwiftState */; }; @@ -1167,7 +1165,6 @@ F253AAB4C8F06208173C9C4A /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; }; F255083E18CDBFDF7E640FB1 /* Avatars.swift in Sources */ = {isa = PBXBuildFile; fileRef = C142248014E08E885E323E56 /* Avatars.swift */; }; F2D5C0E1351DA7BD16867629 /* TimelineStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD4823EAB4B4E8BAB4F6B8C /* TimelineStyle.swift */; }; - F36D048546583D5A9A887D3E /* VoiceMediaEventsRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A0394706AE65DBC57E1FE3 /* VoiceMediaEventsRoomTimelineView.swift */; }; F37629BAA5E8F50AAF2A131D /* SoftLogoutScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB7BAD55A4E2B8E5828CD64C /* SoftLogoutScreenViewModel.swift */; }; F38D32C1B0232AAFE6A0822C /* ExtensionLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A8571A8A071FB41778C016 /* ExtensionLogger.swift */; }; F3E2D3F7ACDED65A4E5CD8DE /* RoomMembersListScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF79FB25E2D4BD6F50CE7C9 /* RoomMembersListScreenViewModel.swift */; }; @@ -1358,7 +1355,6 @@ 086C19086DD16E9B38E25954 /* ReportContentViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentViewModelTests.swift; sourceTree = ""; }; 095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProviderProtocol.swift; sourceTree = ""; }; 099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiProviderTests.swift; sourceTree = ""; }; - 0A191B05A8E3FE8DADC1588F /* AudioMediaEventsTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioMediaEventsTimelineView.swift; sourceTree = ""; }; 0A3E77399BD262D301451BF2 /* RoomDetailsEditScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreenCoordinator.swift; sourceTree = ""; }; 0A459AE4B6566B2FA99E86B2 /* TimelineItemBubbledStylerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemBubbledStylerView.swift; sourceTree = ""; }; 0B0E0B55E2EE75AF67029924 /* SwipeToReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeToReplyView.swift; sourceTree = ""; }; @@ -1633,7 +1629,6 @@ 42236480CF0431535EBE8387 /* CustomLayoutLabelStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomLayoutLabelStyle.swift; sourceTree = ""; }; 42C64A14EE89928207E3B42B /* AnalyticsSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenModels.swift; sourceTree = ""; }; 42C8C368A611B9CB79C7F5FA /* RoomPollsHistoryScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreen.swift; sourceTree = ""; }; - 430C73079A84654BF46A7FF5 /* FileMediaEventsTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileMediaEventsTimelineView.swift; sourceTree = ""; }; 434522ED2BDED08759048077 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; 436A0D98D372B17EAE9AA999 /* GlobalSearchScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSearchScreenModels.swift; sourceTree = ""; }; 43A84EE187D0C772E18A4E39 /* VoiceMessageCacheProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageCacheProtocol.swift; sourceTree = ""; }; @@ -2304,7 +2299,6 @@ D5338450E6783A576B5C16DD /* StickerRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerRoomTimelineView.swift; sourceTree = ""; }; D53FCCE44F96E0BC411A6CF0 /* TimelineSenderAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineSenderAvatarView.swift; sourceTree = ""; }; D54E12B98252F6C527E31FEE /* MediaUploadPreviewScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelProtocol.swift; sourceTree = ""; }; - D5A0394706AE65DBC57E1FE3 /* VoiceMediaEventsRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMediaEventsRoomTimelineView.swift; sourceTree = ""; }; D5B4932E4EFBC8FAC10972CD /* UserProfileScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileScreenCoordinator.swift; sourceTree = ""; }; D5EA0312A6262484AA393AC9 /* CompletionSuggestionServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionSuggestionServiceTests.swift; sourceTree = ""; }; D622EC7898469BB1D0881CDD /* PollFormScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreen.swift; sourceTree = ""; }; @@ -3680,12 +3674,9 @@ 50D53998001EC408A51D6509 /* TimelineViews */ = { isa = PBXGroup; children = ( - 0A191B05A8E3FE8DADC1588F /* AudioMediaEventsTimelineView.swift */, - 430C73079A84654BF46A7FF5 /* FileMediaEventsTimelineView.swift */, 1FF584D757E768EA7776A532 /* ImageMediaEventsTimelineView.swift */, 13F354AD441E2FD83DED89AF /* SeparatorMediaEventsTimelineView.swift */, 2B1FB56520A847DD2532D5C8 /* VideoMediaEventsTimelineView.swift */, - D5A0394706AE65DBC57E1FE3 /* VoiceMediaEventsRoomTimelineView.swift */, ); path = TimelineViews; sourceTree = ""; @@ -6732,7 +6723,6 @@ A6DEC1ADEC8FEEC206A0FA37 /* AttributedStringBuilderProtocol.swift in Sources */, 2DA27D78560D5F79B917E163 /* AudioConverter.swift in Sources */, F3F38062C6CA21CF403C5C90 /* AudioConverterProtocol.swift in Sources */, - 54915485A721D79B94CE31A4 /* AudioMediaEventsTimelineView.swift in Sources */, 46C9F8FE3810A04A005FE16B /* AudioPlayer.swift in Sources */, 086D01E79C8E8D3F004FAF21 /* AudioPlayerProtocol.swift in Sources */, 1471A080552631358D152C18 /* AudioPlayerState.swift in Sources */, @@ -6899,7 +6889,6 @@ 63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */, 36206F74DDEBF9BEAF6A6A1F /* ExtensionLogger.swift in Sources */, 5F06AD3C66884CE793AE6119 /* FileManager.swift in Sources */, - EA8D941771E762A5D3D7FA0D /* FileMediaEventsTimelineView.swift in Sources */, D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */, 37D789F24199B32E3FD1AA7B /* FileRoomTimelineItemContent.swift in Sources */, 64EE9D2CF7AD02EE53983CE1 /* FileRoomTimelineView.swift in Sources */, @@ -7496,7 +7485,6 @@ 2CA61BB208CD82EBDB58CD13 /* VideoRoomTimelineView.swift in Sources */, 6FC10A00D268FCD48B631E37 /* ViewFrameReader.swift in Sources */, EED33AFD9334EFD7398707A6 /* VisualListItem.swift in Sources */, - F36D048546583D5A9A887D3E /* VoiceMediaEventsRoomTimelineView.swift in Sources */, 1318721F4E5F307586D98112 /* VoiceMessageButton.swift in Sources */, 4681820102DAC8BA586357D4 /* VoiceMessageCache.swift in Sources */, 4F2DF6138E87A4B8C2488CA3 /* VoiceMessageCacheProtocol.swift in Sources */, diff --git a/ElementX/Sources/Other/SwiftUI/Layout/TimelineMediaFrame.swift b/ElementX/Sources/Other/SwiftUI/Layout/TimelineMediaFrame.swift index c715f5fc93..cd5bb6399c 100644 --- a/ElementX/Sources/Other/SwiftUI/Layout/TimelineMediaFrame.swift +++ b/ElementX/Sources/Other/SwiftUI/Layout/TimelineMediaFrame.swift @@ -29,4 +29,9 @@ extension View { } } } + + @ViewBuilder + func mediaGalleryTimelineAspectRatio(imageInfo: ImageInfoProxy?) -> some View { + aspectRatio(imageInfo?.aspectRatio, contentMode: .fill) + } } diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenModels.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenModels.swift index 7e073f35b9..c0135fd720 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenModels.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenModels.swift @@ -24,6 +24,8 @@ struct MediaEventsTimelineScreenViewState: BindableState { var isBackPaginating = false var groups = [MediaEventsTimelineGroup]() + var activeTimelineContextProvider: (() -> TimelineViewModel.Context)! + var bindings: MediaEventsTimelineScreenViewStateBindings } diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenViewModel.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenViewModel.swift index 3a62adb045..9a7556a8f6 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenViewModel.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenViewModel.swift @@ -42,6 +42,12 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType super.init(initialViewState: .init(bindings: .init(screenMode: screenMode)), mediaProvider: mediaProvider) + state.activeTimelineContextProvider = { [weak self] in + guard let self else { fatalError() } + + return activeTimelineViewModel.context + } + mediaTimelineViewModel.context.$viewState.sink { [weak self] timelineViewState in guard let self, state.bindings.screenMode == .media else { return diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/MediaEventsTimelineScreen.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/MediaEventsTimelineScreen.swift index 9978baaf6f..cadec47f3c 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/MediaEventsTimelineScreen.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/MediaEventsTimelineScreen.swift @@ -12,7 +12,7 @@ struct MediaEventsTimelineScreen: View { @ObservedObject var context: MediaEventsTimelineScreenViewModel.Context var body: some View { - content + mainContent .navigationBarTitleDisplayMode(.inline) .background(.compound.bgCanvasDefault) // Doesn't play well with the transformed scrollView @@ -31,6 +31,8 @@ struct MediaEventsTimelineScreen: View { } } .timelineMediaQuickLook(viewModel: $context.mediaPreviewViewModel) + .environmentObject(context.viewState.activeTimelineContextProvider()) + .environment(\.timelineContext, context.viewState.activeTimelineContextProvider()) } // The scale effects do the following: @@ -39,32 +41,16 @@ struct MediaEventsTimelineScreen: View { // * flip the grid vertically to counteract the scroll view // but also horizontally to preserve the corect item order // * flip the items on both axes have them render correctly - @ViewBuilder - private var content: some View { + private var mainContent: some View { ScrollView { Group { - let columns = [GridItem(.adaptive(minimum: 80, maximum: 150), spacing: 1)] - LazyVGrid(columns: columns, alignment: .center, spacing: 1) { - ForEach(context.viewState.groups) { group in - Section(footer: sectionFooterForGroup(group)) { - ForEach(group.items) { item in - Button { - context.send(viewAction: .tappedItem(item)) - } label: { - Color.clear // Let the image aspect fill in place - .aspectRatio(1, contentMode: .fill) - .overlay { - viewForTimelineItem(item) - } - .clipped() - .scaleEffect(.init(width: -1, height: -1)) - } - } - } - } + switch context.viewState.bindings.screenMode { + case .media: + mediaContent + case .files: + filesContent } - .scaleEffect(.init(width: -1, height: 1)) - + header } } @@ -74,6 +60,53 @@ struct MediaEventsTimelineScreen: View { } } + @ViewBuilder + private var mediaContent: some View { + let columns = [GridItem(.adaptive(minimum: 80, maximum: 150), spacing: 1)] + LazyVGrid(columns: columns, alignment: .center, spacing: 1) { + ForEach(context.viewState.groups) { group in + Section(footer: SeparatorMediaEventsTimelineView(group: group)) { + ForEach(group.items) { item in + Button { + context.send(viewAction: .tappedItem(item)) + } label: { + Color.clear // Let the image aspect fill in place + .aspectRatio(1, contentMode: .fill) + .overlay { + viewForTimelineItem(item) + } + .clipped() + .scaleEffect(.init(width: -1, height: -1)) + } + } + } + } + } + .scaleEffect(.init(width: -1, height: 1)) + } + + @ViewBuilder + private var filesContent: some View { + LazyVStack(alignment: .center, spacing: 16) { + ForEach(context.viewState.groups) { group in + Section(footer: SeparatorMediaEventsTimelineView(group: group)) { + ForEach(group.items) { item in + viewForTimelineItem(item) + .scaleEffect(.init(width: 1, height: -1)) + .onTapGesture { + context.send(viewAction: .tappedItem(item)) + } + .accessibilityActions { + Button(L10n.actionShow) { + context.send(viewAction: .tappedItem(item)) + } + } + } + } + } + } + } + private var header: some View { // Needs to be wrapped in a LazyStack otherwise appearance calls don't trigger LazyVStack(spacing: 0) { @@ -93,71 +126,25 @@ struct MediaEventsTimelineScreen: View { } } - @ViewBuilder - func sectionFooterForGroup(_ group: MediaEventsTimelineGroup) -> some View { - Text(group.title) - .font(.compound.bodySMSemibold) - .foregroundColor(.compound.textPrimary) - .frame(alignment: .center) - .scaleEffect(.init(width: -1, height: -1)) - .padding(.vertical, 16) - } - @ViewBuilder func viewForTimelineItem(_ item: RoomTimelineItemViewState) -> some View { switch item.type { case .image(let timelineItem): - #warning("Make this work for gifs") - LoadableImage(mediaSource: timelineItem.content.thumbnailInfo?.source ?? timelineItem.content.imageInfo.source, - mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), - blurhash: timelineItem.content.blurhash, - size: timelineItem.content.thumbnailInfo?.size ?? timelineItem.content.imageInfo.size, - mediaProvider: context.mediaProvider) { - placeholder - } - .mediaItemAspectRatio(imageInfo: timelineItem.content.thumbnailInfo ?? timelineItem.content.imageInfo) + ImageMediaEventsTimelineView(timelineItem: timelineItem) case .video(let timelineItem): - if let thumbnailSource = timelineItem.content.thumbnailInfo?.source { - LoadableImage(mediaSource: thumbnailSource, - mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), - blurhash: timelineItem.content.blurhash, - size: timelineItem.content.thumbnailInfo?.size, - mediaProvider: context.mediaProvider) { imageView in - imageView - .overlay { playIcon } - } placeholder: { - placeholder - } - .mediaItemAspectRatio(imageInfo: timelineItem.content.thumbnailInfo) - } else { - playIcon - } + VideoMediaEventsTimelineView(timelineItem: timelineItem) + case .file(let timelineItem): + FileRoomTimelineView(timelineItem: timelineItem) + case .audio(let timelineItem): + AudioRoomTimelineView(timelineItem: timelineItem) + case .voice(let timelineItem): + let defaultPlayerState = AudioPlayerState(id: .timelineItemIdentifier(timelineItem.id), title: L10n.commonVoiceMessage, duration: 0) + let playerState = context.viewState.activeTimelineContextProvider().viewState.audioPlayerStateProvider?(timelineItem.id) ?? defaultPlayerState + VoiceMessageRoomTimelineView(timelineItem: timelineItem, playerState: playerState) default: EmptyView() } } - - private var playIcon: some View { - Image(systemName: "play.circle.fill") - .resizable() - .frame(width: 50, height: 50) - .background(.ultraThinMaterial, in: Circle()) - .foregroundColor(.white) - } - - private var placeholder: some View { - Rectangle() - .foregroundColor(.compound._bgBubbleIncoming) - .opacity(0.3) - } -} - -extension View { - /// Constrains the max height of a media item in the timeline, whilst preserving its aspect ratio. - @ViewBuilder - func mediaItemAspectRatio(imageInfo: ImageInfoProxy?) -> some View { - aspectRatio(imageInfo?.aspectRatio, contentMode: .fill) - } } // MARK: - Previews diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/AudioMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/AudioMediaEventsTimelineView.swift deleted file mode 100644 index 0a488c2e17..0000000000 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/AudioMediaEventsTimelineView.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// Copyright 2023, 2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only -// Please see LICENSE in the repository root for full details. -// - -import Foundation -import SwiftUI - -struct AudioMediaEventsTimelineView: View { - let timelineItem: AudioRoomTimelineItem - - var body: some View { - TimelineStyler(timelineItem: timelineItem) { - MediaFileRoomTimelineContent(timelineItemID: timelineItem.id, - filename: timelineItem.content.filename, - fileSize: timelineItem.content.fileSize, - caption: timelineItem.content.caption, - formattedCaption: timelineItem.content.formattedCaption, - additionalWhitespaces: timelineItem.additionalWhitespaces(), - isAudioFile: true) - .accessibilityLabel(L10n.commonAudio) - } - } -} - -struct AudioMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { - static let viewModel = TimelineViewModel.mock - - static var previews: some View { - VStack(spacing: 20) { - AudioRoomTimelineView(timelineItem: makeItem(filename: "audio.ogg", - fileSize: 2 * 1024 * 1024)) - - AudioRoomTimelineView(timelineItem: makeItem(filename: "Best Song Ever.mp3", - fileSize: 7 * 1024 * 1024, - caption: "This song rocks!")) - } - .environmentObject(viewModel.context) - } - - static func makeItem(filename: String, fileSize: UInt, caption: String? = nil) -> AudioRoomTimelineItem { - .init(id: .randomEvent, - timestamp: .mock, - isOutgoing: false, - isEditable: false, - canBeRepliedTo: true, - isThreaded: false, - sender: .init(id: "Bob"), - content: .init(filename: filename, - caption: caption, - duration: 300, - waveform: nil, - source: nil, - fileSize: fileSize, - contentType: nil)) - } -} diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/FileMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/FileMediaEventsTimelineView.swift deleted file mode 100644 index 8978c927e1..0000000000 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/FileMediaEventsTimelineView.swift +++ /dev/null @@ -1,129 +0,0 @@ -// -// Copyright 2022-2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only -// Please see LICENSE in the repository root for full details. -// - -import Compound -import SwiftUI - -struct FileMediaEventsTimelineView: View { - let timelineItem: FileRoomTimelineItem - - var body: some View { - TimelineStyler(timelineItem: timelineItem) { - MediaFileMediaEventsTimelineContent(timelineItemID: timelineItem.id, - filename: timelineItem.content.filename, - fileSize: timelineItem.content.fileSize, - caption: timelineItem.content.caption, - formattedCaption: timelineItem.content.formattedCaption, - additionalWhitespaces: timelineItem.additionalWhitespaces()) - .accessibilityLabel(L10n.commonFile) - } - } -} - -// MARK: Content - -struct MediaFileMediaEventsTimelineContent: View { - @Environment(\.timelineContext) private var context - - let timelineItemID: TimelineItemIdentifier - let filename: String - let fileSize: UInt? - let caption: String? - let formattedCaption: AttributedString? - let additionalWhitespaces: Int - var isAudioFile = false - - var icon: KeyPath { - isAudioFile ? \.audio : \.attachment - } - - var body: some View { - VStack(alignment: .leading, spacing: 8) { - filePreview - .onTapGesture { - context?.send(viewAction: .mediaTapped(itemID: timelineItemID)) - } - - if let formattedCaption { - FormattedBodyText(attributedString: formattedCaption, - additionalWhitespacesCount: additionalWhitespaces) - } else if let caption { - FormattedBodyText(text: caption, - additionalWhitespacesCount: additionalWhitespaces) - } - } - } - - var filePreview: some View { - Label { - HStack(spacing: 4) { - Text(filename) - .truncationMode(.middle) - - if let fileSize { - Text("(\(fileSize.formatted(.byteCount(style: .file))))") - .layoutPriority(1) // We want the filename to truncate rather than the size. - } - } - .font(.compound.bodyLG) - .foregroundStyle(.compound.textPrimary) - .lineLimit(1) - } icon: { - CompoundIcon(icon, size: .xSmall, relativeTo: .body) - .foregroundColor(.compound.iconPrimary) - .scaledPadding(8) - .background(.compound.iconOnSolidPrimary, in: Circle()) - } - .labelStyle(.custom(spacing: 8, alignment: .center)) - .padding(.horizontal, 4) // Add to the styler's padding of 8, as we use the default insets for the caption. - } -} - -// MARK: - Previews - -struct FileMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { - static let viewModel = TimelineViewModel.mock - - static var previews: some View { - VStack(spacing: 20.0) { - FileMediaEventsTimelineView(timelineItem: makeItem(filename: "document.pdf")) - - FileMediaEventsTimelineView(timelineItem: makeItem(filename: "document.pdf", - fileSize: 3 * 1024 * 1024)) - - FileMediaEventsTimelineView(timelineItem: makeItem(filename: "spreadsheet.xlsx", - fileSize: 17 * 1024, - caption: "The important figures you asked me to send over.")) - - FileMediaEventsTimelineView(timelineItem: makeItem(filename: "document.txt", - fileSize: 456, - caption: "Plain caption", - formattedCaption: "Formatted caption")) - } - .environmentObject(viewModel.context) - } - - static func makeItem(filename: String, - fileSize: UInt? = nil, - caption: String? = nil, - formattedCaption: AttributedString? = nil) -> FileRoomTimelineItem { - .init(id: .randomEvent, - timestamp: .mock, - isOutgoing: false, - isEditable: false, - canBeRepliedTo: true, - isThreaded: false, - sender: .init(id: "Bob"), - content: .init(filename: filename, - caption: caption, - formattedCaption: formattedCaption, - source: nil, - fileSize: fileSize, - thumbnailSource: nil, - contentType: nil)) - } -} diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift index 2cdfe4104b..92f792f924 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift @@ -5,39 +5,17 @@ // Please see LICENSE in the repository root for full details. // -import Foundation +import Compound import SwiftUI struct ImageMediaEventsTimelineView: View { @Environment(\.timelineContext) private var context let timelineItem: ImageRoomTimelineItem - var hasMediaCaption: Bool { timelineItem.content.caption != nil } - var body: some View { - TimelineStyler(timelineItem: timelineItem) { - VStack(alignment: .leading, spacing: 4) { - loadableImage - .accessibilityElement(children: .ignore) - .accessibilityLabel(L10n.commonImage) - // This clip shape is distinct from the one in the styler as that one - // operates on the entire message so wouldn't round the bottom corners. - .clipShape(RoundedRectangle(cornerRadius: hasMediaCaption ? 6 : 0)) - .onTapGesture { - context?.send(viewAction: .mediaTapped(itemID: timelineItem.id)) - } - - if let attributedCaption = timelineItem.content.formattedCaption { - FormattedBodyText(attributedString: attributedCaption, - additionalWhitespacesCount: timelineItem.additionalWhitespaces(), - boostEmojiSize: true) - } else if let caption = timelineItem.content.caption { - FormattedBodyText(text: caption, - additionalWhitespacesCount: timelineItem.additionalWhitespaces(), - boostEmojiSize: true) - } - } - } + loadableImage + .accessibilityElement(children: .ignore) + .accessibilityLabel(L10n.commonImage) } @ViewBuilder @@ -50,7 +28,7 @@ struct ImageMediaEventsTimelineView: View { mediaProvider: context?.mediaProvider) { placeholder } - .timelineMediaFrame(imageInfo: timelineItem.content.imageInfo) + .mediaGalleryTimelineAspectRatio(imageInfo: timelineItem.content.imageInfo) } else { LoadableImage(mediaSource: timelineItem.content.thumbnailInfo?.source ?? timelineItem.content.imageInfo.source, mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), @@ -59,13 +37,13 @@ struct ImageMediaEventsTimelineView: View { mediaProvider: context?.mediaProvider) { placeholder } - .timelineMediaFrame(imageInfo: timelineItem.content.thumbnailInfo ?? timelineItem.content.imageInfo) + .mediaGalleryTimelineAspectRatio(imageInfo: timelineItem.content.thumbnailInfo ?? timelineItem.content.imageInfo) } } private var placeholder: some View { Rectangle() - .foregroundColor(timelineItem.isOutgoing ? .compound._bgBubbleOutgoing : .compound._bgBubbleIncoming) + .foregroundColor(.compound._bgBubbleIncoming) .opacity(0.3) } } @@ -77,19 +55,11 @@ struct ImageMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { ScrollView { VStack(spacing: 20.0) { ImageMediaEventsTimelineView(timelineItem: makeTimelineItem()) - ImageMediaEventsTimelineView(timelineItem: makeTimelineItem(isEdited: true)) - - // Blur hashed item? - - ImageMediaEventsTimelineView(timelineItem: makeTimelineItem(caption: "This is a great image 😎")) - ImageMediaEventsTimelineView(timelineItem: makeTimelineItem(caption: "This is a great image with a really long multiline caption.", - isEdited: true)) } } .environmentObject(viewModel.context) .environment(\.timelineContext, viewModel.context) .previewLayout(.fixed(width: 390, height: 1200)) - .padding(.bottom, 20) } private static func makeTimelineItem(caption: String? = nil, isEdited: Bool = false) -> ImageRoomTimelineItem { diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift index 5ccb38cbb7..80c85daead 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift @@ -5,19 +5,20 @@ // Please see LICENSE in the repository root for full details. // +import Compound import SwiftUI struct SeparatorMediaEventsTimelineView: View { - let timelineItem: SeparatorRoomTimelineItem + let group: MediaEventsTimelineGroup var body: some View { - Text(timelineItem.timestamp.formatted(date: .complete, time: .omitted)) + Text(group.title) .font(.compound.bodySMSemibold) .foregroundColor(.compound.textPrimary) - .frame(maxWidth: .infinity) - .multilineTextAlignment(.center) - .padding(.horizontal, 36.0) - .padding(.vertical, 8.0) + .frame(alignment: .center) + .padding(.vertical, 16) + // Couldn't figure out how to flip it where it's used instead. + .scaleEffect(.init(width: -1, height: -1)) } } @@ -25,6 +26,10 @@ struct SeparatorMediaEventsTimelineView_Previews: PreviewProvider, TestablePrevi static var previews: some View { let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Separator")), timestamp: .mock) - SeparatorMediaEventsTimelineView(timelineItem: item) + + SeparatorMediaEventsTimelineView(group: .init(id: item.id.uniqueID.id, + title: "Group", + items: [])) + .scaleEffect(.init(width: -1, height: -1)) } } diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift index f66db26a3b..9d3eaeac19 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift @@ -12,33 +12,11 @@ struct VideoMediaEventsTimelineView: View { @Environment(\.timelineContext) private var context let timelineItem: VideoRoomTimelineItem - private var hasMediaCaption: Bool { timelineItem.content.caption != nil } - var body: some View { - TimelineStyler(timelineItem: timelineItem) { - VStack(alignment: .leading, spacing: 4) { - thumbnail - .timelineMediaFrame(imageInfo: timelineItem.content.thumbnailInfo) - .accessibilityElement(children: .ignore) - .accessibilityLabel(L10n.commonVideo) - // This clip shape is distinct from the one in the styler as that one - // operates on the entire message so wouldn't round the bottom corners. - .clipShape(RoundedRectangle(cornerRadius: hasMediaCaption ? 6 : 0)) - .onTapGesture { - context?.send(viewAction: .mediaTapped(itemID: timelineItem.id)) - } - - if let attributedCaption = timelineItem.content.formattedCaption { - FormattedBodyText(attributedString: attributedCaption, - additionalWhitespacesCount: timelineItem.additionalWhitespaces(), - boostEmojiSize: true) - } else if let caption = timelineItem.content.caption { - FormattedBodyText(text: caption, - additionalWhitespacesCount: timelineItem.additionalWhitespaces(), - boostEmojiSize: true) - } - } - } + thumbnail + .timelineMediaFrame(imageInfo: timelineItem.content.thumbnailInfo) + .accessibilityElement(children: .ignore) + .accessibilityLabel(L10n.commonVideo) } @ViewBuilder @@ -54,6 +32,7 @@ struct VideoMediaEventsTimelineView: View { } placeholder: { placeholder } + .mediaGalleryTimelineAspectRatio(imageInfo: timelineItem.content.thumbnailInfo) } else { playIcon } @@ -69,7 +48,7 @@ struct VideoMediaEventsTimelineView: View { var placeholder: some View { Rectangle() - .foregroundColor(timelineItem.isOutgoing ? .compound._bgBubbleOutgoing : .compound._bgBubbleIncoming) + .foregroundColor(.compound._bgBubbleIncoming) .opacity(0.3) } } diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VoiceMediaEventsRoomTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VoiceMediaEventsRoomTimelineView.swift deleted file mode 100644 index 0538c76321..0000000000 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VoiceMediaEventsRoomTimelineView.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// Copyright 2023, 2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only -// Please see LICENSE in the repository root for full details. -// - -import Foundation -import SwiftUI - -struct VoiceMessageMediaEventsTimelineView: View { - @Environment(\.timelineContext) private var context - private let timelineItem: VoiceMessageRoomTimelineItem - private let playerState: AudioPlayerState - @State private var resumePlaybackAfterScrubbing = false - - init(timelineItem: VoiceMessageRoomTimelineItem, playerState: AudioPlayerState) { - self.timelineItem = timelineItem - self.playerState = playerState - } - - var body: some View { - TimelineStyler(timelineItem: timelineItem) { - VoiceMessageRoomPlaybackView(playerState: playerState, - onPlayPause: onPlaybackPlayPause, - onSeek: { onPlaybackSeek($0) }, - onScrubbing: { onPlaybackScrubbing($0) }) - .fixedSize(horizontal: false, vertical: true) - .frame(maxWidth: 400) - } - } - - private func onPlaybackPlayPause() { - context?.send(viewAction: .handleAudioPlayerAction(.playPause(itemID: timelineItem.id))) - } - - private func onPlaybackSeek(_ progress: Double) { - context?.send(viewAction: .handleAudioPlayerAction(.seek(itemID: timelineItem.id, progress: progress))) - } - - private func onPlaybackScrubbing(_ dragging: Bool) { - if dragging { - if playerState.playbackState == .playing { - resumePlaybackAfterScrubbing = true - context?.send(viewAction: .handleAudioPlayerAction(.playPause(itemID: timelineItem.id))) - } - } else { - if resumePlaybackAfterScrubbing { - context?.send(viewAction: .handleAudioPlayerAction(.playPause(itemID: timelineItem.id))) - resumePlaybackAfterScrubbing = false - } - } - } -} - -struct VoiceMessageMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { - static let viewModel = TimelineViewModel.mock - static let timelineItemIdentifier = TimelineItemIdentifier.randomEvent - static let voiceRoomTimelineItem = VoiceMessageRoomTimelineItem(id: timelineItemIdentifier, - timestamp: .mock, - isOutgoing: false, - isEditable: false, - canBeRepliedTo: true, - isThreaded: false, - sender: .init(id: "Bob"), - content: .init(filename: "audio.ogg", - duration: 300, - waveform: EstimatedWaveform.mockWaveform, - source: nil, - fileSize: nil, - contentType: nil)) - - static let playerState = AudioPlayerState(id: .timelineItemIdentifier(timelineItemIdentifier), - title: L10n.commonVoiceMessage, - duration: 10.0, - waveform: EstimatedWaveform.mockWaveform, - progress: 0.4) - - static var previews: some View { - body.environmentObject(viewModel.context) - .previewDisplayName("Bubble") - } - - static var body: some View { - VoiceMessageMediaEventsTimelineView(timelineItem: voiceRoomTimelineItem, playerState: playerState) - .fixedSize(horizontal: false, vertical: true) - } -} diff --git a/PreviewTests/Sources/GeneratedPreviewTests.swift b/PreviewTests/Sources/GeneratedPreviewTests.swift index 4dc11560a7..048c6e2c17 100644 --- a/PreviewTests/Sources/GeneratedPreviewTests.swift +++ b/PreviewTests/Sources/GeneratedPreviewTests.swift @@ -53,12 +53,6 @@ extension PreviewTests { } } - func test_audioMediaEventsTimelineView() { - for preview in AudioMediaEventsTimelineView_Previews._allPreviews { - assertSnapshots(matching: preview) - } - } - func test_audioRoomTimelineView() { for preview in AudioRoomTimelineView_Previews._allPreviews { assertSnapshots(matching: preview) @@ -185,12 +179,6 @@ extension PreviewTests { } } - func test_fileMediaEventsTimelineView() { - for preview in FileMediaEventsTimelineView_Previews._allPreviews { - assertSnapshots(matching: preview) - } - } - func test_fileRoomTimelineView() { for preview in FileRoomTimelineView_Previews._allPreviews { assertSnapshots(matching: preview) @@ -1043,12 +1031,6 @@ extension PreviewTests { } } - func test_voiceMessageMediaEventsTimelineView() { - for preview in VoiceMessageMediaEventsTimelineView_Previews._allPreviews { - assertSnapshots(matching: preview) - } - } - func test_voiceMessagePreviewComposer() { for preview in VoiceMessagePreviewComposer_Previews._allPreviews { assertSnapshots(matching: preview) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-en-GB.1.png new file mode 100644 index 0000000000..f308ac5a27 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af5a709979036acc38ba3da66993eaae76bca058c1f1fe253086fc6e965fc540 +size 3656483 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-pseudo.1.png new file mode 100644 index 0000000000..f308ac5a27 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af5a709979036acc38ba3da66993eaae76bca058c1f1fe253086fc6e965fc540 +size 3656483 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-en-GB.1.png new file mode 100644 index 0000000000..fee988d7fa --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c15fa98d9c47dd9259dd78ee7c79192454f35bfd247fbc85b0fe6537fac097b7 +size 606522 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-pseudo.1.png new file mode 100644 index 0000000000..fee988d7fa --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c15fa98d9c47dd9259dd78ee7c79192454f35bfd247fbc85b0fe6537fac097b7 +size 606522 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPad-en-GB.1.png new file mode 100644 index 0000000000..42f8aeaf1e --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPad-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f106042c8e2bd09f7dd7c2e500d9cd51f795eb84e59b038b532ca8ab86ae3e3 +size 68347 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPad-pseudo.1.png new file mode 100644 index 0000000000..42f8aeaf1e --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPad-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f106042c8e2bd09f7dd7c2e500d9cd51f795eb84e59b038b532ca8ab86ae3e3 +size 68347 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPhone-16-en-GB.1.png new file mode 100644 index 0000000000..d4026a22ed --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPhone-16-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0a1a4493ccddb0149b095ab2eaa7ca14043436d80584a5244db7d9899977fe0 +size 27936 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPhone-16-pseudo.1.png new file mode 100644 index 0000000000..d4026a22ed --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_separatorMediaEventsTimelineView-iPhone-16-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0a1a4493ccddb0149b095ab2eaa7ca14043436d80584a5244db7d9899977fe0 +size 27936 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-en-GB.1.png new file mode 100644 index 0000000000..da43be8ebe --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9de636d41f1e279124802b6e53f8dc88615978c5cc6e63361ae38306af42ca6d +size 6012798 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-pseudo.1.png new file mode 100644 index 0000000000..da43be8ebe --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9de636d41f1e279124802b6e53f8dc88615978c5cc6e63361ae38306af42ca6d +size 6012798 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-en-GB.1.png new file mode 100644 index 0000000000..afc2106f65 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a8e3dd430ba8e96bfcb51da01bd2ddc89ae53c3205f760d2c0d1758fcfd5498 +size 2991956 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-pseudo.1.png new file mode 100644 index 0000000000..afc2106f65 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a8e3dd430ba8e96bfcb51da01bd2ddc89ae53c3205f760d2c0d1758fcfd5498 +size 2991956 From 184c7f14218038c9789062dc29ae8d713a75ed13 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 12 Dec 2024 09:24:28 +0200 Subject: [PATCH 4/4] Address PR comments, be more reasonable about snapshots / fix them. --- .../SwiftUI/Layout/TimelineMediaFrame.swift | 5 --- .../View/MediaEventsTimelineScreen.swift | 12 +++++-- .../ImageMediaEventsTimelineView.swift | 30 +++++++++--------- .../SeparatorMediaEventsTimelineView.swift | 3 -- .../VideoMediaEventsTimelineView.swift | 31 +++++-------------- ...geMediaEventsTimelineView-iPad-en-GB.1.png | 4 +-- ...eMediaEventsTimelineView-iPad-pseudo.1.png | 4 +-- ...iaEventsTimelineView-iPhone-16-en-GB.1.png | 4 +-- ...aEventsTimelineView-iPhone-16-pseudo.1.png | 4 +-- ...aEventsTimelineScreen-iPad-en-GB.Files.png | 4 +-- ...aEventsTimelineScreen-iPad-en-GB.Media.png | 4 +-- ...EventsTimelineScreen-iPad-pseudo.Files.png | 4 +-- ...EventsTimelineScreen-iPad-pseudo.Media.png | 4 +-- ...tsTimelineScreen-iPhone-16-en-GB.Files.png | 4 +-- ...tsTimelineScreen-iPhone-16-en-GB.Media.png | 4 +-- ...sTimelineScreen-iPhone-16-pseudo.Files.png | 4 +-- ...sTimelineScreen-iPhone-16-pseudo.Media.png | 4 +-- ...eoMediaEventsTimelineView-iPad-en-GB.1.png | 4 +-- ...oMediaEventsTimelineView-iPad-pseudo.1.png | 4 +-- ...iaEventsTimelineView-iPhone-16-en-GB.1.png | 4 +-- ...aEventsTimelineView-iPhone-16-pseudo.1.png | 4 +-- 21 files changed, 66 insertions(+), 79 deletions(-) diff --git a/ElementX/Sources/Other/SwiftUI/Layout/TimelineMediaFrame.swift b/ElementX/Sources/Other/SwiftUI/Layout/TimelineMediaFrame.swift index cd5bb6399c..c715f5fc93 100644 --- a/ElementX/Sources/Other/SwiftUI/Layout/TimelineMediaFrame.swift +++ b/ElementX/Sources/Other/SwiftUI/Layout/TimelineMediaFrame.swift @@ -29,9 +29,4 @@ extension View { } } } - - @ViewBuilder - func mediaGalleryTimelineAspectRatio(imageInfo: ImageInfoProxy?) -> some View { - aspectRatio(imageInfo?.aspectRatio, contentMode: .fill) - } } diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/MediaEventsTimelineScreen.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/MediaEventsTimelineScreen.swift index cadec47f3c..6403d5593d 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/MediaEventsTimelineScreen.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/MediaEventsTimelineScreen.swift @@ -65,7 +65,7 @@ struct MediaEventsTimelineScreen: View { let columns = [GridItem(.adaptive(minimum: 80, maximum: 150), spacing: 1)] LazyVGrid(columns: columns, alignment: .center, spacing: 1) { ForEach(context.viewState.groups) { group in - Section(footer: SeparatorMediaEventsTimelineView(group: group)) { + Section { ForEach(group.items) { item in Button { context.send(viewAction: .tappedItem(item)) @@ -79,6 +79,10 @@ struct MediaEventsTimelineScreen: View { .scaleEffect(.init(width: -1, height: -1)) } } + } footer: { + // Use a footer as the header because the scrollView is flipped + SeparatorMediaEventsTimelineView(group: group) + .scaleEffect(.init(width: -1, height: -1)) } } } @@ -89,7 +93,7 @@ struct MediaEventsTimelineScreen: View { private var filesContent: some View { LazyVStack(alignment: .center, spacing: 16) { ForEach(context.viewState.groups) { group in - Section(footer: SeparatorMediaEventsTimelineView(group: group)) { + Section { ForEach(group.items) { item in viewForTimelineItem(item) .scaleEffect(.init(width: 1, height: -1)) @@ -102,6 +106,10 @@ struct MediaEventsTimelineScreen: View { } } } + } footer: { + // Use a footer as the header because the scrollView is flipped + SeparatorMediaEventsTimelineView(group: group) + .scaleEffect(.init(width: 1, height: -1)) } } } diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift index 92f792f924..0b940429a1 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift @@ -43,26 +43,31 @@ struct ImageMediaEventsTimelineView: View { private var placeholder: some View { Rectangle() - .foregroundColor(.compound._bgBubbleIncoming) + .foregroundColor(.compound.bgSubtleSecondary) .opacity(0.3) } } +private extension View { + @ViewBuilder + func mediaGalleryTimelineAspectRatio(imageInfo: ImageInfoProxy?) -> some View { + aspectRatio(imageInfo?.aspectRatio, contentMode: .fill) + } +} + struct ImageMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { static let viewModel = TimelineViewModel.mock static var previews: some View { - ScrollView { - VStack(spacing: 20.0) { - ImageMediaEventsTimelineView(timelineItem: makeTimelineItem()) - } - } - .environmentObject(viewModel.context) - .environment(\.timelineContext, viewModel.context) - .previewLayout(.fixed(width: 390, height: 1200)) + ImageMediaEventsTimelineView(timelineItem: makeTimelineItem()) + .frame(width: 100, height: 100) + .environmentObject(viewModel.context) + .environment(\.timelineContext, viewModel.context) + .previewLayout(.sizeThatFits) + .background(.black) } - private static func makeTimelineItem(caption: String? = nil, isEdited: Bool = false) -> ImageRoomTimelineItem { + private static func makeTimelineItem() -> ImageRoomTimelineItem { ImageRoomTimelineItem(id: .randomEvent, timestamp: .mock, isOutgoing: false, @@ -71,11 +76,8 @@ struct ImageMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { isThreaded: false, sender: .init(id: "Bob"), content: .init(filename: "image.jpg", - caption: caption, imageInfo: .mockImage, thumbnailInfo: .mockThumbnail, - blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW", - contentType: .jpeg), - properties: .init(isEdited: isEdited)) + contentType: .jpeg)) } } diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift index 80c85daead..bbe66129c3 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift @@ -17,8 +17,6 @@ struct SeparatorMediaEventsTimelineView: View { .foregroundColor(.compound.textPrimary) .frame(alignment: .center) .padding(.vertical, 16) - // Couldn't figure out how to flip it where it's used instead. - .scaleEffect(.init(width: -1, height: -1)) } } @@ -30,6 +28,5 @@ struct SeparatorMediaEventsTimelineView_Previews: PreviewProvider, TestablePrevi SeparatorMediaEventsTimelineView(group: .init(id: item.id.uniqueID.id, title: "Group", items: [])) - .scaleEffect(.init(width: -1, height: -1)) } } diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift index 9d3eaeac19..17e1d65ffc 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift @@ -14,7 +14,6 @@ struct VideoMediaEventsTimelineView: View { var body: some View { thumbnail - .timelineMediaFrame(imageInfo: timelineItem.content.thumbnailInfo) .accessibilityElement(children: .ignore) .accessibilityLabel(L10n.commonVideo) } @@ -32,7 +31,6 @@ struct VideoMediaEventsTimelineView: View { } placeholder: { placeholder } - .mediaGalleryTimelineAspectRatio(imageInfo: timelineItem.content.thumbnailInfo) } else { playIcon } @@ -48,7 +46,7 @@ struct VideoMediaEventsTimelineView: View { var placeholder: some View { Rectangle() - .foregroundColor(.compound._bgBubbleIncoming) + .foregroundColor(.compound.bgSubtleSecondary) .opacity(0.3) } } @@ -57,22 +55,12 @@ struct VideoMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { static let viewModel = TimelineViewModel.mock static var previews: some View { - ScrollView { - VStack(spacing: 20.0) { - VideoMediaEventsTimelineView(timelineItem: makeTimelineItem()) - VideoMediaEventsTimelineView(timelineItem: makeTimelineItem(isEdited: true)) - - // Blurhash item? - - VideoMediaEventsTimelineView(timelineItem: makeTimelineItem(caption: "This is a great image 😎")) - VideoMediaEventsTimelineView(timelineItem: makeTimelineItem(caption: "This is a great image with a really long multiline caption", - isEdited: true)) - } - } - .environmentObject(viewModel.context) - .environment(\.timelineContext, viewModel.context) - .previewLayout(.fixed(width: 390, height: 975)) - .padding(.bottom, 20) + VideoMediaEventsTimelineView(timelineItem: makeTimelineItem()) + .frame(width: 100, height: 100) + .environmentObject(viewModel.context) + .environment(\.timelineContext, viewModel.context) + .previewLayout(.sizeThatFits) + .background(.black) } private static func makeTimelineItem(caption: String? = nil, isEdited: Bool = false) -> VideoRoomTimelineItem { @@ -84,10 +72,7 @@ struct VideoMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { isThreaded: false, sender: .init(id: "Bob"), content: .init(filename: "video.mp4", - caption: caption, videoInfo: .mockVideo, - thumbnailInfo: .mockVideoThumbnail, - blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"), - properties: .init(isEdited: isEdited)) + thumbnailInfo: .mockVideoThumbnail)) } } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-en-GB.1.png index f308ac5a27..c9143a2f51 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af5a709979036acc38ba3da66993eaae76bca058c1f1fe253086fc6e965fc540 -size 3656483 +oid sha256:3e66d65ca577aa31e708a19e92ee3686b73a2fc9179c9dbad65c67c3a41a9ede +size 113146 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-pseudo.1.png index f308ac5a27..c9143a2f51 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af5a709979036acc38ba3da66993eaae76bca058c1f1fe253086fc6e965fc540 -size 3656483 +oid sha256:3e66d65ca577aa31e708a19e92ee3686b73a2fc9179c9dbad65c67c3a41a9ede +size 113146 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-en-GB.1.png index fee988d7fa..0159d51e80 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c15fa98d9c47dd9259dd78ee7c79192454f35bfd247fbc85b0fe6537fac097b7 -size 606522 +oid sha256:64e70e20286ebc6bbf25f0d93e4043b7113284a41ce96ab701c61a83b249ac18 +size 98920 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-pseudo.1.png index fee988d7fa..0159d51e80 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageMediaEventsTimelineView-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c15fa98d9c47dd9259dd78ee7c79192454f35bfd247fbc85b0fe6537fac097b7 -size 606522 +oid sha256:64e70e20286ebc6bbf25f0d93e4043b7113284a41ce96ab701c61a83b249ac18 +size 98920 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Files.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Files.png index 1419663c16..d751f5bf47 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Files.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Files.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:472cdd02918531822503fbfb067e11ae24b8ad7ee510efa8b23d2dd17be920d1 -size 84907 +oid sha256:1efe8d7219056e410fdb3a3ca386198526a91e096c9ae4c80f7e38947f6d29d2 +size 169520 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Media.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Media.png index 577dce050e..9da05449f2 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Media.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-en-GB.Media.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99cb5be30a32dd9b4d29be2c7c2788256b9a4afadd73b3a1ee9b1d8bb25a67d9 -size 726687 +oid sha256:22b56f65c9574b55043aa8a7593065a075b253268bfb6e1cf4d5588375efcf98 +size 723194 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Files.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Files.png index 6b23f3b399..123a798deb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Files.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Files.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5882b637b51ce4e8d59dcec3779b2ebb37ba1f4db9f04d0a1cab50161b2baa71 -size 92195 +oid sha256:5746225afcd33ffd7a2f0a8beb05e31e005dcfd0ded7cde8398b180c77dbb8a5 +size 178967 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Media.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Media.png index 04f8a35216..80b1ddbfaf 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Media.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPad-pseudo.Media.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:135f21259bdf8324ef4455b75f185988e22b2ed25d289aba19ee3f3e83ba7e88 -size 733767 +oid sha256:fc265e69d1938da9a7550fa049e4456d06c88aec5bf007613b2c2b896186d993 +size 730597 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Files.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Files.png index 869a39990d..92588a9076 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Files.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Files.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71159e727f3bb47533674c0fcd05128e6ff561ba9d54ab30ba14bc40af2875a3 -size 38785 +oid sha256:7109b26c659e7525c559bf8336127c84464f079b47f51f68e343e7e77eeb2947 +size 114655 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Media.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Media.png index 9e1f09fcdd..b76e3f248d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Media.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-en-GB.Media.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc96fc9a2ed0d4c6ddce7a4d84243f157f11f70d4d339bbd6fa59de874e26d0a -size 808526 +oid sha256:86b5bf6112dde6e9de73e5e2b9268346f023bd959f4a07fde51ccaaf4ea6f31e +size 803264 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.Files.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.Files.png index 00461b9aea..28a941e36e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.Files.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.Files.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28bb922273d38dea5b8444e16f2df180cdd73550f318112fa83919ef9b5db90c -size 42692 +oid sha256:4512d75693b003d50ae96a549a5d896bb26ab80435c9e4dc80650ef33e61a294 +size 120654 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.Media.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.Media.png index a08137f92a..b3dc42eb11 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.Media.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaEventsTimelineScreen-iPhone-16-pseudo.Media.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8581f27e004fae35ea17368cc70aa20b8add4cf2fe222275b5385152f9dd04e2 -size 814086 +oid sha256:b26928f0fb0f4e536909ecac7166a0e1cc6606dfeddb0e6b373a0fdd8ba94fac +size 809016 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-en-GB.1.png index da43be8ebe..becc54bb49 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9de636d41f1e279124802b6e53f8dc88615978c5cc6e63361ae38306af42ca6d -size 6012798 +oid sha256:463ae69de9a5919702d8d4561ad18f87d5cf32461b5e8fbd015f38710dc9cbfa +size 110192 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-pseudo.1.png index da43be8ebe..becc54bb49 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9de636d41f1e279124802b6e53f8dc88615978c5cc6e63361ae38306af42ca6d -size 6012798 +oid sha256:463ae69de9a5919702d8d4561ad18f87d5cf32461b5e8fbd015f38710dc9cbfa +size 110192 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-en-GB.1.png index afc2106f65..2504793176 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a8e3dd430ba8e96bfcb51da01bd2ddc89ae53c3205f760d2c0d1758fcfd5498 -size 2991956 +oid sha256:e8327696e387d06deb4a44651894a5ffee31a024093e1dfbebb99f704601991e +size 100670 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-pseudo.1.png index afc2106f65..2504793176 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoMediaEventsTimelineView-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a8e3dd430ba8e96bfcb51da01bd2ddc89ae53c3205f760d2c0d1758fcfd5498 -size 2991956 +oid sha256:e8327696e387d06deb4a44651894a5ffee31a024093e1dfbebb99f704601991e +size 100670