@@ -6,40 +6,60 @@ import StreamChat
66import SwiftUI
77
88/// Container showing the quoted message view with the user avatar.
9- struct QuotedMessageViewContainer < Factory: ViewFactory > : View {
10- private let avatarSize : CGFloat = 24
9+ public struct QuotedMessageViewContainer < Factory: ViewFactory > : View {
10+ public var factory : Factory
11+ public var quotedMessage : ChatMessage
12+ public var fillAvailableSpace : Bool
13+ public var forceLeftToRight : Bool
14+ @Binding public var scrolledId : String ?
15+ public let attachmentSize : CGSize
16+ public let quotedAuthorAvatarSize : CGSize
1117
12- var factory : Factory
13- var quotedMessage : ChatMessage
14- var fillAvailableSpace : Bool
15- var forceLeftToRight = false
16- @Binding var scrolledId : String ?
18+ public init (
19+ factory: Factory ,
20+ quotedMessage: ChatMessage ,
21+ fillAvailableSpace: Bool ,
22+ forceLeftToRight: Bool = false ,
23+ scrolledId: Binding < String ? > ,
24+ attachmentSize: CGSize = CGSize ( width: 36 , height: 36 ) ,
25+ quotedAuthorAvatarSize: CGSize = CGSize ( width: 24 , height: 24 )
26+ ) {
27+ self . factory = factory
28+ self . quotedMessage = quotedMessage
29+ self . fillAvailableSpace = fillAvailableSpace
30+ self . forceLeftToRight = forceLeftToRight
31+ _scrolledId = scrolledId
32+ self . attachmentSize = attachmentSize
33+ self . quotedAuthorAvatarSize = quotedAuthorAvatarSize
34+ }
1735
18- var body : some View {
36+ public var body : some View {
1937 HStack ( alignment: . bottom) {
2038 if !quotedMessage. isSentByCurrentUser || forceLeftToRight {
2139 factory. makeQuotedMessageAvatarView (
2240 for: quotedMessage. authorDisplayInfo,
23- size: CGSize ( width : avatarSize , height : avatarSize )
41+ size: quotedAuthorAvatarSize
2442 )
2543
2644 QuotedMessageView (
2745 factory: factory,
2846 quotedMessage: quotedMessage,
2947 fillAvailableSpace: fillAvailableSpace,
30- forceLeftToRight: forceLeftToRight
48+ forceLeftToRight: forceLeftToRight,
49+ attachmentSize: attachmentSize
3150 )
3251 } else {
3352 QuotedMessageView (
3453 factory: factory,
3554 quotedMessage: quotedMessage,
3655 fillAvailableSpace: fillAvailableSpace,
37- forceLeftToRight: forceLeftToRight
56+ forceLeftToRight: forceLeftToRight,
57+ attachmentSize: attachmentSize
3858 )
3959
4060 factory. makeQuotedMessageAvatarView (
4161 for: quotedMessage. authorDisplayInfo,
42- size: CGSize ( width : avatarSize , height : avatarSize )
62+ size: quotedAuthorAvatarSize
4363 )
4464 }
4565 }
@@ -62,14 +82,13 @@ public struct QuotedMessageView<Factory: ViewFactory>: View {
6282 @Injected ( \. fonts) private var fonts
6383 @Injected ( \. colors) private var colors
6484 @Injected ( \. utils) private var utils
65-
66- private let attachmentWidth : CGFloat = 36
6785
6886 public var factory : Factory
6987 public var quotedMessage : ChatMessage
7088 public var fillAvailableSpace : Bool
7189 public var forceLeftToRight : Bool
72-
90+ public let attachmentSize : CGSize
91+
7392 private var messageTypeResolver : MessageTypeResolving {
7493 utils. messageTypeResolver
7594 }
@@ -78,73 +97,26 @@ public struct QuotedMessageView<Factory: ViewFactory>: View {
7897 factory: Factory ,
7998 quotedMessage: ChatMessage ,
8099 fillAvailableSpace: Bool ,
81- forceLeftToRight: Bool
100+ forceLeftToRight: Bool ,
101+ attachmentSize: CGSize = CGSize ( width: 36 , height: 36 )
82102 ) {
83103 self . factory = factory
84104 self . quotedMessage = quotedMessage
85105 self . fillAvailableSpace = fillAvailableSpace
86106 self . forceLeftToRight = forceLeftToRight
107+ self . attachmentSize = attachmentSize
87108 }
88109
89110 public var body : some View {
90111 HStack ( alignment: . top) {
91- if !quotedMessage. attachmentCounts. isEmpty {
92- ZStack {
93- if messageTypeResolver. hasCustomAttachment ( message: quotedMessage) {
94- factory. makeCustomAttachmentQuotedView ( for: quotedMessage)
95- } else if hasVoiceAttachments {
96- VoiceRecordingPreview ( voiceAttachment: quotedMessage. voiceRecordingAttachments [ 0 ] . payload)
97- } else if !quotedMessage. imageAttachments. isEmpty {
98- LazyLoadingImage (
99- source: MediaAttachment ( url: quotedMessage. imageAttachments [ 0 ] . imageURL, type: . image) ,
100- width: attachmentWidth,
101- height: attachmentWidth,
102- resize: false
103- )
104- } else if !quotedMessage. giphyAttachments. isEmpty {
105- LazyGiphyView (
106- source: quotedMessage. giphyAttachments [ 0 ] . previewURL,
107- width: attachmentWidth
108- )
109- } else if !quotedMessage. fileAttachments. isEmpty {
110- Image ( uiImage: filePreviewImage ( for: quotedMessage. fileAttachments [ 0 ] . assetURL) )
111- } else if !quotedMessage. videoAttachments. isEmpty {
112- VideoAttachmentView (
113- attachment: quotedMessage. videoAttachments [ 0 ] ,
114- message: quotedMessage,
115- width: attachmentWidth,
116- ratio: 1.0 ,
117- cornerRadius: 0
118- )
119- } else if !quotedMessage. linkAttachments. isEmpty {
120- LazyImage (
121- imageURL: quotedMessage. linkAttachments [ 0 ] . previewURL ?? quotedMessage. linkAttachments [ 0 ]
122- . originalURL
123- )
124- . onDisappear ( . cancel)
125- . processors ( [ ImageProcessors . Resize ( width: attachmentWidth) ] )
126- . priority ( . high)
127- }
128- }
129- . frame ( width: hasVoiceAttachments ? nil : attachmentWidth, height: attachmentWidth)
130- . aspectRatio ( 1 , contentMode: . fill)
131- . clipShape ( RoundedRectangle ( cornerRadius: 8 ) )
132- . allowsHitTesting ( false )
133- } else if let poll = quotedMessage. poll, !quotedMessage. isDeleted {
134- Text ( " 📊 \( poll. name) " )
135- }
136-
137- if !hasVoiceAttachments {
138- Text ( textForMessage)
139- . foregroundColor ( textColor ( for: quotedMessage) )
140- . lineLimit ( 3 )
141- . font ( fonts. footnote)
142- . accessibility ( identifier: " quotedMessageText " )
143- }
144-
145- if fillAvailableSpace {
146- Spacer ( )
147- }
112+ factory. makeQuotedMessageContentView (
113+ options: QuotedMessageContentViewOptions (
114+ quotedMessage: quotedMessage,
115+ fillAvailableSpace: fillAvailableSpace,
116+ forceLeftToRight: forceLeftToRight,
117+ attachmentSize: attachmentSize
118+ )
119+ )
148120 }
149121 . id ( quotedMessage. messageId)
150122 . padding (
@@ -174,6 +146,125 @@ public struct QuotedMessageView<Factory: ViewFactory>: View {
174146 colors. quotedMessageBackgroundCurrentUser : colors. quotedMessageBackgroundOtherUser
175147 return color
176148 }
149+
150+ private var hasVoiceAttachments : Bool {
151+ !quotedMessage. voiceRecordingAttachments. isEmpty
152+ }
153+ }
154+
155+ /// Options for configuring the quoted message content view.
156+ public struct QuotedMessageContentViewOptions {
157+ /// The quoted message to display.
158+ public let quotedMessage : ChatMessage
159+ /// Whether the quoted container should take all the available space.
160+ public let fillAvailableSpace : Bool
161+ /// Whether to force left to right layout.
162+ public let forceLeftToRight : Bool
163+ /// The size of the attachment preview.
164+ public let attachmentSize : CGSize
165+
166+ public init (
167+ quotedMessage: ChatMessage ,
168+ fillAvailableSpace: Bool ,
169+ forceLeftToRight: Bool ,
170+ attachmentSize: CGSize = CGSize ( width: 36 , height: 36 )
171+ ) {
172+ self . quotedMessage = quotedMessage
173+ self . fillAvailableSpace = fillAvailableSpace
174+ self . forceLeftToRight = forceLeftToRight
175+ self . attachmentSize = attachmentSize
176+ }
177+ }
178+
179+ /// The quoted message content view.
180+ ///
181+ /// It is the view that is embedded in quoted message bubble view.
182+ public struct QuotedMessageContentView < Factory: ViewFactory > : View {
183+ @Environment ( \. channelTranslationLanguage) var translationLanguage
184+
185+ @Injected ( \. images) private var images
186+ @Injected ( \. fonts) private var fonts
187+ @Injected ( \. colors) private var colors
188+ @Injected ( \. utils) private var utils
189+
190+ public var factory : Factory
191+ public var options : QuotedMessageContentViewOptions
192+
193+ private var quotedMessage : ChatMessage {
194+ options. quotedMessage
195+ }
196+
197+ private var messageTypeResolver : MessageTypeResolving {
198+ utils. messageTypeResolver
199+ }
200+
201+ public init (
202+ factory: Factory ,
203+ options: QuotedMessageContentViewOptions
204+ ) {
205+ self . factory = factory
206+ self . options = options
207+ }
208+
209+ public var body : some View {
210+ if !quotedMessage. attachmentCounts. isEmpty {
211+ ZStack {
212+ if messageTypeResolver. hasCustomAttachment ( message: quotedMessage) {
213+ factory. makeCustomAttachmentQuotedView ( for: quotedMessage)
214+ } else if hasVoiceAttachments {
215+ VoiceRecordingPreview ( voiceAttachment: quotedMessage. voiceRecordingAttachments [ 0 ] . payload)
216+ } else if !quotedMessage. imageAttachments. isEmpty {
217+ LazyLoadingImage (
218+ source: MediaAttachment ( url: quotedMessage. imageAttachments [ 0 ] . imageURL, type: . image) ,
219+ width: options. attachmentSize. width,
220+ height: options. attachmentSize. height,
221+ resize: false
222+ )
223+ } else if !quotedMessage. giphyAttachments. isEmpty {
224+ LazyGiphyView (
225+ source: quotedMessage. giphyAttachments [ 0 ] . previewURL,
226+ width: options. attachmentSize. width
227+ )
228+ } else if !quotedMessage. fileAttachments. isEmpty {
229+ Image ( uiImage: filePreviewImage ( for: quotedMessage. fileAttachments [ 0 ] . assetURL) )
230+ } else if !quotedMessage. videoAttachments. isEmpty {
231+ VideoAttachmentView (
232+ attachment: quotedMessage. videoAttachments [ 0 ] ,
233+ message: quotedMessage,
234+ width: options. attachmentSize. width,
235+ ratio: 1.0 ,
236+ cornerRadius: 0
237+ )
238+ } else if !quotedMessage. linkAttachments. isEmpty {
239+ LazyImage (
240+ imageURL: quotedMessage. linkAttachments [ 0 ] . previewURL ?? quotedMessage. linkAttachments [ 0 ]
241+ . originalURL
242+ )
243+ . onDisappear ( . cancel)
244+ . processors ( [ ImageProcessors . Resize ( width: options. attachmentSize. width) ] )
245+ . priority ( . high)
246+ }
247+ }
248+ . frame ( width: hasVoiceAttachments ? nil : options. attachmentSize. width, height: options. attachmentSize. height)
249+ . aspectRatio ( 1 , contentMode: . fill)
250+ . clipShape ( RoundedRectangle ( cornerRadius: 8 ) )
251+ . allowsHitTesting ( false )
252+ } else if let poll = quotedMessage. poll, !quotedMessage. isDeleted {
253+ Text ( " 📊 \( poll. name) " )
254+ }
255+
256+ if !hasVoiceAttachments {
257+ Text ( textForMessage)
258+ . foregroundColor ( textColor ( for: quotedMessage) )
259+ . lineLimit ( 3 )
260+ . font ( fonts. footnote)
261+ . accessibility ( identifier: " quotedMessageText " )
262+ }
263+
264+ if options. fillAvailableSpace {
265+ Spacer ( )
266+ }
267+ }
177268
178269 private func filePreviewImage( for url: URL ) -> UIImage {
179270 let iconName = url. pathExtension
0 commit comments