@@ -214,4 +214,131 @@ class QuotedMessageView_Tests: StreamChatTestCase {
214214 // Then - Default attachment size should be applied
215215 XCTAssertEqual ( view. attachmentSize, CGSize ( width: 36 , height: 36 ) )
216216 }
217+
218+ func test_quotedMessageView_customContentView_snapshot( ) {
219+ // Given - Create a custom football game result attachment
220+ let footballGamePayload = FootballGameAttachmentPayload (
221+ homeTeam: " Benfica " ,
222+ awayTeam: " Porto " ,
223+ homeScore: 2 ,
224+ awayScore: 0
225+ )
226+
227+ let customAttachment = ChatMessageAttachment < FootballGameAttachmentPayload > (
228+ id: . unique,
229+ type: . init( rawValue: " football_game " ) ,
230+ payload: footballGamePayload,
231+ downloadingState: nil ,
232+ uploadingState: nil
233+ ) . asAnyAttachment
234+
235+ let message = ChatMessage . mock (
236+ id: " test " ,
237+ cid: . unique,
238+ text: " Check out this game result! " ,
239+ author: . mock( id: " test " , name: " martin " ) ,
240+ attachments: [ customAttachment]
241+ )
242+
243+ let view = QuotedMessageViewContainer (
244+ factory: CustomQuotedContentViewFactory . shared,
245+ quotedMessage: message,
246+ fillAvailableSpace: false ,
247+ scrolledId: . constant( nil )
248+ )
249+ . applyDefaultSize ( )
250+
251+ // Then
252+ assertSnapshot ( matching: view, as: . image( perceptualPrecision: precision) )
253+ }
254+ }
255+
256+
257+ // MARK: - Custom Football Game Attachment for Custom Quoted Message
258+
259+ fileprivate struct FootballGameAttachmentPayload : AttachmentPayload {
260+ let homeTeam : String
261+ let awayTeam : String
262+ let homeScore : Int
263+ let awayScore : Int
264+
265+ static let type : AttachmentType = . init( rawValue: " football_game " )
266+ }
267+
268+ fileprivate class CustomQuotedContentViewFactory : ViewFactory {
269+ @Injected ( \. chatClient) var chatClient
270+
271+ private init ( ) { }
272+
273+ static let shared = CustomQuotedContentViewFactory ( )
274+
275+ func makeQuotedMessageContentView(
276+ quotedMessage: ChatMessage ,
277+ options: QuotedMessageContentViewOptions
278+ ) -> some View {
279+ Group {
280+ if let footballGameAttachmentPayload = quotedMessage
281+ . attachments ( payloadType: FootballGameAttachmentPayload . self)
282+ . first?
283+ . payload {
284+ // Show custom football game result view
285+ FootballGameQuotedView ( payload: footballGameAttachmentPayload)
286+ } else {
287+ // Fallback to default content view
288+ QuotedMessageContentView (
289+ factory: self ,
290+ quotedMessage: quotedMessage,
291+ options: options
292+ )
293+ }
294+ }
295+ }
296+ }
297+
298+ fileprivate struct FootballGameQuotedView : View {
299+ @Injected ( \. colors) private var colors
300+ @Injected ( \. fonts) private var fonts
301+
302+ let payload : FootballGameAttachmentPayload
303+
304+ var body : some View {
305+ HStack ( spacing: 8 ) {
306+ VStack ( alignment: . center, spacing: 4 ) {
307+ Text ( " ⚽ " )
308+ . font ( . title2)
309+ Text ( " Match " )
310+ . font ( fonts. footnoteBold)
311+ . foregroundColor ( Color ( colors. textLowEmphasis) )
312+ }
313+
314+ Divider ( )
315+ . frame ( height: 50 )
316+
317+ VStack ( spacing: 8 ) {
318+ HStack {
319+ Text ( payload. homeTeam)
320+ . font ( fonts. bodyBold)
321+ . foregroundColor ( Color ( colors. text) )
322+ Spacer ( )
323+ Text ( " \( payload. homeScore) " )
324+ . font ( fonts. title)
325+ . foregroundColor ( Color ( colors. text) )
326+ . frame ( minWidth: 30 )
327+ }
328+
329+ HStack {
330+ Text ( payload. awayTeam)
331+ . font ( fonts. bodyBold)
332+ . foregroundColor ( Color ( colors. text) )
333+ Spacer ( )
334+ Text ( " \( payload. awayScore) " )
335+ . font ( fonts. title)
336+ . foregroundColor ( Color ( colors. text) )
337+ . frame ( minWidth: 30 )
338+ }
339+ }
340+ }
341+ . padding ( 8 )
342+ . frame ( minWidth: 200 )
343+ }
217344}
0 commit comments