diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift index c3e4805bf3..16974147c8 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift @@ -70,14 +70,14 @@ struct CIImageView: View { } private func imageView(_ img: UIImage) -> some View { - let w = img.size.width <= img.size.height ? maxWidth * 0.75 : img.imageData == nil ? .infinity : maxWidth + let w = img.size.width <= img.size.height ? maxWidth * 0.75 : maxWidth DispatchQueue.main.async { imgWidth = w } return ZStack(alignment: .topTrailing) { if img.imageData == nil { Image(uiImage: img) .resizable() .scaledToFit() - .frame(maxWidth: w) + .frame(width: w) } else { SwiftyGif(image: img) .frame(width: w, height: w * img.size.height / img.size.width) diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift index ff208fe58a..a3918e17bc 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift @@ -243,13 +243,13 @@ struct CIVideoView: View { } private func imageView(_ img: UIImage) -> some View { - let w = img.size.width <= img.size.height ? maxWidth * 0.75 : .infinity + let w = img.size.width <= img.size.height ? maxWidth * 0.75 : maxWidth DispatchQueue.main.async { videoWidth = w } return ZStack(alignment: .topTrailing) { Image(uiImage: img) .resizable() .scaledToFit() - .frame(maxWidth: w) + .frame(width: w) loadingIndicator() } } diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index 33bd46e393..cd2aa55bc3 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -514,6 +514,7 @@ struct ChatView: View { chat: chat, chatItem: ci, maxWidth: maxWidth, + itemWidth: maxWidth, composeState: $composeState, selectedMember: $selectedMember, chatView: self @@ -526,6 +527,7 @@ struct ChatView: View { @ObservedObject var chat: Chat var chatItem: ChatItem var maxWidth: CGFloat + @State var itemWidth: CGFloat @Binding var composeState: ComposeState @Binding var selectedMember: GMember? var chatView: ChatView @@ -654,7 +656,7 @@ struct ChatView: View { playbackState: $playbackState, playbackTime: $playbackTime ) - .uiKitContextMenu(maxWidth: maxWidth, menu: uiMenu, allowMenu: $allowMenu) + .uiKitContextMenu(hasImageOrVideo: ci.content.msgContent?.isImageOrVideo == true, maxWidth: maxWidth, itemWidth: $itemWidth, menu: uiMenu, allowMenu: $allowMenu) .accessibilityLabel("") if ci.content.msgContent != nil && (ci.meta.itemDeleted == nil || revealed) && ci.reactions.count > 0 { chatItemReactions(ci) diff --git a/apps/ios/Shared/Views/Helpers/ContextMenu.swift b/apps/ios/Shared/Views/Helpers/ContextMenu.swift index 3b82d6eb95..9504d919ef 100644 --- a/apps/ios/Shared/Views/Helpers/ContextMenu.swift +++ b/apps/ios/Shared/Views/Helpers/ContextMenu.swift @@ -11,11 +11,20 @@ import UIKit import SwiftUI extension View { - func uiKitContextMenu(maxWidth: CGFloat, menu: Binding, allowMenu: Binding) -> some View { + func uiKitContextMenu(hasImageOrVideo: Bool, maxWidth: CGFloat, itemWidth: Binding, menu: Binding, allowMenu: Binding) -> some View { Group { if allowMenu.wrappedValue { - InteractionView(content: self, maxWidth: maxWidth, menu: menu) - .fixedSize(horizontal: true, vertical: false) + if hasImageOrVideo { + InteractionView(content: + self.environmentObject(ChatModel.shared) + .overlay(DetermineWidthImageVideoItem()) + .onPreferenceChange(DetermineWidthImageVideoItem.Key.self) { itemWidth.wrappedValue = $0 == 0 ? maxWidth : $0 } + , maxWidth: maxWidth, itemWidth: itemWidth, menu: menu) + .frame(maxWidth: itemWidth.wrappedValue) + } else { + InteractionView(content: self.environmentObject(ChatModel.shared), maxWidth: maxWidth, itemWidth: itemWidth, menu: menu) + .fixedSize(horizontal: true, vertical: false) + } } else { self } @@ -31,13 +40,14 @@ private class HostingViewHolder: UIView { struct InteractionView: UIViewRepresentable { let content: Content var maxWidth: CGFloat + var itemWidth: Binding @Binding var menu: UIMenu func makeUIView(context: Context) -> UIView { let view = HostingViewHolder() - view.contentSize = CGSizeMake(maxWidth, .infinity) view.backgroundColor = .clear let hostView = UIHostingController(rootView: content) + view.contentSize = hostView.view.intrinsicContentSize hostView.view.translatesAutoresizingMaskIntoConstraints = false let constraints = [ hostView.view.topAnchor.constraint(equalTo: view.topAnchor), @@ -57,7 +67,11 @@ struct InteractionView: UIViewRepresentable { } func updateUIView(_ uiView: UIView, context: Context) { - (uiView as! HostingViewHolder).contentSize = uiView.subviews[0].sizeThatFits(CGSizeMake(maxWidth, .infinity)) + let was = (uiView as! HostingViewHolder).contentSize + (uiView as! HostingViewHolder).contentSize = uiView.subviews[0].sizeThatFits(CGSizeMake(itemWidth.wrappedValue, .infinity)) + if was != (uiView as! HostingViewHolder).contentSize { + uiView.invalidateIntrinsicContentSize() + } } func makeCoordinator() -> Coordinator { diff --git a/apps/ios/Shared/Views/Helpers/DetermineWidth.swift b/apps/ios/Shared/Views/Helpers/DetermineWidth.swift index d2a0aaab1d..b05ab17089 100644 --- a/apps/ios/Shared/Views/Helpers/DetermineWidth.swift +++ b/apps/ios/Shared/Views/Helpers/DetermineWidth.swift @@ -21,6 +21,19 @@ struct DetermineWidth: View { } } +struct DetermineWidthImageVideoItem: View { + typealias Key = MaximumWidthImageVideoPreferenceKey + var body: some View { + GeometryReader { proxy in + Color.clear + .preference( + key: MaximumWidthImageVideoPreferenceKey.self, + value: proxy.size.width + ) + } + } +} + struct MaximumWidthPreferenceKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { @@ -28,6 +41,13 @@ struct MaximumWidthPreferenceKey: PreferenceKey { } } +struct MaximumWidthImageVideoPreferenceKey: PreferenceKey { + static var defaultValue: CGFloat = 0 + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = max(value, nextValue()) + } +} + struct DetermineWidth_Previews: PreviewProvider { static var previews: some View { DetermineWidth() diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 5e1c0ac538..ed62b5c9ac 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -3209,6 +3209,14 @@ public enum MsgContent: Equatable { } } + public var isImageOrVideo: Bool { + switch self { + case .image: true + case .video: true + default: false + } + } + var cmdString: String { "json \(encodeJSON(self))" }