diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/AsyncVoiceMessages/VoiceRecordingContainerView.swift b/Sources/StreamChatSwiftUI/ChatMessageList/AsyncVoiceMessages/VoiceRecordingContainerView.swift index dd01bd8f1..8ebef6613 100644 --- a/Sources/StreamChatSwiftUI/ChatMessageList/AsyncVoiceMessages/VoiceRecordingContainerView.swift +++ b/Sources/StreamChatSwiftUI/ChatMessageList/AsyncVoiceMessages/VoiceRecordingContainerView.swift @@ -9,6 +9,7 @@ import SwiftUI public struct VoiceRecordingContainerView: View { @Injected(\.colors) var colors @Injected(\.images) var images + @Injected(\.tokens) var tokens @Injected(\.utils) var utils let factory: Factory @@ -39,38 +40,22 @@ public struct VoiceRecordingContainerView: View { } public var body: some View { - VStack(spacing: 0) { - VStack { - if let quotedMessage = message.quotedMessage { - factory.makeChatQuotedMessageView( - options: ChatQuotedMessageViewOptions( - quotedMessage: quotedMessage, - parentMessage: message, - scrolledId: $scrolledId - ) - ) - } - VStack(spacing: 2) { - ForEach(message.voiceRecordingAttachments, id: \.self) { attachment in - VoiceRecordingView( - handler: handler, - addedVoiceRecording: AddedVoiceRecording( - url: attachment.payload.voiceRecordingURL, - duration: attachment.payload.duration ?? 0, - waveform: attachment.payload.waveformData ?? [] - ), - isSentByCurrentUser: message.isSentByCurrentUser - ) - .padding(.all, 8) - } - } - } - if !message.text.isEmpty { - AttachmentTextView(factory: factory, message: message) - .frame(maxWidth: .infinity) + VStack(spacing: tokens.spacingXxxs) { + ForEach(message.voiceRecordingAttachments) { attachment in + VoiceRecordingView( + handler: handler, + addedVoiceRecording: AddedVoiceRecording( + url: attachment.payload.voiceRecordingURL, + duration: attachment.payload.duration ?? 0, + waveform: attachment.payload.waveformData ?? [] + ), + isSentByCurrentUser: message.isSentByCurrentUser + ) + .padding(.all, tokens.spacingXs) + .background(MessageAttachmentsBubbleConfiguration.attachmentBackgroundColor(for: message)) + .roundWithBorder(cornerRadius: tokens.messageBubbleRadiusAttachment) } } - .padding(.all, 2) .onReceive(handler.$context, perform: { value in guard message.voiceRecordingAttachments.count > 1 else { return } if value.state == .playing { @@ -92,15 +77,6 @@ public struct VoiceRecordingContainerView: View { .onAppear { player.subscribe(handler) } - .modifier( - factory.styles.makeMessageViewModifier( - for: MessageModifierInfo( - message: message, - isFirst: isFirst, - cornerRadius: 16 - ) - ) - ) } private func index(for attachment: ChatMessageVoiceRecordingAttachment) -> Int { @@ -124,7 +100,7 @@ struct VoiceRecordingView: View { private var showContextDuration: Bool { isActive && handler.context.currentTime > 0 } private var controlBorderColor: Color? { - isSentByCurrentUser ? Color(colors.chatBorderOnChatOutgoing) : nil + isSentByCurrentUser ? Color(colors.chatBorderOnChatOutgoing) : Color(colors.chatBorderOnChatIncoming) } var body: some View { diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/AttachmentTextView.swift b/Sources/StreamChatSwiftUI/ChatMessageList/AttachmentTextView.swift new file mode 100644 index 000000000..729bcf090 --- /dev/null +++ b/Sources/StreamChatSwiftUI/ChatMessageList/AttachmentTextView.swift @@ -0,0 +1,40 @@ +// +// Copyright © 2026 Stream.io Inc. All rights reserved. +// + +import StreamChat +import SwiftUI + +public struct AttachmentTextView: View { + @Injected(\.colors) private var colors + @Injected(\.fonts) private var fonts + @Injected(\.tokens) private var tokens + + var factory: Factory + var message: ChatMessage + let injectedBackgroundColor: UIColor? + + public init(factory: Factory = DefaultViewFactory.shared, message: ChatMessage, injectedBackgroundColor: UIColor? = nil) { + self.factory = factory + self.message = message + self.injectedBackgroundColor = injectedBackgroundColor + } + + public var body: some View { + HStack { + factory.makeStreamTextView(options: .init(message: message)) + .padding(.horizontal, tokens.spacingXxs) + .fixedSize(horizontal: false, vertical: true) + Spacer() + } + .background(Color(backgroundColor)) + .accessibilityIdentifier("MessageTextView") + } + + private var backgroundColor: UIColor { + if let injectedBackgroundColor { + return injectedBackgroundColor + } + return message.isSentByCurrentUser ? colors.chatBackgroundOutgoing : colors.chatBackgroundIncoming + } +} diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/FileAttachmentView.swift b/Sources/StreamChatSwiftUI/ChatMessageList/FileAttachmentView.swift index bb69472fa..147e8348e 100644 --- a/Sources/StreamChatSwiftUI/ChatMessageList/FileAttachmentView.swift +++ b/Sources/StreamChatSwiftUI/ChatMessageList/FileAttachmentView.swift @@ -6,6 +6,8 @@ import StreamChat import SwiftUI public struct FileAttachmentsContainer: View { + @Injected(\.colors) var colors + @Injected(\.tokens) var tokens var factory: Factory var message: ChatMessage var width: CGFloat @@ -27,46 +29,17 @@ public struct FileAttachmentsContainer: View { } public var body: some View { - VStack(alignment: message.alignmentInBubble) { - if let quotedMessage = message.quotedMessage { - factory.makeChatQuotedMessageView( - options: ChatQuotedMessageViewOptions( - quotedMessage: quotedMessage, - parentMessage: message, - scrolledId: $scrolledId - ) + VStack(spacing: tokens.spacingXxs) { + ForEach(message.fileAttachments) { attachment in + FileAttachmentView( + attachment: attachment, + width: width, + isFirst: isFirst ) + .background(MessageAttachmentsBubbleConfiguration.attachmentBackgroundColor(for: message)) + .roundWithBorder(cornerRadius: tokens.messageBubbleRadiusAttachment) } - - VStack(spacing: 0) { - VStack(spacing: 4) { - ForEach(message.fileAttachments, id: \.self) { attachment in - FileAttachmentView( - attachment: attachment, - width: width, - isFirst: isFirst - ) - } - } - if !message.text.isEmpty { - HStack { - Text(message.adjustedText) - .foregroundColor(textColor(for: message)) - .standardPadding() - Spacer() - } - } - } - .padding(.all, 4) } - .modifier( - factory.styles.makeMessageViewModifier( - for: MessageModifierInfo( - message: message, - isFirst: isFirst - ) - ) - ) .accessibilityIdentifier("FileAttachmentsContainer") } } @@ -76,6 +49,7 @@ public struct FileAttachmentView: View { @Injected(\.images) private var images @Injected(\.fonts) private var fonts @Injected(\.colors) private var colors + @Injected(\.tokens) private var tokens @Injected(\.chatClient) private var chatClient @State private var fullScreenShown = false @@ -110,10 +84,8 @@ public struct FileAttachmentView: View { DownloadShareAttachmentView(attachment: attachment) } } - .padding(.all, 8) - .background(Color(colors.background)) + .padding(.all, tokens.spacingSm) .frame(width: width) - .roundWithBorder() .withUploadingStateIndicator(for: attachment.uploadingState, url: attachment.assetURL) .withDownloadingStateIndicator(for: attachment.downloadingState, url: attachment.assetURL) .sheet(isPresented: $fullScreenShown) { @@ -135,6 +107,7 @@ public struct FileAttachmentDisplayView: View { @Injected(\.images) private var images @Injected(\.fonts) private var fonts @Injected(\.colors) private var colors + @Injected(\.tokens) private var tokens var url: URL var title: String @@ -147,13 +120,13 @@ public struct FileAttachmentDisplayView: View { } public var body: some View { - HStack { + HStack(spacing: tokens.spacingSm) { Image(uiImage: previewImage) .resizable() .aspectRatio(contentMode: .fit) .frame(width: 34, height: 40) .accessibilityHidden(true) - VStack(alignment: .leading, spacing: 8) { + VStack(alignment: .leading, spacing: tokens.spacingXxs) { Text(title) .font(fonts.bodyBold) .lineLimit(1) diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/GiphyAttachmentView.swift b/Sources/StreamChatSwiftUI/ChatMessageList/GiphyAttachmentView.swift index 4f3ab3735..5a92d9346 100644 --- a/Sources/StreamChatSwiftUI/ChatMessageList/GiphyAttachmentView.swift +++ b/Sources/StreamChatSwiftUI/ChatMessageList/GiphyAttachmentView.swift @@ -32,6 +32,7 @@ public struct GiphyAttachmentView: View { scrolledId: $scrolledId ) ) + .padding(tokens.spacingXs) } if visibleOnlyToCurrentUser { diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/ImageAttachmentView.swift b/Sources/StreamChatSwiftUI/ChatMessageList/ImageAttachmentView.swift deleted file mode 100644 index 85b85fcc1..000000000 --- a/Sources/StreamChatSwiftUI/ChatMessageList/ImageAttachmentView.swift +++ /dev/null @@ -1,522 +0,0 @@ -// -// Copyright © 2026 Stream.io Inc. All rights reserved. -// - -import StreamChat -import SwiftUI - -public struct ImageAttachmentContainer: View { - @Injected(\.colors) private var colors - - var factory: Factory - let message: ChatMessage - let width: CGFloat - let isFirst: Bool - @Binding var scrolledId: String? - - @State private var galleryShown = false - @State private var selectedIndex = 0 - - public init( - factory: Factory, - message: ChatMessage, - width: CGFloat, - isFirst: Bool, - scrolledId: Binding - ) { - self.factory = factory - self.message = message - self.width = width - self.isFirst = isFirst - self._scrolledId = scrolledId - } - - public var body: some View { - VStack( - alignment: message.alignmentInBubble, - spacing: 0 - ) { - if let quotedMessage = message.quotedMessage { - factory.makeChatQuotedMessageView( - options: ChatQuotedMessageViewOptions( - quotedMessage: quotedMessage, - parentMessage: message, - scrolledId: $scrolledId - ) - ) - } - - VStack( - alignment: message.alignmentInBubble, - spacing: 0 - ) { - ImageAttachmentView( - message: message, - sources: sources, - width: width - ) { index in - if message.localState == nil { - selectedIndex = index - galleryShown = true - } - } - - if !message.text.isEmpty { - AttachmentTextView(factory: factory, message: message) - .frame(width: width) - } - } - } - .modifier( - factory.styles.makeMessageViewModifier( - for: MessageModifierInfo( - message: message, isFirst: isFirst && message.videoAttachments.isEmpty - ) - ) - ) - .fullScreenCover(isPresented: $galleryShown, onDismiss: { - selectedIndex = 0 - }) { - factory.makeGalleryView( - options: GalleryViewOptions( - mediaAttachments: sources, - message: message, - isShown: $galleryShown, - options: .init(selectedIndex: selectedIndex) - ) - ) - } - .accessibilityIdentifier("ImageAttachmentContainer") - } - - private var sources: [MediaAttachment] { - let videoSources = message.videoAttachments.map { attachment in - let url: URL = attachment.videoURL - return MediaAttachment( - url: url, - type: .video, - uploadingState: attachment.uploadingState - ) - } - let imageSources = message.imageAttachments.map { attachment in - let url: URL = attachment.imageURL - return MediaAttachment( - url: url, - type: .image, - uploadingState: attachment.uploadingState - ) - } - return videoSources + imageSources - } -} - -public struct AttachmentTextView: View { - @Injected(\.colors) private var colors - @Injected(\.fonts) private var fonts - - var factory: Factory - var message: ChatMessage - let injectedBackgroundColor: UIColor? - - public init(factory: Factory = DefaultViewFactory.shared, message: ChatMessage, injectedBackgroundColor: UIColor? = nil) { - self.factory = factory - self.message = message - self.injectedBackgroundColor = injectedBackgroundColor - } - - public var body: some View { - HStack { - factory.makeAttachmentTextView(options: .init(mesage: message)) - .standardPadding() - .fixedSize(horizontal: false, vertical: true) - Spacer() - } - .background(Color(backgroundColor)) - .accessibilityIdentifier("AttachmentTextView") - } - - private var backgroundColor: UIColor { - if let injectedBackgroundColor { - return injectedBackgroundColor - } - if message.isSentByCurrentUser { - if message.type == .ephemeral { - return colors.background8 - } else { - return colors.chatBackgroundOutgoing - } - } else { - return colors.chatBackgroundIncoming - } - } -} - -struct ImageAttachmentView: View { - @Injected(\.colors) private var colors - @Injected(\.fonts) private var fonts - @Injected(\.utils) private var utils - - let message: ChatMessage - let sources: [MediaAttachment] - let width: CGFloat - var imageTapped: ((Int) -> Void)? - - private let spacing: CGFloat = 2 - private let maxDisplayedImages = 4 - - private var imageCDN: ImageCDN { - utils.imageCDN - } - - var body: some View { - Group { - if sources.count == 1 { - SingleImageView( - source: sources[0], - width: width, - imageTapped: imageTapped, - index: 0 - ) - .withUploadingStateIndicator(for: uploadState(for: 0), url: sources[0].url) - } else if sources.count == 2 { - HStack(spacing: spacing) { - MultiImageView( - source: sources[0], - width: width / 2, - height: fullHeight, - imageTapped: imageTapped, - index: 0 - ) - .withUploadingStateIndicator(for: uploadState(for: 0), url: sources[0].url) - - MultiImageView( - source: sources[1], - width: width / 2, - height: fullHeight, - imageTapped: imageTapped, - index: 1 - ) - .withUploadingStateIndicator(for: uploadState(for: 1), url: sources[1].url) - } - } else if sources.count == 3 { - HStack(spacing: spacing) { - MultiImageView( - source: sources[0], - width: width / 2, - height: fullHeight, - imageTapped: imageTapped, - index: 0 - ) - .withUploadingStateIndicator(for: uploadState(for: 0), url: sources[0].url) - - VStack(spacing: spacing) { - MultiImageView( - source: sources[1], - width: width / 2, - height: fullHeight / 2, - imageTapped: imageTapped, - index: 1 - ) - .withUploadingStateIndicator(for: uploadState(for: 1), url: sources[1].url) - - MultiImageView( - source: sources[2], - width: width / 2, - height: fullHeight / 2, - imageTapped: imageTapped, - index: 2 - ) - .withUploadingStateIndicator(for: uploadState(for: 2), url: sources[2].url) - } - } - } else if sources.count > 3 { - HStack(spacing: spacing) { - VStack(spacing: spacing) { - MultiImageView( - source: sources[0], - width: width / 2, - height: fullHeight / 2, - imageTapped: imageTapped, - index: 0 - ) - .withUploadingStateIndicator(for: uploadState(for: 0), url: sources[0].url) - - MultiImageView( - source: sources[2], - width: width / 2, - height: fullHeight / 2, - imageTapped: imageTapped, - index: 2 - ) - .withUploadingStateIndicator(for: uploadState(for: 2), url: sources[2].url) - } - - VStack(spacing: spacing) { - MultiImageView( - source: sources[1], - width: width / 2, - height: fullHeight / 2, - imageTapped: imageTapped, - index: 1 - ) - .withUploadingStateIndicator(for: uploadState(for: 1), url: sources[1].url) - - ZStack { - MultiImageView( - source: sources[3], - width: width / 2, - height: fullHeight / 2, - imageTapped: imageTapped, - index: 3 - ) - .withUploadingStateIndicator(for: uploadState(for: 3), url: sources[3].url) - - if notDisplayedImages > 0 { - Color.black.opacity(0.4) - .allowsHitTesting(false) - - Text("+\(notDisplayedImages)") - .foregroundColor(Color(colors.staticColorText)) - .font(fonts.title) - .allowsHitTesting(false) - } - } - .frame(width: width / 2, height: fullHeight / 2) - } - } - } - } - .frame(width: width, height: fullHeight) - } - - private var fullHeight: CGFloat { - 3 * width / 4 - } - - private var notDisplayedImages: Int { - sources.count > maxDisplayedImages ? sources.count - maxDisplayedImages : 0 - } - - private func uploadState(for index: Int) -> AttachmentUploadingState? { - sources[index].uploadingState - } -} - -struct SingleImageView: View { - let source: MediaAttachment - let width: CGFloat - var imageTapped: ((Int) -> Void)? - var index: Int? - - private var height: CGFloat { - 3 * width / 4 - } - - var body: some View { - LazyLoadingImage( - source: source, - width: width, - height: height, - imageTapped: imageTapped, - index: index - ) - .frame(width: width, height: height) - .id(source.id) - .accessibilityIdentifier("SingleImageView") - } -} - -struct MultiImageView: View { - let source: MediaAttachment - let width: CGFloat - let height: CGFloat - var imageTapped: ((Int) -> Void)? - var index: Int? - - var body: some View { - LazyLoadingImage( - source: source, - width: width, - height: height, - imageTapped: imageTapped, - index: index - ) - .frame(width: width, height: height) - .id(source.id) - .accessibilityIdentifier("MultiImageView") - } -} - -struct LazyLoadingImage: View { - @Injected(\.utils) private var utils - - @State private var image: UIImage? - @State private var error: Error? - - let source: MediaAttachment - let width: CGFloat - let height: CGFloat - var resize: Bool = true - var shouldSetFrame: Bool = true - var showVideoIcon: Bool = true - var imageTapped: ((Int) -> Void)? - var index: Int? - var onImageLoaded: (UIImage) -> Void = { _ in /* Default implementation. */ } - - var body: some View { - ZStack { - if let image { - imageView(for: image) - if let imageTapped { - // NOTE: needed because of bug with SwiftUI. - // The click area expands outside the image view (although not visible). - Rectangle() - .fill(.clear) - .frame(width: width, height: height) - .contentShape(.rect) - .clipped() - .allowsHitTesting(true) - .highPriorityGesture( - TapGesture() - .onEnded { _ in - imageTapped(index ?? 0) - } - ) - .accessibilityLabel(L10n.Message.Attachment.accessibilityLabel((index ?? 0) + 1)) - .accessibilityAddTraits(source.type == .video ? .startsMediaSession : .isImage) - .accessibilityAction { - imageTapped(index ?? 0) - } - } - } else if error != nil { - Color(.secondarySystemBackground) - } else { - ZStack { - Color(.secondarySystemBackground) - ProgressView() - } - } - - if showVideoIcon && source.type == .video && width > 64 && source.uploadingState == nil { - VideoPlayIcon() - .accessibilityHidden(true) - } - } - .onAppear { - if image != nil { - return - } - - source.generateThumbnail( - resize: resize, - preferredSize: CGSize(width: width, height: height) - ) { result in - switch result { - case let .success(image): - self.image = image - onImageLoaded(image) - case let .failure(error): - self.error = error - } - } - } - } - - func imageView(for image: UIImage) -> some View { - Image(uiImage: image) - .resizable() - .scaledToFill() - .aspectRatio(contentMode: .fill) - .frame(width: shouldSetFrame ? width : nil, height: shouldSetFrame ? height : nil) - .allowsHitTesting(false) - .scaleEffect(1.0001) // Needed because of SwiftUI sometimes incorrectly displaying landscape images. - .clipped() - .accessibilityHidden(true) - } -} - -extension ChatMessage { - var alignmentInBubble: HorizontalAlignment { - .leading - } -} - -public final class MediaAttachment: Identifiable, Equatable, Sendable { - public let url: URL - public let type: MediaAttachmentType - public let uploadingState: AttachmentUploadingState? - - public init(url: URL, type: MediaAttachmentType, uploadingState: AttachmentUploadingState? = nil) { - self.url = url - self.type = type - self.uploadingState = uploadingState - } - - public var id: String { - url.absoluteString - } - - @MainActor func generateThumbnail( - resize: Bool, - preferredSize: CGSize, - completion: @escaping @MainActor (Result) -> Void - ) { - let utils = InjectedValues[\.utils] - if type == .image { - utils.imageLoader.loadImage( - url: url, - imageCDN: utils.imageCDN, - resize: resize, - preferredSize: preferredSize, - completion: completion - ) - } else if type == .video { - utils.videoPreviewLoader.loadPreviewForVideo( - at: url, - completion: completion - ) - } - } - - public static func == (lhs: MediaAttachment, rhs: MediaAttachment) -> Bool { - lhs.url == rhs.url - && lhs.type == rhs.type - && lhs.uploadingState == rhs.uploadingState - } -} - -extension MediaAttachment { - convenience init(from attachment: ChatMessageImageAttachment) { - let url: URL - if let state = attachment.uploadingState { - url = state.localFileURL - } else { - url = attachment.imageURL - } - self.init( - url: url, - type: .image, - uploadingState: attachment.uploadingState - ) - } -} - -public final class MediaAttachmentType: RawRepresentable, Sendable { - public let rawValue: String - public init(rawValue: String) { - self.rawValue = rawValue - } - - public static let image = MediaAttachmentType(rawValue: "image") - public static let video = MediaAttachmentType(rawValue: "video") -} - -/// Options for the gallery view. -public final class MediaViewsOptions: Sendable { - /// The index of the selected media item. - public let selectedIndex: Int - - public init(selectedIndex: Int) { - self.selectedIndex = selectedIndex - } -} diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/LazyLoadingImage.swift b/Sources/StreamChatSwiftUI/ChatMessageList/LazyLoadingImage.swift new file mode 100644 index 000000000..7f8cbd0ef --- /dev/null +++ b/Sources/StreamChatSwiftUI/ChatMessageList/LazyLoadingImage.swift @@ -0,0 +1,108 @@ +// +// Copyright © 2026 Stream.io Inc. All rights reserved. +// + +import StreamChat +import SwiftUI + +struct LazyLoadingImage: View { + @Injected(\.utils) private var utils + + @State private var image: UIImage? + @State private var error: Error? + + let source: MediaAttachment + let width: CGFloat + let height: CGFloat + var resize: Bool = true + var shouldSetFrame: Bool = true + var showVideoIcon: Bool = true + var imageTapped: ((Int) -> Void)? + var index: Int? + var onImageLoaded: (UIImage) -> Void = { _ in /* Default implementation. */ } + + var body: some View { + ZStack { + if let image { + imageView(for: image) + if let imageTapped { + // NOTE: needed because of bug with SwiftUI. + // The click area expands outside the image view (although not visible). + Rectangle() + .fill(.clear) + .frame(width: width, height: height) + .contentShape(.rect) + .clipped() + .allowsHitTesting(true) + .highPriorityGesture( + TapGesture() + .onEnded { _ in + imageTapped(index ?? 0) + } + ) + .accessibilityLabel(L10n.Message.Attachment.accessibilityLabel((index ?? 0) + 1)) + .accessibilityAddTraits(source.type == .video ? .startsMediaSession : .isImage) + .accessibilityAction { + imageTapped(index ?? 0) + } + } + } else if error != nil { + Color(.secondarySystemBackground) + } else { + ZStack { + Color(.secondarySystemBackground) + ProgressView() + } + } + + if showVideoIcon && source.type == .video && width > 64 && source.uploadingState == nil { + VideoPlayIcon() + .accessibilityHidden(true) + } + } + .onAppear { + if image != nil { + return + } + + source.generateThumbnail( + resize: resize, + preferredSize: CGSize(width: width, height: height) + ) { result in + switch result { + case let .success(image): + self.image = image + onImageLoaded(image) + case let .failure(error): + self.error = error + } + } + } + } + + func imageView(for image: UIImage) -> some View { + Image(uiImage: image) + .resizable() + .scaledToFill() + .aspectRatio(contentMode: .fill) + .frame(width: shouldSetFrame ? width : nil, height: shouldSetFrame ? height : nil) + .allowsHitTesting(false) + .scaleEffect(1.0001) // Needed because of SwiftUI sometimes incorrectly displaying landscape images. + .clipped() + .accessibilityHidden(true) + } +} + +struct VideoPlayIcon: View { + @Injected(\.images) var images + + var width: CGFloat = 24 + + var body: some View { + Image(uiImage: images.playFill) + .customizable() + .frame(width: width) + .foregroundColor(.white) + .modifier(ShadowModifier()) + } +} diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/LinkAttachmentView.swift b/Sources/StreamChatSwiftUI/ChatMessageList/LinkAttachmentView.swift index 7ca9a8aec..94fe8700e 100644 --- a/Sources/StreamChatSwiftUI/ChatMessageList/LinkAttachmentView.swift +++ b/Sources/StreamChatSwiftUI/ChatMessageList/LinkAttachmentView.swift @@ -36,59 +36,17 @@ public struct LinkAttachmentContainer: View { } public var body: some View { - VStack( - alignment: message.alignmentInBubble, - spacing: 0 - ) { - if let quotedMessage = message.quotedMessage { - factory.makeChatQuotedMessageView( - options: ChatQuotedMessageViewOptions( - quotedMessage: quotedMessage, - parentMessage: message, - scrolledId: $scrolledId - ) - ) - } - - if #available(iOS 15, *) { - HStack { - factory.makeAttachmentTextView(options: .init(mesage: message)) - .standardPadding() - Spacer() - } - .layoutPriority(1) - } else { - let availableWidth = width - 4 * padding - let size = message.adjustedText.frameSize(maxWidth: availableWidth) - LinkTextView( - message: message, - width: availableWidth, - textColor: UIColor(textColor(for: message)) - ) - .frame(width: availableWidth, height: size.height) - .standardPadding() - } - - if !message.linkAttachments.isEmpty { - LinkAttachmentView( - linkAttachment: message.linkAttachments[0], - width: width, - isFirst: isFirst, - onImageTap: onImageTap - ) - } - } - .padding(.bottom, 8) - .modifier( - factory.styles.makeMessageViewModifier( - for: MessageModifierInfo( - message: message, - isFirst: isFirst, - injectedBackgroundColor: colors.highlightedAccentBackground1 - ) + if !message.linkAttachments.isEmpty { + LinkAttachmentView( + linkAttachment: message.linkAttachments[0], + width: width, + isFirst: isFirst, + onImageTap: onImageTap ) - ) - .accessibilityIdentifier("LinkAttachmentContainer") + .background(MessageAttachmentsBubbleConfiguration.attachmentBackgroundColor(for: message)) + .roundWithBorder() + .accessibilityIdentifier("LinkAttachmentContainer") + } } } @@ -96,8 +54,7 @@ public struct LinkAttachmentContainer: View { public struct LinkAttachmentView: View { @Injected(\.colors) private var colors @Injected(\.fonts) private var fonts - - private let padding: CGFloat = 8 + @Injected(\.tokens) private var tokens var linkAttachment: ChatMessageLinkAttachment var width: CGFloat @@ -117,7 +74,7 @@ public struct LinkAttachmentView: View { } public var body: some View { - VStack(alignment: .leading, spacing: padding) { + VStack(alignment: .leading, spacing: 0) { if !imageHidden { ZStack { LazyImage(imageURL: linkAttachment.previewURL ?? linkAttachment.originalURL) { state in @@ -130,8 +87,8 @@ public struct LinkAttachmentView: View { .onDisappear(.cancel) .processors([ImageProcessors.Resize(width: width)]) .priority(.high) - .frame(width: width - 2 * padding, height: (width - 2 * padding) / 2) - .cornerRadius(14) + .frame(height: width / 2) + .clipped() if !authorHidden { BottomLeftView { @@ -149,7 +106,7 @@ public struct LinkAttachmentView: View { } } - VStack(alignment: .leading) { + VStack(alignment: .leading, spacing: tokens.spacingXxs) { if let title = linkAttachment.title { Text(title) .font(fonts.footnoteBold) @@ -164,10 +121,8 @@ public struct LinkAttachmentView: View { .lineLimit(3) } } - .padding(.horizontal, 8) - .padding(.bottom, 8) + .padding(tokens.spacingSm) } - .padding(.horizontal, padding) .onTapGesture { if let onImageTap { onImageTap(linkAttachment) diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/MediaAttachment.swift b/Sources/StreamChatSwiftUI/ChatMessageList/MediaAttachment.swift new file mode 100644 index 000000000..3fae48cfd --- /dev/null +++ b/Sources/StreamChatSwiftUI/ChatMessageList/MediaAttachment.swift @@ -0,0 +1,106 @@ +// +// Copyright © 2026 Stream.io Inc. All rights reserved. +// + +import StreamChat +import SwiftUI + +public final class MediaAttachment: Identifiable, Equatable, Sendable { + public let url: URL + public let type: MediaAttachmentType + public let uploadingState: AttachmentUploadingState? + public let originalWidth: Double? + public let originalHeight: Double? + + public init( + url: URL, + type: MediaAttachmentType, + uploadingState: AttachmentUploadingState? = nil, + originalWidth: Double? = nil, + originalHeight: Double? = nil + ) { + self.url = url + self.type = type + self.uploadingState = uploadingState + self.originalWidth = originalWidth + self.originalHeight = originalHeight + } + + public var id: String { + url.absoluteString + } + + @MainActor func generateThumbnail( + resize: Bool, + preferredSize: CGSize, + completion: @escaping @MainActor (Result) -> Void + ) { + let utils = InjectedValues[\.utils] + if type == .image { + utils.imageLoader.loadImage( + url: url, + imageCDN: utils.imageCDN, + resize: resize, + preferredSize: preferredSize, + completion: completion + ) + } else if type == .video { + utils.videoPreviewLoader.loadPreviewForVideo( + at: url, + completion: completion + ) + } + } + + public static func == (lhs: MediaAttachment, rhs: MediaAttachment) -> Bool { + lhs.url == rhs.url + && lhs.type == rhs.type + && lhs.uploadingState == rhs.uploadingState + && lhs.originalWidth == rhs.originalWidth + && lhs.originalHeight == rhs.originalHeight + } +} + +extension MediaAttachment { + convenience init(from attachment: ChatMessageImageAttachment) { + let url: URL + if let state = attachment.uploadingState { + url = state.localFileURL + } else { + url = attachment.imageURL + } + self.init( + url: url, + type: .image, + uploadingState: attachment.uploadingState, + originalWidth: attachment.originalWidth, + originalHeight: attachment.originalHeight + ) + } +} + +public final class MediaAttachmentType: RawRepresentable, Sendable { + public let rawValue: String + public init(rawValue: String) { + self.rawValue = rawValue + } + + public static let image = MediaAttachmentType(rawValue: "image") + public static let video = MediaAttachmentType(rawValue: "video") +} + +extension ChatMessage { + var alignmentInBubble: HorizontalAlignment { + .leading + } +} + +/// Options for the gallery view. +public final class MediaViewsOptions: Sendable { + /// The index of the selected media item. + public let selectedIndex: Int + + public init(selectedIndex: Int) { + self.selectedIndex = selectedIndex + } +} diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/MessageAttachmentsView.swift b/Sources/StreamChatSwiftUI/ChatMessageList/MessageAttachmentsView.swift new file mode 100644 index 000000000..1d4d0f616 --- /dev/null +++ b/Sources/StreamChatSwiftUI/ChatMessageList/MessageAttachmentsView.swift @@ -0,0 +1,175 @@ +// +// Copyright © 2026 Stream.io Inc. All rights reserved. +// + +import StreamChat +import SwiftUI + +public struct MessageAttachmentsView: View { + @Injected(\.tokens) private var tokens + @Injected(\.utils) private var utils + + private var messageTypeResolver: MessageTypeResolving { + utils.messageTypeResolver + } + + let factory: Factory + let message: ChatMessage + let width: CGFloat + let isFirst: Bool + @Binding var scrolledId: String? + + public init( + factory: Factory, + message: ChatMessage, + width: CGFloat, + isFirst: Bool, + scrolledId: Binding + ) { + self.factory = factory + self.message = message + self.width = width + self.isFirst = isFirst + self._scrolledId = scrolledId + } + + public var body: some View { + VStack(alignment: message.alignmentInBubble, spacing: tokens.spacingXs) { + VStack(alignment: message.isRightAligned ? .trailing : .leading, spacing: tokens.spacingXs) { + if let quotedMessage = message.quotedMessage { + factory.makeChatQuotedMessageView( + options: ChatQuotedMessageViewOptions( + quotedMessage: quotedMessage, + parentMessage: message, + availableWidth: width, + scrolledId: $scrolledId + ) + ) + } + + // Images or images and videos + if messageTypeResolver.hasImageAttachment(message: message) { + factory.makeImageAttachmentView( + options: ImageAttachmentViewOptions( + message: message, + isFirst: isFirst, + availableWidth: width, + scrolledId: $scrolledId + ) + ) + } + + // Only videos + if messageTypeResolver.hasVideoAttachment(message: message) + && !messageTypeResolver.hasImageAttachment(message: message) { + factory.makeVideoAttachmentView( + options: VideoAttachmentViewOptions( + message: message, + isFirst: isFirst, + availableWidth: width, + scrolledId: $scrolledId + ) + ) + } + + // Files + if messageTypeResolver.hasFileAttachment(message: message) { + factory.makeFileAttachmentView( + options: FileAttachmentViewOptions( + message: message, + isFirst: isFirst, + availableWidth: width, + scrolledId: $scrolledId + ) + ) + } + + // Voice recordings + if messageTypeResolver.hasVoiceRecording(message: message) { + factory.makeVoiceRecordingView( + options: VoiceRecordingViewOptions( + message: message, + isFirst: isFirst, + availableWidth: width, + scrolledId: $scrolledId + ) + ) + } + + // Link previews + if messageTypeResolver.hasLinkAttachment(message: message) + && message.attachmentCounts.keys.allSatisfy({ $0 == .linkPreview }) { + factory.makeLinkAttachmentView( + options: LinkAttachmentViewOptions( + message: message, + isFirst: isFirst, + availableWidth: width, + scrolledId: $scrolledId + ) + ) + } + } + // Text caption + if !message.text.isEmpty { + factory.makeAttachmentTextView( + options: AttachmentTextViewOptions(message: message) + ) + } + } + .if(MessageAttachmentsBubbleConfiguration.isBubbleShown(for: message)) { view in + view + .padding(MessageAttachmentsBubbleConfiguration.bubbleContentPadding(for: message)) + .modifier( + factory.styles.makeMessageViewModifier( + for: MessageModifierInfo( + message: message, + isFirst: isFirst + ) + ) + ) + } + .accessibilityElement(children: .contain) + .accessibilityIdentifier("MessageAttachmentsView") + } +} + +/// Bubble styling configuration for stacked attachments. +enum MessageAttachmentsBubbleConfiguration { + @MainActor static func isBubbleShown(for message: ChatMessage) -> Bool { + if message.hasSingleMediaAttachmentWithoutCaption { + return false + } + return true + } + + @MainActor static func bubbleContentPadding(for message: ChatMessage) -> CGFloat { + guard isBubbleShown(for: message) else { return 0 } + // Single voice and file don't have extra padding + if message.hasSingleFileOrVoiceAttachmentWithoutCaption { + return 0 + } + @Injected(\.tokens) var tokens + return tokens.spacingXs + } + + @MainActor static func attachmentBackgroundColor(for message: ChatMessage) -> Color { + // Single file and voice attachments are rendered in a bubble, but attachment itself does not have additional darker background + if message.hasSingleFileOrVoiceAttachmentWithoutCaption { + return .clear + } + @Injected(\.colors) var colors + return Color(message.isSentByCurrentUser ? colors.chatBackgroundAttachmentOutgoing : colors.chatBackgroundAttachmentIncoming) + } +} + +private extension ChatMessage { + var hasSingleFileOrVoiceAttachmentWithoutCaption: Bool { + guard text.isEmpty else { return false } + return attachmentCounts.count == 1 && (attachmentCounts[.file] == 1 || attachmentCounts[.voiceRecording] == 1) + } + + var hasSingleMediaAttachmentWithoutCaption: Bool { + guard text.isEmpty else { return false } + return attachmentCounts.count == 1 && (attachmentCounts[.image] == 1 || attachmentCounts[.video] == 1) + } +} diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/MessageMediaAttachmentContentView.swift b/Sources/StreamChatSwiftUI/ChatMessageList/MessageMediaAttachmentContentView.swift new file mode 100644 index 000000000..60e1f8654 --- /dev/null +++ b/Sources/StreamChatSwiftUI/ChatMessageList/MessageMediaAttachmentContentView.swift @@ -0,0 +1,94 @@ +// +// Copyright © 2026 Stream.io Inc. All rights reserved. +// + +import StreamChat +import SwiftUI + +/// A view that renders a single media attachment (image or video) thumbnail. +/// +/// Uses ``MediaAttachment/generateThumbnail(resize:preferredSize:completion:)`` +/// to load and display thumbnails. +/// Shows a gradient placeholder while the thumbnail is loading. +/// For video attachments, a play icon is overlaid on the thumbnail. +public struct MessageMediaAttachmentContentView: View { + @Injected(\.colors) private var colors + + /// The view factory used to create subviews. + let factory: Factory + /// The media attachment source to display. + let source: MediaAttachment + /// The width of the view. + let width: CGFloat + /// The height of the view. + let height: CGFloat + /// The corner radius applied to the view. + let cornerRadius: CGFloat + /// Whether the message is sent by the current user (outgoing). + let isOutgoing: Bool + + @State private var image: UIImage? + @State private var error: Error? + + public init( + factory: Factory, + source: MediaAttachment, + width: CGFloat, + height: CGFloat, + cornerRadius: CGFloat? = nil, + isOutgoing: Bool = false + ) { + @Injected(\.tokens) var tokens + self.factory = factory + self.source = source + self.width = width + self.height = height + self.cornerRadius = cornerRadius ?? tokens.messageBubbleRadiusAttachment + self.isOutgoing = isOutgoing + } + + public var body: some View { + ZStack { + if let image { + Image(uiImage: image) + .resizable() + .scaledToFill() + .frame(width: width, height: height) + .clipped() + } else if error != nil { + Color(.secondarySystemBackground) + } else { + placeholderGradient + } + + if image == nil && error == nil { + LoadingSpinnerView(size: LoadingSpinnerSize.large, bordered: true) + .allowsHitTesting(false) + } + + if source.type == .video && width > 64 && source.uploadingState == nil { + VideoPlayIndicatorView(size: VideoPlayIndicatorSize.medium) + .allowsHitTesting(false) + } + } + .frame(width: width, height: height) + .clipShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)) + .onAppear { + guard image == nil else { return } + source.generateThumbnail(resize: true, preferredSize: CGSize(width: width, height: height)) { result in + switch result { + case .success(let image): + self.image = image + case .failure(let failure): + self.error = failure + } + } + } + .accessibilityIdentifier("MessageMediaAttachmentContentView") + } + + private var placeholderGradient: some View { + Color(isOutgoing ? colors.chatBackgroundAttachmentOutgoing : colors.chatBackgroundAttachmentIncoming) + .shimmering() + } +} diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/MessageMediaAttachmentsContainerView.swift b/Sources/StreamChatSwiftUI/ChatMessageList/MessageMediaAttachmentsContainerView.swift new file mode 100644 index 000000000..7a7ca414c --- /dev/null +++ b/Sources/StreamChatSwiftUI/ChatMessageList/MessageMediaAttachmentsContainerView.swift @@ -0,0 +1,321 @@ +// +// Copyright © 2026 Stream.io Inc. All rights reserved. +// + +import StreamChat +import SwiftUI + +/// The orientation of media attachments in a gallery, determined by the +/// first attachment's original dimensions. +public enum MediaGalleryOrientation: Sendable { + case landscape + case portrait + case square + + /// Initializes the orientation from the given pixel dimensions. + /// + /// Uses a tolerance of 5% around a 1:1 ratio to classify near-square + /// images as ``square``. Falls back to ``landscape`` when dimensions + /// are unavailable. + public init(width: Double?, height: Double?) { + guard let width, let height, width > 0, height > 0 else { + self = .landscape + return + } + let ratio = width / height + if ratio > 1.05 { + self = .landscape + } else if ratio < 0.95 { + self = .portrait + } else { + self = .square + } + } +} + +/// A container view that displays media (image and video) attachments in a +/// gallery grid layout. +/// +/// The layout adapts based on the orientation (landscape, portrait, square) +/// of the first image attachment and the total number of media items: +/// - **1 item**: Full-bleed single image whose container aspect ratio +/// matches the detected orientation. +/// - **2 items**: Two items side by side in a landscape-aspect container. +/// - **3 items**: One item on the left (full height) with two stacked +/// on the right. +/// - **4+ items**: A 2×2 grid. When there are more than four items the +/// last visible cell shows a "+N" overlay with the remaining count. +/// +/// This view does **not** render message text or a bubble background. +/// Tapping any cell opens the full-screen gallery. +public struct MessageMediaAttachmentsContainerView: View { + @Injected(\.colors) private var colors + @Injected(\.fonts) private var fonts + @Injected(\.tokens) private var tokens + + let factory: Factory + let message: ChatMessage + let width: CGFloat + + @State private var galleryShown = false + @State private var selectedIndex = 0 + private var spacing: CGFloat { tokens.spacingXxxs } + private var cornerRadius: CGFloat { tokens.messageBubbleRadiusAttachment } + private let maxDisplayedItems = 4 + + public init( + factory: Factory, + message: ChatMessage, + width: CGFloat + ) { + self.factory = factory + self.message = message + self.width = width + } + + public var body: some View { + galleryGrid + .fullScreenCover(isPresented: $galleryShown, onDismiss: { + selectedIndex = 0 + }) { + factory.makeGalleryView( + options: GalleryViewOptions( + mediaAttachments: sources, + message: message, + isShown: $galleryShown, + options: .init(selectedIndex: selectedIndex) + ) + ) + } + .accessibilityIdentifier("MessageMediaAttachmentsContainerView") + } + + // MARK: - Layout + + @ViewBuilder + private var galleryGrid: some View { + let items = sources + let size = containerSize(for: items.count) + + Group { + switch items.count { + case 0: + EmptyView() + case 1: + singleItemLayout(items[0], width: size.width, height: size.height) + case 2: + twoItemLayout(items, size: size) + case 3: + threeItemLayout(items, size: size) + default: + fourPlusItemLayout(items, size: size) + } + } + .frame(width: size.width, height: size.height) + } + + private func singleItemLayout( + _ item: MediaAttachment, + width: CGFloat, + height: CGFloat + ) -> some View { + mediaCell(item, width: width, height: height, index: 0) + } + + @ViewBuilder + private func twoItemLayout( + _ items: [MediaAttachment], + size: CGSize + ) -> some View { + if orientation == .landscape { + // Landscape: stacked vertically + let cellHeight = (size.height - spacing) / 2 + VStack(spacing: spacing) { + mediaCell(items[0], width: size.width, height: cellHeight, index: 0) + mediaCell(items[1], width: size.width, height: cellHeight, index: 1) + } + } else { + // Portrait / Square: side by side + let cellWidth = (size.width - spacing) / 2 + HStack(spacing: spacing) { + mediaCell(items[0], width: cellWidth, height: size.height, index: 0) + mediaCell(items[1], width: cellWidth, height: size.height, index: 1) + } + } + } + + @ViewBuilder + private func threeItemLayout( + _ items: [MediaAttachment], + size: CGSize + ) -> some View { + if orientation == .landscape { + // Landscape: top item full width, bottom two side by side + let cellWidth = (size.width - spacing) / 2 + let cellHeight = (size.height - spacing) / 2 + VStack(spacing: spacing) { + mediaCell(items[0], width: size.width, height: cellHeight, index: 0) + HStack(spacing: spacing) { + mediaCell(items[1], width: cellWidth, height: cellHeight, index: 1) + mediaCell(items[2], width: cellWidth, height: cellHeight, index: 2) + } + } + } else { + // Portrait / Square: left item full height, right two stacked + let cellWidth = (size.width - spacing) / 2 + let cellHeight = (size.height - spacing) / 2 + HStack(spacing: spacing) { + mediaCell(items[0], width: cellWidth, height: size.height, index: 0) + VStack(spacing: spacing) { + mediaCell(items[1], width: cellWidth, height: cellHeight, index: 1) + mediaCell(items[2], width: cellWidth, height: cellHeight, index: 2) + } + } + } + } + + @ViewBuilder + private func fourPlusItemLayout( + _ items: [MediaAttachment], + size: CGSize + ) -> some View { + let cellWidth = (size.width - spacing) / 2 + let cellHeight = (size.height - spacing) / 2 + if orientation == .landscape { + // Landscape: two rows (VStack of HStacks) + VStack(spacing: spacing) { + HStack(spacing: spacing) { + mediaCell(items[0], width: cellWidth, height: cellHeight, index: 0) + mediaCell(items[1], width: cellWidth, height: cellHeight, index: 1) + } + HStack(spacing: spacing) { + mediaCell(items[2], width: cellWidth, height: cellHeight, index: 2) + overflowCell(items[3], width: cellWidth, height: cellHeight, index: 3) + } + } + } else { + // Portrait / Square: two columns (HStack of VStacks) + HStack(spacing: spacing) { + VStack(spacing: spacing) { + mediaCell(items[0], width: cellWidth, height: cellHeight, index: 0) + mediaCell(items[2], width: cellWidth, height: cellHeight, index: 2) + } + VStack(spacing: spacing) { + mediaCell(items[1], width: cellWidth, height: cellHeight, index: 1) + overflowCell(items[3], width: cellWidth, height: cellHeight, index: 3) + } + } + } + } + + private func overflowCell( + _ item: MediaAttachment, + width: CGFloat, + height: CGFloat, + index: Int + ) -> some View { + ZStack { + mediaCell(item, width: width, height: height, index: index) + if remainingCount > 0 { + Color.black.opacity(0.4) + .clipShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)) + .allowsHitTesting(false) + Text("+\(remainingCount)") + .foregroundColor(Color(colors.staticColorText)) + .font(fonts.title) + .allowsHitTesting(false) + } + } + .frame(width: width, height: height) + } + + // MARK: - Cell + + private func mediaCell( + _ item: MediaAttachment, + width: CGFloat, + height: CGFloat, + index: Int + ) -> some View { + MessageMediaAttachmentContentView( + factory: factory, + source: item, + width: width, + height: height, + cornerRadius: cornerRadius, + isOutgoing: message.isSentByCurrentUser + ) + .withUploadingStateIndicator(for: item.uploadingState, url: item.url) + .contentShape(Rectangle()) + .onTapGesture { + if message.localState == nil { + selectedIndex = index + galleryShown = true + } + } + .accessibilityLabel(L10n.Message.Attachment.accessibilityLabel(index + 1)) + .accessibilityAddTraits(item.type == .video ? .startsMediaSession : .isImage) + } + + // MARK: - Data + + private var orientation: MediaGalleryOrientation { + if let first = sources.first { + return MediaGalleryOrientation( + width: first.originalWidth, + height: first.originalHeight + ) + } + return .landscape + } + + private var sources: [MediaAttachment] { + let videoSources = message.videoAttachments.map { attachment in + MediaAttachment( + url: attachment.videoURL, + type: .video, + uploadingState: attachment.uploadingState + ) + } + let imageSources = message.imageAttachments.map { attachment in + let url: URL + if let state = attachment.uploadingState { + url = state.localFileURL + } else { + url = attachment.imageURL + } + return MediaAttachment( + url: url, + type: .image, + uploadingState: attachment.uploadingState, + originalWidth: attachment.originalWidth, + originalHeight: attachment.originalHeight + ) + } + return videoSources + imageSources + } + + private func containerSize(for itemCount: Int) -> CGSize { + guard itemCount > 0 else { return .zero } + let maxItemWidth = width + if itemCount == 1 { + switch orientation { + case .landscape: + // Width-constrained: 256×192 at max width + return CGSize(width: maxItemWidth, height: maxItemWidth * 3.0 / 4.0) + case .portrait: + // Height-constrained: 192×256 at max width + return CGSize(width: maxItemWidth * 3.0 / 4.0, height: maxItemWidth) + case .square: + return CGSize(width: maxItemWidth, height: maxItemWidth) + } + } else { + // Multi-item always uses landscape ratio + return CGSize(width: maxItemWidth, height: maxItemWidth * 3.0 / 4.0) + } + } + + private var remainingCount: Int { + max(sources.count - maxDisplayedItems, 0) + } +} diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/MessageView.swift b/Sources/StreamChatSwiftUI/ChatMessageList/MessageView.swift index 087c687d9..7c627e61f 100644 --- a/Sources/StreamChatSwiftUI/ChatMessageList/MessageView.swift +++ b/Sources/StreamChatSwiftUI/ChatMessageList/MessageView.swift @@ -53,74 +53,24 @@ public struct MessageView: View { ) } else if let poll = message.poll { factory.makePollView(options: PollViewOptions(message: message, poll: poll, isFirst: isFirst)) - } else if !message.attachmentCounts.isEmpty { - let hasOnlyLinks = { message.attachmentCounts.keys.allSatisfy { $0 == .linkPreview } } - if messageTypeResolver.hasLinkAttachment(message: message) && hasOnlyLinks() { - factory.makeLinkAttachmentView( - options: LinkAttachmentViewOptions( - message: message, - isFirst: isFirst, - availableWidth: contentWidth, - scrolledId: $scrolledId - ) - ) - } - - if messageTypeResolver.hasFileAttachment(message: message) { - factory.makeFileAttachmentView( - options: FileAttachmentViewOptions( - message: message, - isFirst: isFirst, - availableWidth: contentWidth, - scrolledId: $scrolledId - ) - ) - } - - if messageTypeResolver.hasImageAttachment(message: message) { - factory.makeImageAttachmentView( - options: ImageAttachmentViewOptions( - message: message, - isFirst: isFirst, - availableWidth: contentWidth, - scrolledId: $scrolledId - ) - ) - } - - if messageTypeResolver.hasGiphyAttachment(message: message) { - factory.makeGiphyAttachmentView( - options: GiphyAttachmentViewOptions( - message: message, - isFirst: isFirst, - availableWidth: contentWidth, - scrolledId: $scrolledId - ) - ) - } - - if messageTypeResolver.hasVideoAttachment(message: message) - && !messageTypeResolver.hasImageAttachment(message: message) { - factory.makeVideoAttachmentView( - options: VideoAttachmentViewOptions( - message: message, - isFirst: isFirst, - availableWidth: contentWidth, - scrolledId: $scrolledId - ) + } else if messageTypeResolver.hasGiphyAttachment(message: message) { + factory.makeGiphyAttachmentView( + options: GiphyAttachmentViewOptions( + message: message, + isFirst: isFirst, + availableWidth: contentWidth, + scrolledId: $scrolledId ) - } - - if messageTypeResolver.hasVoiceRecording(message: message) { - factory.makeVoiceRecordingView( - options: VoiceRecordingViewOptions( - message: message, - isFirst: isFirst, - availableWidth: contentWidth, - scrolledId: $scrolledId - ) + ) + } else if !message.attachmentCounts.isEmpty || message.quotedMessage != nil { + factory.makeMessageAttachmentsView( + options: MessageAttachmentsViewOptions( + message: message, + isFirst: isFirst, + availableWidth: contentWidth, + scrolledId: $scrolledId ) - } + ) } else { if message.shouldRenderAsJumbomoji { factory.makeEmojiTextView( @@ -203,17 +153,7 @@ public struct MessageTextView: View { alignment: message.alignmentInBubble, spacing: 0 ) { - if let quotedMessage = message.quotedMessage { - factory.makeChatQuotedMessageView( - options: ChatQuotedMessageViewOptions( - quotedMessage: quotedMessage, - parentMessage: message, - scrolledId: $scrolledId - ) - ) - } - - factory.makeAttachmentTextView(options: .init(mesage: message)) + factory.makeStreamTextView(options: .init(message: message)) .padding(.leading, leadingPadding) .padding(.trailing, trailingPadding) .padding(.top, topPadding) @@ -294,16 +234,6 @@ struct StreamTextView: View { } } -// Options for the attachment text view. -public class AttachmentTextViewOptions { - // The message to display the text for. - public let message: ChatMessage - - public init(mesage: ChatMessage) { - self.message = mesage - } -} - @available(iOS 15, *) public struct LinkDetectionTextView: View { @Environment(\.layoutDirection) var layoutDirection diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/QuotedMessageView/ChatQuotedMessageView.swift b/Sources/StreamChatSwiftUI/ChatMessageList/QuotedMessageView/ChatQuotedMessageView.swift index 68a2f2373..8bd270745 100644 --- a/Sources/StreamChatSwiftUI/ChatMessageList/QuotedMessageView/ChatQuotedMessageView.swift +++ b/Sources/StreamChatSwiftUI/ChatMessageList/QuotedMessageView/ChatQuotedMessageView.swift @@ -14,22 +14,26 @@ public struct ChatQuotedMessageView: View { private let factory: Factory private let quotedMessage: ChatMessage private let parentMessageSentByCurrentUser: Bool + private let availableWidth: CGFloat? @Binding private var scrolledId: String? /// Creates a chat quoted message view. /// - Parameters: /// - factory: The view factory to create the quoted message view. /// - quotedMessage: The quoted message to display. + /// - availableWidth: The available width for the quoted message view. /// - scrolledId: A binding to the scrolled message ID for navigation to the quoted message. public init( factory: Factory, quotedMessage: ChatMessage, parentMessage: ChatMessage, + availableWidth: CGFloat? = nil, scrolledId: Binding ) { self.factory = factory self.quotedMessage = quotedMessage parentMessageSentByCurrentUser = parentMessage.isSentByCurrentUser + self.availableWidth = availableWidth self._scrolledId = scrolledId } @@ -40,6 +44,7 @@ public struct ChatQuotedMessageView: View { outgoing: parentMessageSentByCurrentUser ) ) + .frame(width: availableWidth) .modifier(ReferenceMessageViewBackgroundModifier( backgroundColor: Color( parentMessageSentByCurrentUser @@ -48,7 +53,6 @@ public struct ChatQuotedMessageView: View { ) )) .frame(height: 56) - .padding(tokens.spacingXs) .onTapGesture { scrolledId = quotedMessage.messageId } diff --git a/Sources/StreamChatSwiftUI/ChatMessageList/VideoAttachmentView.swift b/Sources/StreamChatSwiftUI/ChatMessageList/VideoAttachmentView.swift deleted file mode 100644 index 1f8ae0683..000000000 --- a/Sources/StreamChatSwiftUI/ChatMessageList/VideoAttachmentView.swift +++ /dev/null @@ -1,232 +0,0 @@ -// -// Copyright © 2026 Stream.io Inc. All rights reserved. -// - -import AVKit -import StreamChat -import SwiftUI - -public struct VideoAttachmentsContainer: View { - var factory: Factory - let message: ChatMessage - let width: CGFloat - @Binding var scrolledId: String? - - public var body: some View { - VStack(spacing: 0) { - if let quotedMessage = message.quotedMessage { - VStack { - factory.makeChatQuotedMessageView( - options: ChatQuotedMessageViewOptions( - quotedMessage: quotedMessage, - parentMessage: message, - scrolledId: $scrolledId - ) - ) - - VideoAttachmentsList( - factory: factory, - message: message, - width: width - ) - } - .modifier( - factory.styles.makeMessageViewModifier( - for: MessageModifierInfo( - message: message, - isFirst: false - ) - ) - ) - } else { - VideoAttachmentsList( - factory: factory, - message: message, - width: width - ) - } - - if !message.text.isEmpty { - AttachmentTextView(factory: factory, message: message) - .frame(width: width) - } - } - .if(!message.text.isEmpty, transform: { view in - view.modifier( - factory.styles.makeMessageViewModifier( - for: MessageModifierInfo( - message: message, - isFirst: true, - cornerRadius: 24 - ) - ) - ) - }) - .accessibilityIdentifier("VideoAttachmentsContainer") - } -} - -public struct VideoAttachmentsList: View { - let factory: Factory - let message: ChatMessage - let width: CGFloat - - public init( - factory: Factory = DefaultViewFactory.shared, - message: ChatMessage, - width: CGFloat - ) { - self.factory = factory - self.message = message - self.width = width - } - - public var body: some View { - VStack { - ForEach(message.videoAttachments, id: \.self) { attachment in - VideoAttachmentView( - factory: factory, - attachment: attachment, - message: message, - width: width - ) - .withUploadingStateIndicator( - for: attachment.uploadingState, - url: attachment.videoURL - ) - } - } - } -} - -public struct VideoAttachmentView: View { - let factory: Factory - let attachment: ChatMessageVideoAttachment - let message: ChatMessage - let width: CGFloat - var ratio: CGFloat = 0.75 - var cornerRadius: CGFloat = 24 - - public init( - factory: Factory = DefaultViewFactory.shared, - attachment: ChatMessageVideoAttachment, - message: ChatMessage, - width: CGFloat, - ratio: CGFloat = 0.75, - cornerRadius: CGFloat = 24 - ) { - self.factory = factory - self.attachment = attachment - self.message = message - self.width = width - self.ratio = ratio - self.cornerRadius = cornerRadius - } - - @State var previewImage: UIImage? - @State var error: Error? - @State var fullScreenShown = false - - public var body: some View { - VideoAttachmentContentView( - factory: factory, - attachment: attachment, - message: message, - width: width, - ratio: ratio, - cornerRadius: cornerRadius - ) - .accessibilityIdentifier("VideoAttachmentView") - } -} - -struct VideoAttachmentContentView: View { - @Injected(\.utils) private var utils - @Injected(\.images) private var images - - private var videoPreviewLoader: VideoPreviewLoader { - utils.videoPreviewLoader - } - - let factory: Factory - let attachment: ChatMessageVideoAttachment - let message: ChatMessage - let width: CGFloat - var ratio: CGFloat = 0.75 - var cornerRadius: CGFloat = 24 - - @State var previewImage: UIImage? - @State var error: Error? - @State var fullScreenShown = false - - public var body: some View { - ZStack { - if let previewImage { - Image(uiImage: previewImage) - .resizable() - .scaledToFill() - .clipped() - .allowsHitTesting(false) - .accessibilityHidden(true) - - if width > 64 && attachment.uploadingState == nil { - VStack { - VideoPlayIcon() - } - .frame(width: width, height: width * ratio) - .contentShape(Rectangle()) - .clipped() - .onTapGesture { - fullScreenShown = true - } - .accessibilityAction { - fullScreenShown = true - } - } - } else if error != nil { - Color(.secondarySystemBackground) - } else { - ZStack { - Color(.secondarySystemBackground) - ProgressView() - } - } - } - .frame(width: width, height: width * ratio) - .cornerRadius(cornerRadius) - .fullScreenCover(isPresented: $fullScreenShown) { - factory.makeVideoPlayerView( - options: VideoPlayerViewOptions( - attachment: attachment, - message: message, - isShown: $fullScreenShown, - options: .init(selectedIndex: 0) - ) - ) - } - .onAppear { - videoPreviewLoader.loadPreviewForVideo(at: attachment.videoURL) { result in - switch result { - case let .success(image): - previewImage = image - case let .failure(error): - self.error = error - } - } - } - } -} - -struct VideoPlayIcon: View { - @Injected(\.images) var images - - var width: CGFloat = 24 - - var body: some View { - Image(uiImage: images.playFill) - .customizable() - .frame(width: width) - .foregroundColor(.white) - .modifier(ShadowModifier()) - } -} diff --git a/Sources/StreamChatSwiftUI/CommonViews/LoadingSpinnerView.swift b/Sources/StreamChatSwiftUI/CommonViews/LoadingSpinnerView.swift new file mode 100644 index 000000000..aa9af6459 --- /dev/null +++ b/Sources/StreamChatSwiftUI/CommonViews/LoadingSpinnerView.swift @@ -0,0 +1,71 @@ +// +// Copyright © 2026 Stream.io Inc. All rights reserved. +// + +import SwiftUI + +/// Predefined sizes for ``LoadingSpinnerView``. +public enum LoadingSpinnerSize: Sendable { + @MainActor public static var large: CGFloat = 32 + @MainActor public static var small: CGFloat = 20 + @MainActor public static var extraSmall: CGFloat = 16 +} + +/// A circular spinner badge used as a loading indicator over media content. +/// +/// Renders a white circular badge with a border and an animated arc spinner +/// inside. The spinner consists of a light gray track circle with a blue +/// accent arc that rotates continuously. +public struct LoadingSpinnerView: View { + @Injected(\.colors) private var colors + + let size: CGFloat + let bordered: Bool + + @State private var isAnimating = false + + public init(size: CGFloat, bordered: Bool) { + self.size = size + self.bordered = bordered + } + + public var body: some View { + ZStack { + Circle() + .fill(Color(colors.backgroundElevationElevation0)) + .overlay( + Circle() + .inset(by: -1) + .stroke(colors.backgroundElevationElevation0.toColor, lineWidth: bordered ? 2 : 0) + ) + Circle() + .stroke(Color(colors.borderCoreDefault), lineWidth: strokeWidth) + .frame(width: spinnerDiameter, height: spinnerDiameter) + Circle() + .trim(from: 0, to: 0.25) + .stroke( + Color(colors.accentPrimary), + style: StrokeStyle(lineWidth: strokeWidth, lineCap: .round) + ) + .frame(width: spinnerDiameter, height: spinnerDiameter) + .rotationEffect(.degrees(isAnimating ? 360 : 0)) + .animation( + .linear(duration: 1).repeatForever(autoreverses: false), + value: isAnimating + ) + } + .frame(width: size, height: size) + .onAppear { isAnimating = true } + .accessibilityIdentifier("LoadingSpinnerView") + } + + /// Diameter of the spinner arc, inset from the badge border. + private var spinnerDiameter: CGFloat { + size - 2 + } + + /// Stroke width of the arc, scaled proportionally to the badge size. + private var strokeWidth: CGFloat { + max(size * 3.0 / 32.0, 1.5) + } +} diff --git a/Sources/StreamChatSwiftUI/CommonViews/VideoPlayIndicatorView.swift b/Sources/StreamChatSwiftUI/CommonViews/VideoPlayIndicatorView.swift new file mode 100644 index 000000000..7c3199fe1 --- /dev/null +++ b/Sources/StreamChatSwiftUI/CommonViews/VideoPlayIndicatorView.swift @@ -0,0 +1,78 @@ +// +// Copyright © 2026 Stream.io Inc. All rights reserved. +// + +import SwiftUI + +/// Predefined sizes for ``VideoPlayIndicatorView``. +public enum VideoPlayIndicatorSize { + @MainActor public static var extraLarge: CGFloat = 64 + @MainActor public static var large: CGFloat = 48 + @MainActor public static var medium: CGFloat = 40 + @MainActor public static var small: CGFloat = 20 +} + +/// A circular play/pause indicator for video content. +/// +/// Renders a filled circle with a play or pause icon centered inside. +/// The ``playing`` parameter controls which icon is shown. +public struct VideoPlayIndicatorView: View { + @Injected(\.colors) private var colors + @Injected(\.images) private var images + @Environment(\.layoutDirection) private var layoutDirection + + let size: CGFloat + let playing: Bool + + public init(size: CGFloat, playing: Bool = false) { + self.size = size + self.playing = playing + } + + public var body: some View { + Circle() + .fill(Color(colors.controlPlayControlBackground)) + .frame(width: size, height: size) + .overlay( + Image(uiImage: playing ? images.pauseFill : images.playFill) + .renderingMode(.template) + .resizable() + .scaledToFit() + .frame(width: iconSize, height: iconSize) + .foregroundColor(Color(colors.controlPlayControlIcon)) + .offset(x: offsetX) + ) + .accessibilityIdentifier("VideoPlayIndicatorView") + } + + private var iconSize: CGFloat { + size / 2 + } + + private var offsetX: CGFloat { + guard !playing else { return 0 } + let offset = size / 16 + return layoutDirection == .rightToLeft ? -offset : offset + } +} + +@available(iOS 26, *) +#Preview { + @Previewable let streamChat = StreamChat(chatClient: .init(config: .init(apiKeyString: "Preview"))) + + VStack(spacing: 16) { + HStack(spacing: 12) { + VideoPlayIndicatorView(size: VideoPlayIndicatorSize.extraLarge) + VideoPlayIndicatorView(size: VideoPlayIndicatorSize.large) + VideoPlayIndicatorView(size: VideoPlayIndicatorSize.medium) + VideoPlayIndicatorView(size: VideoPlayIndicatorSize.small) + } + HStack(spacing: 12) { + VideoPlayIndicatorView(size: VideoPlayIndicatorSize.extraLarge, playing: true) + VideoPlayIndicatorView(size: VideoPlayIndicatorSize.large, playing: true) + VideoPlayIndicatorView(size: VideoPlayIndicatorSize.medium, playing: true) + VideoPlayIndicatorView(size: VideoPlayIndicatorSize.small, playing: true) + } + } + .padding() +} diff --git a/Sources/StreamChatSwiftUI/ViewFactory/DefaultViewFactory.swift b/Sources/StreamChatSwiftUI/ViewFactory/DefaultViewFactory.swift index 0b004f234..72317d469 100644 --- a/Sources/StreamChatSwiftUI/ViewFactory/DefaultViewFactory.swift +++ b/Sources/StreamChatSwiftUI/ViewFactory/DefaultViewFactory.swift @@ -289,10 +289,10 @@ extension ViewFactory { ) } - public func makeImageAttachmentView( - options: ImageAttachmentViewOptions + public func makeMessageAttachmentsView( + options: MessageAttachmentsViewOptions ) -> some View { - ImageAttachmentContainer( + MessageAttachmentsView( factory: self, message: options.message, width: options.availableWidth, @@ -300,7 +300,17 @@ extension ViewFactory { scrolledId: options.scrolledId ) } - + + public func makeImageAttachmentView( + options: ImageAttachmentViewOptions + ) -> some View { + MessageMediaAttachmentsContainerView( + factory: self, + message: options.message, + width: options.availableWidth + ) + } + public func makeGiphyAttachmentView( options: GiphyAttachmentViewOptions ) -> some View { @@ -312,7 +322,7 @@ extension ViewFactory { scrolledId: options.scrolledId ) } - + public func makeLinkAttachmentView( options: LinkAttachmentViewOptions ) -> some View { @@ -324,7 +334,7 @@ extension ViewFactory { scrolledId: options.scrolledId ) } - + public func makeFileAttachmentView( options: FileAttachmentViewOptions ) -> some View { @@ -336,15 +346,14 @@ extension ViewFactory { scrolledId: options.scrolledId ) } - + public func makeVideoAttachmentView( options: VideoAttachmentViewOptions ) -> some View { - VideoAttachmentsContainer( + MessageMediaAttachmentsContainerView( factory: self, message: options.message, - width: options.availableWidth, - scrolledId: options.scrolledId + width: options.availableWidth ) } @@ -818,6 +827,7 @@ extension ViewFactory { factory: self, quotedMessage: options.quotedMessage, parentMessage: options.parentMessage, + availableWidth: options.availableWidth, scrolledId: options.scrolledId ) } @@ -995,6 +1005,12 @@ extension ViewFactory { public func makeAttachmentTextView( options: AttachmentTextViewOptions + ) -> some View { + AttachmentTextView(factory: self, message: options.message) + } + + public func makeStreamTextView( + options: StreamTextViewOptions ) -> some View { StreamTextView(message: options.message) } diff --git a/Sources/StreamChatSwiftUI/ViewFactory/Options/AttachmentViewFactoryOptions.swift b/Sources/StreamChatSwiftUI/ViewFactory/Options/AttachmentViewFactoryOptions.swift index 068e42c16..24504fe7b 100644 --- a/Sources/StreamChatSwiftUI/ViewFactory/Options/AttachmentViewFactoryOptions.swift +++ b/Sources/StreamChatSwiftUI/ViewFactory/Options/AttachmentViewFactoryOptions.swift @@ -19,7 +19,7 @@ public final class ImageAttachmentViewOptions: Sendable { public let availableWidth: CGFloat /// Binding to the currently scrolled message ID. public let scrolledId: Binding - + public init( message: ChatMessage, isFirst: Bool, @@ -136,7 +136,7 @@ public final class VideoAttachmentViewOptions: Sendable { public let availableWidth: CGFloat /// Binding to the currently scrolled message ID. public let scrolledId: Binding - + public init( message: ChatMessage, isFirst: Bool, @@ -176,6 +176,32 @@ public final class VoiceRecordingViewOptions: Sendable { } } +// MARK: - Message Attachments Options + +/// Options for creating the message attachments view. +public final class MessageAttachmentsViewOptions: Sendable { + /// The message containing the attachments. + public let message: ChatMessage + /// Whether this is the first message in a group. + public let isFirst: Bool + /// The available width for the attachments. + public let availableWidth: CGFloat + /// Binding to the currently scrolled message ID. + public let scrolledId: Binding + + public init( + message: ChatMessage, + isFirst: Bool, + availableWidth: CGFloat, + scrolledId: Binding + ) { + self.message = message + self.isFirst = isFirst + self.availableWidth = availableWidth + self.scrolledId = scrolledId + } +} + // MARK: - Custom Attachment Options /// Options for creating the custom attachment view. diff --git a/Sources/StreamChatSwiftUI/ViewFactory/Options/ComposerViewFactoryOptions.swift b/Sources/StreamChatSwiftUI/ViewFactory/Options/ComposerViewFactoryOptions.swift index a477b317e..9bdd70055 100644 --- a/Sources/StreamChatSwiftUI/ViewFactory/Options/ComposerViewFactoryOptions.swift +++ b/Sources/StreamChatSwiftUI/ViewFactory/Options/ComposerViewFactoryOptions.swift @@ -397,16 +397,20 @@ public final class ChatQuotedMessageViewOptions: Sendable { public let quotedMessage: ChatMessage /// The parent message which is quoting another message. public let parentMessage: ChatMessage + /// The available width for the quoted message view. + public let availableWidth: CGFloat? /// Binding to the currently scrolled message ID. public let scrolledId: Binding public init( quotedMessage: ChatMessage, parentMessage: ChatMessage, + availableWidth: CGFloat? = nil, scrolledId: Binding ) { self.quotedMessage = quotedMessage self.parentMessage = parentMessage + self.availableWidth = availableWidth self.scrolledId = scrolledId } } diff --git a/Sources/StreamChatSwiftUI/ViewFactory/Options/MessageViewFactoryOptions.swift b/Sources/StreamChatSwiftUI/ViewFactory/Options/MessageViewFactoryOptions.swift index 2170becca..2b2f3c6cf 100644 --- a/Sources/StreamChatSwiftUI/ViewFactory/Options/MessageViewFactoryOptions.swift +++ b/Sources/StreamChatSwiftUI/ViewFactory/Options/MessageViewFactoryOptions.swift @@ -137,6 +137,29 @@ public final class MessageTextViewOptions: Sendable { } } +/// Options for the reusable stream text view. +/// +/// Used by ``ViewFactory/makeStreamTextView(options:)`` which is shared +/// across standalone text messages and attachment text captions. +public class StreamTextViewOptions { + /// The message whose text should be displayed. + public let message: ChatMessage + + public init(message: ChatMessage) { + self.message = message + } +} + +/// Options for the attachment text caption view shown inside ``MessageAttachmentsView``. +public class AttachmentTextViewOptions { + /// The message whose text caption should be displayed. + public let message: ChatMessage + + public init(message: ChatMessage) { + self.message = message + } +} + // MARK: - Message Date Options /// Options for creating the message date view. diff --git a/Sources/StreamChatSwiftUI/ViewFactory/ViewFactory.swift b/Sources/StreamChatSwiftUI/ViewFactory/ViewFactory.swift index 7d261383d..6d3556b75 100644 --- a/Sources/StreamChatSwiftUI/ViewFactory/ViewFactory.swift +++ b/Sources/StreamChatSwiftUI/ViewFactory/ViewFactory.swift @@ -186,6 +186,11 @@ import SwiftUI /// - Returns: view shown in the header of the last message. func makeLastInGroupHeaderView(options: LastInGroupHeaderViewOptions) -> LastInGroupHeaderView + associatedtype MessageAttachmentsViewType: View + /// Creates the message attachments view. + /// - Parameter options: the options for creating the message attachments view. + func makeMessageAttachmentsView(options: MessageAttachmentsViewOptions) -> MessageAttachmentsViewType + associatedtype ImageAttachmentViewType: View /// Creates the image attachment view. /// - Parameter options: the options for creating the image attachment view. @@ -215,7 +220,7 @@ import SwiftUI /// - Parameter options: the options for creating the video attachment view. /// - Returns: view displayed in the video attachment slot. func makeVideoAttachmentView(options: VideoAttachmentViewOptions) -> VideoAttachmentViewType - + associatedtype GalleryViewType: View /// Creates the gallery view. /// - Parameter options: the options for creating the gallery view. @@ -616,10 +621,21 @@ import SwiftUI func makeAddUsersView(options: AddUsersViewOptions) -> AddUsersViewType associatedtype AttachmentTextViewType: View - /// Creates a view for displaying the text of an attachment. - /// - Parameter options: Configuration options for the attachment text view, such as message. - /// - Returns: The view shown in the attachment text slot. + /// Creates a text caption view displayed below attachments inside ``MessageAttachmentsView``. + /// - Parameter options: Configuration options for the attachment text view. + /// - Returns: The text caption view shown beneath attachments. func makeAttachmentTextView( options: AttachmentTextViewOptions ) -> AttachmentTextViewType + + associatedtype StreamTextViewType: View + /// Creates a reusable text view for displaying message text. + /// + /// This view is shared across multiple message layouts, including + /// standalone text messages and text captions within attachment views. + /// - Parameter options: Configuration options such as the message to display. + /// - Returns: The view shown in the text slot. + func makeStreamTextView( + options: StreamTextViewOptions + ) -> StreamTextViewType } diff --git a/StreamChatSwiftUI.xcodeproj/project.pbxproj b/StreamChatSwiftUI.xcodeproj/project.pbxproj index c76c69d60..2e308d087 100644 --- a/StreamChatSwiftUI.xcodeproj/project.pbxproj +++ b/StreamChatSwiftUI.xcodeproj/project.pbxproj @@ -251,6 +251,7 @@ Tests/CommonViews/SearchBar_Tests.swift, Tests/CommonViews/SnackBarView_Tests.swift, Tests/CommonViews/StreamButton_Tests.swift, + Tests/CommonViews/VideoPlayIndicatorView_Tests.swift, Tests/StreamChatTestCase.swift, Tests/Utils/ChatClientExtensions_Tests.swift, Tests/Utils/ChatMessageExtensions_Tests.swift, diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_nonEmptySnapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_nonEmptySnapshot.1.png index 1894813f4..ebaf4414d 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_nonEmptySnapshot.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_nonEmptySnapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_themedSnapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_themedSnapshot.1.png index 90d2412d8..837b31e3c 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_themedSnapshot.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_themedSnapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_withDownloadEnabled.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_withDownloadEnabled.1.png index f9e3d16c0..3ebcc0ba0 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_withDownloadEnabled.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_withDownloadEnabled.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/ChatChannelTestHelpers.swift b/StreamChatSwiftUITests/Tests/ChatChannel/ChatChannelTestHelpers.swift index fcbde78f5..2997b7e26 100644 --- a/StreamChatSwiftUITests/Tests/ChatChannel/ChatChannelTestHelpers.swift +++ b/StreamChatSwiftUITests/Tests/ChatChannel/ChatChannelTestHelpers.swift @@ -93,6 +93,56 @@ class ChatChannelTestHelpers { .asAnyAttachment } + static func imageAttachment( + url: URL = testURL, + originalWidth: Double? = nil, + originalHeight: Double? = nil, + state: LocalAttachmentState = .pendingUpload + ) -> AnyChatMessageAttachment { + let attachmentFile = AttachmentFile(type: .png, size: 0, mimeType: "image/png") + let uploadingState = AttachmentUploadingState( + localFileURL: url, + state: state, + file: attachmentFile + ) + return ChatMessageImageAttachment( + id: .unique, + type: .image, + payload: ImageAttachmentPayload( + title: "test", + imageRemoteURL: url, + file: attachmentFile, + originalWidth: originalWidth, + originalHeight: originalHeight, + extraData: [:] + ), + downloadingState: nil, + uploadingState: uploadingState + ) + .asAnyAttachment + } + + static func imageAttachments( + count: Int, + originalWidth: Double? = nil, + originalHeight: Double? = nil + ) -> [AnyChatMessageAttachment] { + let urls = [ + XCTestCase.TestImages.yoda.url, + XCTestCase.TestImages.chewbacca.url, + XCTestCase.TestImages.r2.url, + XCTestCase.TestImages.vader.url, + XCTestCase.TestImages.yoda.url + ] + return (0.. some View { MessageItemView( factory: DefaultViewFactory.shared, @@ -868,7 +881,46 @@ import XCTest viewModel: messageViewModel ?? MessageViewModel(message: message, channel: channel ?? .mockDMChannel()) ) .environment(\.highlightedMessageId, highlightedMessageId) - .frame(width: 375, height: 200) + .frame(width: 375, height: height) + } + + private func assertMediaGallerySnapshot( + orientation: MediaGalleryOrientation, + count: Int, + file: StaticString = #filePath, + testName: String = #function, + line: UInt = #line + ) { + let dimensions: (width: Double, height: Double) = { + switch orientation { + case .landscape: return (1600, 1200) + case .portrait: return (1200, 1600) + case .square: return (1200, 1200) + } + }() + + let attachments = ChatChannelTestHelpers.imageAttachments( + count: count, + originalWidth: dimensions.width, + originalHeight: dimensions.height + ) + + let message = ChatMessage.mock( + id: .unique, + cid: .unique, + text: "", + author: .mock(id: .unique), + attachments: attachments + ) + + let view = testMessageViewContainer(message: message, height: 300) + assertSnapshot( + matching: view, + as: .image(perceptualPrecision: precision), + file: file, + testName: testName, + line: line + ) } } diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/MessageView_Tests.swift b/StreamChatSwiftUITests/Tests/ChatChannel/MessageView_Tests.swift index fd2877cac..43550523d 100644 --- a/StreamChatSwiftUITests/Tests/ChatChannel/MessageView_Tests.swift +++ b/StreamChatSwiftUITests/Tests/ChatChannel/MessageView_Tests.swift @@ -655,11 +655,12 @@ import XCTest id: .unique, cid: .unique, text: "https://getstream.io", - author: .mock(id: .unique) + author: .mock(id: .unique), + attachments: ChatChannelTestHelpers.linkAttachments ) - + // When - let view = LinkAttachmentContainer( + let view = MessageAttachmentsView( factory: DefaultViewFactory.shared, message: message, width: defaultScreenSize.width, @@ -667,7 +668,7 @@ import XCTest scrolledId: .constant(nil) ) .applyDefaultSize() - + // Then assertSnapshot(matching: view, as: .image(perceptualPrecision: precision)) } diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.default-light.png index 160df39d1..a276e506c 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.default-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.extraExtraExtraLarge-light.png index d52da0ef8..3d6b280de 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.extraExtraExtraLarge-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.rightToLeftLayout-default.png index 11dd170a8..755242d61 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.rightToLeftLayout-default.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.small-dark.png index 3d553841b..12fadea36 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.small-dark.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_incoming.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.default-light.png index b01c2e011..e6d9a389d 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.default-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.extraExtraExtraLarge-light.png index c7a0b1d03..e9d9441d7 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.extraExtraExtraLarge-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.rightToLeftLayout-default.png index 81d09b016..cc9de0309 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.rightToLeftLayout-default.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.small-dark.png index 3176f0739..368285c95 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.small-dark.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_outgoing.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.default-light.png index b32a8e463..536e67495 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.default-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.extraExtraExtraLarge-light.png index 520894609..2ee1cded4 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.extraExtraExtraLarge-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.rightToLeftLayout-default.png index 2fde460a2..8fd17c756 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.rightToLeftLayout-default.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.small-dark.png index 934f29c37..301bf287d 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.small-dark.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/ChatQuotedMessageView_Tests/test_chatQuotedMessageView_withAttachment.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadButton.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadButton.1.png index 32c3ece36..1e6e9bbf3 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadButton.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadButton.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadDisabled.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadDisabled.1.png index 2cbe0b63c..40daf88d6 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadDisabled.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadDisabled.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadFailedState.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadFailedState.1.png index 1643a004e..4b0345e3a 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadFailedState.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadFailedState.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadedState.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadedState.1.png index cf541bfb6..07c6901e3 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadedState.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadedState.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadingState.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadingState.1.png index db782641d..e80a0bcee 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadingState.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/FileAttachmentView_Tests/test_fileAttachmentView_downloadingState.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_failedWhenMessageTextIsEmpty_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_failedWhenMessageTextIsEmpty_snapshot.1.png index 4788e5aad..ebb275d52 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_failedWhenMessageTextIsEmpty_snapshot.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_failedWhenMessageTextIsEmpty_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_failed_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_failed_snapshot.1.png index c7bc04f6b..86c39d2cb 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_failed_snapshot.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_failed_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_snapshot.1.png deleted file mode 100644 index 3a8571c17..000000000 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_snapshot.1.png and /dev/null differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_snapshotFiveImages.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_snapshotFiveImages.1.png deleted file mode 100644 index f42350f8d..000000000 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_imageAttachments_snapshotFiveImages.1.png and /dev/null differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_1_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_1_snapshot.1.png new file mode 100644 index 000000000..be6de9a2b Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_1_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_2_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_2_snapshot.1.png new file mode 100644 index 000000000..91b5ef44b Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_2_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_3_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_3_snapshot.1.png new file mode 100644 index 000000000..927c029d2 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_3_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_4_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_4_snapshot.1.png new file mode 100644 index 000000000..365b03d03 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_4_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_5_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_5_snapshot.1.png new file mode 100644 index 000000000..618ae7c38 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_landscape_5_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_1_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_1_snapshot.1.png new file mode 100644 index 000000000..5ad0e15b9 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_1_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_2_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_2_snapshot.1.png new file mode 100644 index 000000000..17e03ddb3 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_2_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_3_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_3_snapshot.1.png new file mode 100644 index 000000000..e8ddfec5e Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_3_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_4_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_4_snapshot.1.png new file mode 100644 index 000000000..365b03d03 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_4_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_5_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_5_snapshot.1.png new file mode 100644 index 000000000..618ae7c38 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_portrait_5_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_1_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_1_snapshot.1.png new file mode 100644 index 000000000..2d0264127 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_1_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_2_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_2_snapshot.1.png new file mode 100644 index 000000000..17e03ddb3 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_2_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_3_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_3_snapshot.1.png new file mode 100644 index 000000000..e8ddfec5e Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_3_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_4_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_4_snapshot.1.png new file mode 100644 index 000000000..365b03d03 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_4_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_5_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_5_snapshot.1.png new file mode 100644 index 000000000..618ae7c38 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_mediaGallery_square_5_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_videoAttachment_snapshotNoText.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_videoAttachment_snapshotNoText.1.png index f9d9b41ba..be6de9a2b 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_videoAttachment_snapshotNoText.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_videoAttachment_snapshotNoText.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_videoAttachment_snapshotText.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_videoAttachment_snapshotText.1.png index 82e956462..e7e86f3c7 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_videoAttachment_snapshotText.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageItemView_Tests/test_videoAttachment_snapshotText.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkAttachmentView_customColors_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkAttachmentView_customColors_snapshot.1.png index a1ec3646e..a9b203653 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkAttachmentView_customColors_snapshot.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkAttachmentView_customColors_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkAttachmentView_shouldNotRenderLinkPreviewWithOtherAttachments.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkAttachmentView_shouldNotRenderLinkPreviewWithOtherAttachments.1.png index d8410d336..a185d84e2 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkAttachmentView_shouldNotRenderLinkPreviewWithOtherAttachments.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkAttachmentView_shouldNotRenderLinkPreviewWithOtherAttachments.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkAttachmentView_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkAttachmentView_snapshot.1.png index 8c8815bf9..fbb2f1cf5 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkAttachmentView_snapshot.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkAttachmentView_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewFileText_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewFileText_snapshot.1.png index c37a4b58c..45312e4b4 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewFileText_snapshot.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewFileText_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewFile_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewFile_snapshot.1.png index 2a57dc5de..60612b851 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewFile_snapshot.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewFile_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewGiphy_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewGiphy_snapshot.1.png index 3618e81fa..ad0e38347 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewGiphy_snapshot.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewGiphy_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot.1.png index 70c41fd0e..8da76fa75 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot2Images.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot2Images.1.png index 3d5ab3c0a..5d2ca1253 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot2Images.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot2Images.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot3Images.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot3Images.1.png index ba319dc4d..e018dfa88 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot3Images.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot3Images.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot3ImagesAndVideo.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot3ImagesAndVideo.1.png index 4324cd5ce..338e8954f 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot3ImagesAndVideo.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshot3ImagesAndVideo.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshotQuoted.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshotQuoted.1.png index b118d9ed9..ab7b9890a 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshotQuoted.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewImage_snapshotQuoted.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVideo_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVideo_snapshot.1.png index 12a5a7c75..9848e8a8a 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVideo_snapshot.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVideo_snapshot.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMeTheming_snapshot.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMeTheming_snapshot.default-light.png index 1b9db0f43..96e2ba4c1 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMeTheming_snapshot.default-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMeTheming_snapshot.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.default-light.png index 306838535..73b548ff7 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.default-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.extraExtraExtraLarge-light.png index 5bd9909cb..fbdba4458 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.extraExtraExtraLarge-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.rightToLeftLayout-default.png index f2c7a5330..e12d986c9 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.rightToLeftLayout-default.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.small-dark.png index 8705a8485..8f966ca50 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.small-dark.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromMe_snapshot.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.default-light.png index e8a7ef582..bc02c7d6e 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.default-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.extraExtraExtraLarge-light.png index 62dcb81df..fc714210a 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.extraExtraExtraLarge-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.rightToLeftLayout-default.png index 4cf279fcd..25ff39697 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.rightToLeftLayout-default.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.small-dark.png index e7c36a5d4..5633a1675 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.small-dark.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingFromParticipant_snapshot.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.default-light.png index 86149bab7..21997a1c9 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.default-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.extraExtraExtraLarge-light.png index 056525237..6a932de01 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.extraExtraExtraLarge-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.rightToLeftLayout-default.png index f454a9271..9847dc9f2 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.rightToLeftLayout-default.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.small-dark.png index 5c81e5f93..5dbf0fcb0 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.small-dark.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.default-light.png index 0ac77ccbe..b5d49d7ec 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.default-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.extraExtraExtraLarge-light.png index f67bd3936..c0b53b4d4 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.extraExtraExtraLarge-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.rightToLeftLayout-default.png index e87bab7d9..a2048c950 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.rightToLeftLayout-default.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.small-dark.png index e7fdb0596..263327755 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.small-dark.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromMe_snapshot.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.default-light.png index f40b15fff..c47ab13cc 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.default-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.extraExtraExtraLarge-light.png index 750746391..818f5bec8 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.extraExtraExtraLarge-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.rightToLeftLayout-default.png index 639ee5827..6188912be 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.rightToLeftLayout-default.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.small-dark.png index 854d90fb8..dd3998b35 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.small-dark.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.default-light.png index 673816efa..09e97a874 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.default-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.extraExtraExtraLarge-light.png index 8d93dc639..76f52b261 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.extraExtraExtraLarge-light.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.rightToLeftLayout-default.png index d4cfaba11..dc594bb89 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.rightToLeftLayout-default.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.small-dark.png index 9033e2bc5..9f0aa3156 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.small-dark.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_messageViewVoiceRecordingWithTextFromParticipant_snapshot.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/CommonViews/VideoPlayIndicatorView_Tests.swift b/StreamChatSwiftUITests/Tests/CommonViews/VideoPlayIndicatorView_Tests.swift new file mode 100644 index 000000000..6db335738 --- /dev/null +++ b/StreamChatSwiftUITests/Tests/CommonViews/VideoPlayIndicatorView_Tests.swift @@ -0,0 +1,40 @@ +// +// Copyright © 2026 Stream.io Inc. All rights reserved. +// + +import SnapshotTesting +@testable import StreamChat +@testable import StreamChatSwiftUI +@testable import StreamChatTestTools +import StreamSwiftTestHelpers +import SwiftUI +import XCTest + +final class VideoPlayIndicatorView_Tests: StreamChatTestCase { + func test_videoPlayIndicator_default() { + let size = CGSize(width: 280, height: 100) + let view = stateRow(playing: false) + .frame(width: size.width, height: size.height) + + AssertSnapshot(view, size: size) + } + + func test_videoPlayIndicator_playing() { + let size = CGSize(width: 280, height: 100) + let view = stateRow(playing: true) + .frame(width: size.width, height: size.height) + + AssertSnapshot(view, size: size) + } + + // MARK: - Helpers + + private func stateRow(playing: Bool) -> some View { + HStack(spacing: 12) { + VideoPlayIndicatorView(size: VideoPlayIndicatorSize.extraLarge, playing: playing) + VideoPlayIndicatorView(size: VideoPlayIndicatorSize.large, playing: playing) + VideoPlayIndicatorView(size: VideoPlayIndicatorSize.medium, playing: playing) + VideoPlayIndicatorView(size: VideoPlayIndicatorSize.small, playing: playing) + } + } +} diff --git a/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_default.default-light.png b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_default.default-light.png new file mode 100644 index 000000000..88c21719c Binary files /dev/null and b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_default.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_default.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_default.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..88c21719c Binary files /dev/null and b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_default.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_default.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_default.rightToLeftLayout-default.png new file mode 100644 index 000000000..ee36f6d2e Binary files /dev/null and b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_default.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_default.small-dark.png b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_default.small-dark.png new file mode 100644 index 000000000..0f32ef372 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_default.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_playing.default-light.png b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_playing.default-light.png new file mode 100644 index 000000000..f06c2e5ac Binary files /dev/null and b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_playing.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_playing.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_playing.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..f06c2e5ac Binary files /dev/null and b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_playing.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_playing.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_playing.rightToLeftLayout-default.png new file mode 100644 index 000000000..f1a3528e7 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_playing.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_playing.small-dark.png b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_playing.small-dark.png new file mode 100644 index 000000000..9d44edc71 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/CommonViews/__Snapshots__/VideoPlayIndicatorView_Tests/test_videoPlayIndicator_playing.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/Utils/ViewFactory_Tests.swift b/StreamChatSwiftUITests/Tests/Utils/ViewFactory_Tests.swift index 4f5e165aa..f26519825 100644 --- a/StreamChatSwiftUITests/Tests/Utils/ViewFactory_Tests.swift +++ b/StreamChatSwiftUITests/Tests/Utils/ViewFactory_Tests.swift @@ -35,7 +35,7 @@ import XCTest let view = viewFactory.makeLoadingView(options: LoadingViewOptions()) // Then - XCTAssert(view is RedactedLoadingView) + XCTAssertNotNil(view) } func test_viewFactory_makeChannelListHeaderViewModifier() { @@ -129,6 +129,24 @@ import XCTest XCTAssert(view is MessageTextView) } + func test_viewFactory_makeMessageAttachmentsView() { + // Given + let viewFactory = DefaultViewFactory.shared + + // When + let view = viewFactory.makeMessageAttachmentsView( + options: MessageAttachmentsViewOptions( + message: message, + isFirst: true, + availableWidth: 300, + scrolledId: .constant(nil) + ) + ) + + // Then + XCTAssert(view is MessageAttachmentsView) + } + func test_viewFactory_makeImageAttachmentView() { // Given let viewFactory = DefaultViewFactory.shared @@ -144,16 +162,16 @@ import XCTest ) // Then - XCTAssert(view is ImageAttachmentContainer) + XCTAssert(view is MessageMediaAttachmentsContainerView) } - func test_viewFactory_makeGiphyAttachmentView() { + func test_viewFactory_makeVideoAttachmentView() { // Given let viewFactory = DefaultViewFactory.shared // When - let view = viewFactory.makeGiphyAttachmentView( - options: GiphyAttachmentViewOptions( + let view = viewFactory.makeVideoAttachmentView( + options: VideoAttachmentViewOptions( message: message, isFirst: true, availableWidth: 300, @@ -162,16 +180,16 @@ import XCTest ) // Then - XCTAssert(view is GiphyAttachmentView) + XCTAssert(view is MessageMediaAttachmentsContainerView) } - func test_viewFactory_makeLinkAttachmentView() { + func test_viewFactory_makeGiphyAttachmentView() { // Given let viewFactory = DefaultViewFactory.shared // When - let view = viewFactory.makeLinkAttachmentView( - options: LinkAttachmentViewOptions( + let view = viewFactory.makeGiphyAttachmentView( + options: GiphyAttachmentViewOptions( message: message, isFirst: true, availableWidth: 300, @@ -180,16 +198,16 @@ import XCTest ) // Then - XCTAssert(view is LinkAttachmentContainer) + XCTAssert(view is GiphyAttachmentView) } - func test_viewFactory_makeFileAttachmentView() { + func test_viewFactory_makeLinkAttachmentView() { // Given let viewFactory = DefaultViewFactory.shared // When - let view = viewFactory.makeFileAttachmentView( - options: FileAttachmentViewOptions( + let view = viewFactory.makeLinkAttachmentView( + options: LinkAttachmentViewOptions( message: message, isFirst: true, availableWidth: 300, @@ -198,16 +216,16 @@ import XCTest ) // Then - XCTAssert(view is FileAttachmentsContainer) + XCTAssert(view is LinkAttachmentContainer) } - func test_viewFactory_makeVideoAttachmentView() { + func test_viewFactory_makeFileAttachmentView() { // Given let viewFactory = DefaultViewFactory.shared // When - let view = viewFactory.makeVideoAttachmentView( - options: VideoAttachmentViewOptions( + let view = viewFactory.makeFileAttachmentView( + options: FileAttachmentViewOptions( message: message, isFirst: true, availableWidth: 300, @@ -216,7 +234,7 @@ import XCTest ) // Then - XCTAssert(view is VideoAttachmentsContainer) + XCTAssert(view is FileAttachmentsContainer) } func test_viewFactory_makeDeletedMessageView() { @@ -1035,17 +1053,28 @@ import XCTest XCTAssert(view is AddUsersView) } - func test_viewFactory_makeAttachmentTextView() { + func test_viewFactory_makeStreamTextView() { // Given let viewFactory = DefaultViewFactory.shared // When - let view = viewFactory.makeAttachmentTextView(options: .init(mesage: message)) + let view = viewFactory.makeStreamTextView(options: .init(message: message)) // Then XCTAssert(view is StreamTextView) } + func test_viewFactory_makeAttachmentTextView() { + // Given + let viewFactory = DefaultViewFactory.shared + + // When + let view = viewFactory.makeAttachmentTextView(options: .init(message: message)) + + // Then + XCTAssert(view is AttachmentTextView) + } + func test_viewFactory_makeReactionsDetailView() { // Given let viewFactory = DefaultViewFactory.shared diff --git a/StreamChatSwiftUITestsAppTests/Pages/MessageListPage.swift b/StreamChatSwiftUITestsAppTests/Pages/MessageListPage.swift index 842833b40..d52cc2fdd 100644 --- a/StreamChatSwiftUITestsAppTests/Pages/MessageListPage.swift +++ b/StreamChatSwiftUITestsAppTests/Pages/MessageListPage.swift @@ -212,15 +212,15 @@ class MessageListPage { } static func image(in messageCell: XCUIElement) -> XCUIElement { - messageCell.images["ImageAttachmentContainer"] + messageCell.images["MessageMediaAttachmentsContainerView"] } static func imagePreloader(in messageCell: XCUIElement) -> XCUIElement { - messageCell.activityIndicators["ImageAttachmentContainer"] + messageCell.activityIndicators["MessageMediaAttachmentsContainerView"] } static func video(in messageCell: XCUIElement) -> XCUIElement { - messageCell.images["VideoAttachmentsContainer"] + messageCell.images["MessageMediaAttachmentsContainerView"] } static func fullscreenImage() -> XCUIElement {