diff --git a/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift b/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift index ad3a4fb75..c8702156d 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift @@ -204,7 +204,7 @@ private struct LoadableImageContent: View { var messageBubbleWithActions: some View { messageBubble - .onTapGesture { - context.send(viewAction: .itemTapped(itemID: timelineItem.id)) - } - // We need a tap gesture before this long one so that it doesn't - // steal away the gestures from the scroll view .longPressWithFeedback { context.send(viewAction: .displayTimelineItemMenu(itemID: timelineItem.id)) } @@ -396,6 +391,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview } } .environmentObject(viewModel.context) + .environment(\.timelineContext, viewModel.context) } static var replies: some View { @@ -427,6 +423,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview groupStyle: .single)) } .environmentObject(viewModel.context) + .environment(\.timelineContext, viewModel.context) } static var threads: some View { @@ -434,6 +431,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview MockTimelineContent(isThreaded: true) } .environmentObject(viewModel.context) + .environment(\.timelineContext, viewModel.context) } static var pinned: some View { @@ -441,6 +439,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview MockTimelineContent(isPinned: true) } .environmentObject(viewModelWithPins.context) + .environment(\.timelineContext, viewModel.context) } static var encryptionAuthenticity: some View { @@ -523,6 +522,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview waveform: EstimatedWaveform.mockWaveform)) } .environmentObject(viewModel.context) + .environment(\.timelineContext, viewModel.context) } } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift index 7c9fb5c8b..dd0ef0882 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift @@ -13,7 +13,8 @@ struct AudioRoomTimelineView: View { var body: some View { TimelineStyler(timelineItem: timelineItem) { - MediaFileRoomTimelineContent(filename: timelineItem.content.filename, + MediaFileRoomTimelineContent(timelineItemID: timelineItem.id, + filename: timelineItem.content.filename, fileSize: timelineItem.content.fileSize, caption: timelineItem.content.caption, formattedCaption: timelineItem.content.formattedCaption, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallNotificationRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallNotificationRoomTimelineView.swift index c584b8a34..e4a8c93b0 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallNotificationRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallNotificationRoomTimelineView.swift @@ -10,7 +10,7 @@ import Foundation import SwiftUI struct CallNotificationRoomTimelineView: View { - @EnvironmentObject private var context: TimelineViewModel.Context + @Environment(\.timelineContext) private var context let timelineItem: CallNotificationRoomTimelineItem @@ -20,7 +20,7 @@ struct CallNotificationRoomTimelineView: View { name: timelineItem.sender.displayName ?? timelineItem.sender.id, contentID: timelineItem.sender.id, avatarSize: .user(on: .timeline), - mediaProvider: context.mediaProvider) + mediaProvider: context?.mediaProvider) .accessibilityHidden(true) VStack(alignment: .leading, spacing: 0) { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift index b2f2e5d8d..3bb13c658 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift @@ -13,7 +13,8 @@ struct FileRoomTimelineView: View { var body: some View { TimelineStyler(timelineItem: timelineItem) { - MediaFileRoomTimelineContent(filename: timelineItem.content.filename, + MediaFileRoomTimelineContent(timelineItemID: timelineItem.id, + filename: timelineItem.content.filename, fileSize: timelineItem.content.fileSize, caption: timelineItem.content.caption, formattedCaption: timelineItem.content.formattedCaption, @@ -26,6 +27,9 @@ struct FileRoomTimelineView: View { // MARK: Content struct MediaFileRoomTimelineContent: View { + @Environment(\.timelineContext) private var context + + let timelineItemID: TimelineItemIdentifier let filename: String let fileSize: UInt? let caption: String? @@ -40,6 +44,9 @@ struct MediaFileRoomTimelineContent: View { var body: some View { VStack(alignment: .leading, spacing: 8) { filePreview + .onTapGesture { + context?.send(viewAction: .mediaTapped(itemID: timelineItemID)) + } if let formattedCaption { FormattedBodyText(attributedString: formattedCaption, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift index 53bd61704..86ad073d0 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift @@ -9,7 +9,7 @@ import Foundation import SwiftUI struct ImageRoomTimelineView: View { - @EnvironmentObject private var context: TimelineViewModel.Context + @Environment(\.timelineContext) private var context let timelineItem: ImageRoomTimelineItem var hasMediaCaption: Bool { timelineItem.content.caption != nil } @@ -23,6 +23,9 @@ struct ImageRoomTimelineView: View { // 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, @@ -44,7 +47,7 @@ struct ImageRoomTimelineView: View { mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), blurhash: timelineItem.content.blurhash, size: timelineItem.content.imageInfo.size, - mediaProvider: context.mediaProvider) { + mediaProvider: context?.mediaProvider) { placeholder } .timelineMediaFrame(imageInfo: timelineItem.content.imageInfo) @@ -53,7 +56,7 @@ struct ImageRoomTimelineView: View { mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), blurhash: timelineItem.content.blurhash, size: timelineItem.content.thumbnailInfo?.size ?? timelineItem.content.imageInfo.size, - mediaProvider: context.mediaProvider) { + mediaProvider: context?.mediaProvider) { placeholder } .timelineMediaFrame(imageInfo: timelineItem.content.thumbnailInfo ?? timelineItem.content.imageInfo) @@ -73,6 +76,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var previews: some View { body .environmentObject(viewModel.context) + .environment(\.timelineContext, viewModel.context) } static var body: some View { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift index 6d3752dbd..6c6b70189 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift @@ -8,6 +8,7 @@ import SwiftUI struct LocationRoomTimelineView: View { + @Environment(\.timelineContext) private var context let timelineItem: LocationRoomTimelineItem var body: some View { @@ -15,6 +16,9 @@ struct LocationRoomTimelineView: View { mainContent .accessibilityElement(children: .ignore) .accessibilityLabel(accessibilityLabel) + .onTapGesture { + context?.send(viewAction: .mediaTapped(itemID: timelineItem.id)) + } } } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift index b41114867..043d68834 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift @@ -9,7 +9,7 @@ import Foundation import SwiftUI struct StickerRoomTimelineView: View { - @EnvironmentObject private var context: TimelineViewModel.Context + @Environment(\.timelineContext) private var context let timelineItem: StickerRoomTimelineItem var body: some View { @@ -18,12 +18,15 @@ struct StickerRoomTimelineView: View { mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), blurhash: timelineItem.blurhash, size: timelineItem.imageInfo.size, - mediaProvider: context.mediaProvider) { + mediaProvider: context?.mediaProvider) { placeholder } .timelineMediaFrame(imageInfo: timelineItem.imageInfo) .accessibilityElement(children: .ignore) .accessibilityLabel("\(L10n.commonSticker), \(timelineItem.body)") + .onTapGesture { + context?.send(viewAction: .mediaTapped(itemID: timelineItem.id)) + } } } @@ -38,7 +41,9 @@ struct StickerRoomTimelineView_Previews: PreviewProvider, TestablePreview { static let viewModel = TimelineViewModel.mock static var previews: some View { - body.environmentObject(viewModel.context) + body + .environmentObject(viewModel.context) + .environment(\.timelineContext, viewModel.context) } static var body: some View { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift index 030b58499..88337eb78 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift @@ -9,7 +9,7 @@ import Foundation import SwiftUI struct VideoRoomTimelineView: View { - @EnvironmentObject private var context: TimelineViewModel.Context + @Environment(\.timelineContext) private var context let timelineItem: VideoRoomTimelineItem private var hasMediaCaption: Bool { timelineItem.content.caption != nil } @@ -24,6 +24,9 @@ struct VideoRoomTimelineView: View { // 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, @@ -45,7 +48,7 @@ struct VideoRoomTimelineView: View { mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), blurhash: timelineItem.content.blurhash, size: timelineItem.content.thumbnailInfo?.size, - mediaProvider: context.mediaProvider) { imageView in + mediaProvider: context?.mediaProvider) { imageView in imageView .overlay { playIcon } } placeholder: { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineSenderAvatarView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineSenderAvatarView.swift index 4dd222bce..72fb09764 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineSenderAvatarView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineSenderAvatarView.swift @@ -9,7 +9,7 @@ import Foundation import SwiftUI struct TimelineSenderAvatarView: View { - @EnvironmentObject private var context: TimelineViewModel.Context + @Environment(\.timelineContext) private var context let timelineItem: EventBasedTimelineItemProtocol @@ -18,7 +18,7 @@ struct TimelineSenderAvatarView: View { name: timelineItem.sender.displayName, contentID: timelineItem.sender.id, avatarSize: .user(on: .timeline), - mediaProvider: context.mediaProvider) + mediaProvider: context?.mediaProvider) .overlay { Circle().stroke(Color.compound.bgCanvasDefault, lineWidth: 3) } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift index 27e1699e6..df55cf05d 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift @@ -9,7 +9,7 @@ import Foundation import SwiftUI struct VoiceMessageRoomTimelineView: View { - @EnvironmentObject private var context: TimelineViewModel.Context + @Environment(\.timelineContext) private var context private let timelineItem: VoiceMessageRoomTimelineItem private let playerState: AudioPlayerState @State private var resumePlaybackAfterScrubbing = false @@ -31,22 +31,22 @@ struct VoiceMessageRoomTimelineView: View { } private func onPlaybackPlayPause() { - context.send(viewAction: .handleAudioPlayerAction(.playPause(itemID: timelineItem.id))) + context?.send(viewAction: .handleAudioPlayerAction(.playPause(itemID: timelineItem.id))) } private func onPlaybackSeek(_ progress: Double) { - context.send(viewAction: .handleAudioPlayerAction(.seek(itemID: timelineItem.id, progress: progress))) + 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))) + context?.send(viewAction: .handleAudioPlayerAction(.playPause(itemID: timelineItem.id))) } } else { if resumePlaybackAfterScrubbing { - context.send(viewAction: .handleAudioPlayerAction(.playPause(itemID: timelineItem.id))) + context?.send(viewAction: .handleAudioPlayerAction(.playPause(itemID: timelineItem.id))) resumePlaybackAfterScrubbing = false } }