diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 20e0eb07d..ec6884a1a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,25 +1,36 @@ -### ๐Ÿ”— Issue Link -_Jira or Github issue link, if applicable._ +### ๐Ÿ”— Issue Links + +_Provide all Linear and/or Github issues related to this PR, if applicable._ ### ๐ŸŽฏ Goal _Describe why we are making this change._ +### ๐Ÿ“ Summary + +_Provide bullet points with the most important changes in the codebase._ + ### ๐Ÿ›  Implementation -_Provide a description of the implementation._ +_Provide a detailed description of the implementation and explain your decisions if you find them relevant._ + +### ๐ŸŽจ Showcase -### ๐Ÿงช Testing +_Add relevant screenshots and/or videos/gifs to easily see what this PR changes, if applicable._ -_Describe the steps how this change can be tested (or why it can't be tested)._ +| Before | After | +| ------ | ----- | +| img | img | -### ๐ŸŽจ Changes +### ๐Ÿงช Manual Testing Notes -_Add relevant screenshots or videos showcasing the changes._ +_Explain how this change can be tested manually, if applicable._ -### โ˜‘๏ธ Checklist +### โ˜‘๏ธ Contributor Checklist - [ ] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) +- [x] This change should be manually QAed - [ ] Changelog is updated with client-facing changes +- [ ] Changelog is updated with new localization keys - [ ] New code is covered by unit tests -- [ ] Affected documentation updated (docusaurus, tutorial, CMS (task created) +- [ ] Documentation has been updated in the `docs-content` repo diff --git a/.github/workflows/cron-checks.yml b/.github/workflows/cron-checks.yml index 7372dce31..5f9ec8b85 100644 --- a/.github/workflows/cron-checks.yml +++ b/.github/workflows/cron-checks.yml @@ -18,7 +18,7 @@ env: jobs: build-test-app-and-frameworks: name: Build Test App and Frameworks - runs-on: macos-14 + runs-on: macos-15 steps: - uses: actions/checkout@v4.1.1 - uses: ./.github/actions/ruby-cache @@ -42,14 +42,14 @@ jobs: matrix: include: - ios: 18.1 - xcode: 16.1 + xcode: 16.1 # fails on 16.2 os: macos-15 device: "iPhone 16 Pro" setup_runtime: false - ios: 17.5 xcode: 15.4 os: macos-15 - device: "iPhone 14 Pro" + device: "iPhone 15 Pro" setup_runtime: false - ios: 16.4 xcode: 15.3 # fails on 15.4 @@ -131,7 +131,6 @@ jobs: build-old-xcode: name: Build SDKs (Xcode 15) runs-on: macos-15 - if: ${{ github.event_name != 'push' && github.event.inputs.snapshots != 'true' }} env: XCODE_VERSION: "15.4" steps: diff --git a/.github/workflows/release-merge.yml b/.github/workflows/release-merge.yml index c8c29253c..250cab82c 100644 --- a/.github/workflows/release-merge.yml +++ b/.github/workflows/release-merge.yml @@ -9,7 +9,7 @@ on: jobs: merge-comment: name: Merge release to main - runs-on: macos-14 + runs-on: macos-15 if: github.event_name == 'workflow_dispatch' || (github.event.issue.pull_request && github.event.issue.state == 'open' && github.event.comment.body == '/merge release') steps: - name: Connect Bot diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 8def5f082..893000383 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -6,7 +6,7 @@ on: jobs: release: name: Publish new release - runs-on: macos-13 + runs-on: macos-15 steps: - name: Connect Bot uses: webfactory/ssh-agent@v0.7.0 diff --git a/.github/workflows/release-start.yml b/.github/workflows/release-start.yml index e195127c9..b96e7f0d0 100644 --- a/.github/workflows/release-start.yml +++ b/.github/workflows/release-start.yml @@ -32,4 +32,3 @@ jobs: GITHUB_TOKEN: ${{ secrets.CI_BOT_GITHUB_TOKEN }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} APPSTORE_API_KEY: ${{ secrets.APPSTORE_API_KEY }} - XCODE_VERSION: "15.4" diff --git a/.github/workflows/sdk-size-metrics.yml b/.github/workflows/sdk-size-metrics.yml index b2245193b..a05407b1c 100644 --- a/.github/workflows/sdk-size-metrics.yml +++ b/.github/workflows/sdk-size-metrics.yml @@ -15,7 +15,7 @@ env: jobs: sdk_size: name: Metrics - runs-on: macos-14 + runs-on: macos-15 env: GITHUB_TOKEN: '${{ secrets.CI_BOT_GITHUB_TOKEN }}' steps: diff --git a/.github/workflows/smoke-checks.yml b/.github/workflows/smoke-checks.yml index 756bcf8f9..cf9537bcb 100644 --- a/.github/workflows/smoke-checks.yml +++ b/.github/workflows/smoke-checks.yml @@ -20,7 +20,7 @@ concurrency: env: HOMEBREW_NO_INSTALL_CLEANUP: 1 # Disable cleanup for homebrew, we don't need it on CI - IOS_SIMULATOR_DEVICE: "iPhone 16 Pro (18.1)" + IOS_SIMULATOR_DEVICE: "iPhone 16 Pro (18.2)" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_PR_NUM: ${{ github.event.pull_request.number }} @@ -64,7 +64,7 @@ jobs: run: bundle exec fastlane pod_lint build-xcode15: - name: Build SDKs (Xcode 15.0) + name: Build SDKs (Xcode 15) runs-on: macos-15 if: ${{ github.event.inputs.record_snapshots != 'true' }} env: @@ -104,7 +104,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.CI_BOT_GITHUB_TOKEN }} # to open a PR - name: Run Sonar analysis - if: ${{ github.event.inputs.snapshots != 'true' }} + if: ${{ github.event.inputs.record_snapshots != 'true' }} run: bundle exec fastlane sonar_upload env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} @@ -133,7 +133,7 @@ jobs: allure_testops_launch: name: Launch Allure TestOps - runs-on: macos-13 + runs-on: macos-15 if: ${{ github.event.inputs.record_snapshots != 'true' }} needs: build-test-app-and-frameworks outputs: @@ -159,6 +159,8 @@ jobs: - build-test-app-and-frameworks env: LAUNCH_ID: ${{ needs.allure_testops_launch.outputs.launch_id }} + XCODE_VERSION: "16.1" # fails on 16.2 + IOS_SIMULATOR_DEVICE: "iPhone 16 Pro (18.1)" strategy: matrix: batch: [0, 1] @@ -207,7 +209,7 @@ jobs: build-apps: name: Build Demo App - runs-on: macos-14 + runs-on: macos-15 if: ${{ github.event.inputs.record_snapshots != 'true' }} needs: build-test-app-and-frameworks steps: diff --git a/.github/workflows/testflight.yml b/.github/workflows/testflight.yml index 0f6e37b8f..e17d113af 100644 --- a/.github/workflows/testflight.yml +++ b/.github/workflows/testflight.yml @@ -15,7 +15,7 @@ env: jobs: deploy: - runs-on: macos-14 + runs-on: macos-15 steps: - name: Connect Bot uses: webfactory/ssh-agent@v0.7.0 diff --git a/.github/workflows/update-copyright.yml b/.github/workflows/update-copyright.yml index fa5f354a3..bfa07999f 100644 --- a/.github/workflows/update-copyright.yml +++ b/.github/workflows/update-copyright.yml @@ -13,7 +13,7 @@ env: jobs: copyright: name: Copyright - runs-on: macos-13 + runs-on: macos-15 steps: - uses: actions/checkout@v4.1.1 - uses: ./.github/actions/ruby-cache diff --git a/CHANGELOG.md b/CHANGELOG.md index 738922fd3..f4d6474a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### ๐Ÿ”„ Changed +# [4.74.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.74.0) +_March 14, 2025_ + +### โœ… Added +- Feature rich markdown rendering with AttributedString [#757](https://github.com/GetStream/stream-chat-swiftui/pull/757) +- Add `Fonts.title2` for supporting markdown headers [#757](https://github.com/GetStream/stream-chat-swiftui/pull/757) +- Add `resignsFirstResponderOnScrollDown` to `MessageListConfig` [#769](https://github.com/GetStream/stream-chat-swiftui/pull/769) +- Show auto-translated message translations ([learn more](https://getstream.io/chat/docs/ios-swift/translation/#enabling-automatic-translation)) [#776](https://github.com/GetStream/stream-chat-swiftui/pull/776) +### ๐Ÿž Fixed +- Show typing suggestions for names containing whitespace [#781](https://github.com/GetStream/stream-chat-swiftui/pull/781) +### ๐Ÿ”„ Changed +- Uploading a HEIC photo from the library is now converted to JPEG for better compatibility [#767](https://github.com/GetStream/stream-chat-swiftui/pull/767) +- Customizing the message avatar view is reflected in all views that use it [#772](https://github.com/GetStream/stream-chat-swiftui/pull/772) +- Made the sendMessage method in MessageComposerViewModel open [#779](https://github.com/GetStream/stream-chat-swiftui/pull/779) +- Move `ChangeBarsVisibilityModifier` into `ViewFactory` for better customization [#774](https://github.com/GetStream/stream-chat-swiftui/pull/774) +### ๐ŸŽญ New Localizations +- `message.translatedTo` [#776](https://github.com/GetStream/stream-chat-swiftui/pull/776) + # [4.73.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.73.0) _February 28, 2025_ diff --git a/DemoAppSwiftUI/AppConfiguration/AppConfiguration.swift b/DemoAppSwiftUI/AppConfiguration/AppConfiguration.swift new file mode 100644 index 000000000..19c6fc028 --- /dev/null +++ b/DemoAppSwiftUI/AppConfiguration/AppConfiguration.swift @@ -0,0 +1,14 @@ +// +// Copyright ยฉ 2025 Stream.io Inc. All rights reserved. +// + +import Foundation +import StreamChat +import StreamChatSwiftUI + +final class AppConfiguration { + static let `default` = AppConfiguration() + + /// The translation language to set on connect. + var translationLanguage: TranslationLanguage? +} diff --git a/DemoAppSwiftUI/AppConfiguration/AppConfigurationTranslationView.swift b/DemoAppSwiftUI/AppConfiguration/AppConfigurationTranslationView.swift new file mode 100644 index 000000000..779b83562 --- /dev/null +++ b/DemoAppSwiftUI/AppConfiguration/AppConfigurationTranslationView.swift @@ -0,0 +1,47 @@ +// +// Copyright ยฉ 2025 Stream.io Inc. All rights reserved. +// + +import StreamChat +import SwiftUI + +struct AppConfigurationTranslationView: View { + @Environment(\.dismiss) var dismiss + + var selection: Binding = Binding { + AppConfiguration.default.translationLanguage + } set: { newValue in + AppConfiguration.default.translationLanguage = newValue + } + + var body: some View { + List { + ForEach(TranslationLanguage.all, id: \.languageCode) { language in + Button(action: { + selection.wrappedValue = language + dismiss() + }) { + HStack { + Text(language.languageCode) + Spacer() + if selection.wrappedValue == language { + Image(systemName: "checkmark") + } + } + } + .foregroundStyle(.primary) + } + .navigationTitle("Translation Language") + } + } +} + +extension TranslationLanguage { + static let all = allCases.sorted(by: { $0.languageCode < $1.languageCode }) +} + +#Preview { + NavigationView { + AppConfigurationTranslationView() + } +} diff --git a/DemoAppSwiftUI/AppConfiguration/AppConfigurationView.swift b/DemoAppSwiftUI/AppConfiguration/AppConfigurationView.swift new file mode 100644 index 000000000..13acd91e5 --- /dev/null +++ b/DemoAppSwiftUI/AppConfiguration/AppConfigurationView.swift @@ -0,0 +1,26 @@ +// +// Copyright ยฉ 2025 Stream.io Inc. All rights reserved. +// + +import Combine +import SwiftUI + +struct AppConfigurationView: View { + var body: some View { + NavigationView { + List { + Section("Connect User Configuration") { + NavigationLink("Translation") { + AppConfigurationTranslationView() + } + } + } + .navigationBarTitleDisplayMode(.inline) + .navigationTitle("App Configuration") + } + } +} + +#Preview { + AppConfigurationView() +} diff --git a/DemoAppSwiftUI/AppDelegate.swift b/DemoAppSwiftUI/AppDelegate.swift index 93975c3bc..9a2cd7ebc 100644 --- a/DemoAppSwiftUI/AppDelegate.swift +++ b/DemoAppSwiftUI/AppDelegate.swift @@ -81,7 +81,8 @@ class AppDelegate: NSObject, UIApplicationDelegate { userInfo: .init( id: credentials.id, name: credentials.name, - imageURL: credentials.avatarURL + imageURL: credentials.avatarURL, + language: AppConfiguration.default.translationLanguage ), token: token ) diff --git a/DemoAppSwiftUI/LoginView.swift b/DemoAppSwiftUI/LoginView.swift index baeca29ec..246d702a1 100644 --- a/DemoAppSwiftUI/LoginView.swift +++ b/DemoAppSwiftUI/LoginView.swift @@ -20,6 +20,11 @@ struct LoginView: View { Text("Welcome to Stream Chat") .font(.title) .padding(.all, 8) + + Button("Configuration") { + viewModel.showsConfiguration = true + } + .buttonStyle(.borderedProminent) Text("Select a user to try the iOS SDK:") .font(.body) @@ -42,6 +47,9 @@ struct LoginView: View { .overlay( viewModel.loading ? ProgressView() : nil ) + .sheet(isPresented: $viewModel.showsConfiguration) { + AppConfigurationView() + } } } diff --git a/DemoAppSwiftUI/LoginViewModel.swift b/DemoAppSwiftUI/LoginViewModel.swift index e2bab8a7f..efe5ab798 100644 --- a/DemoAppSwiftUI/LoginViewModel.swift +++ b/DemoAppSwiftUI/LoginViewModel.swift @@ -10,6 +10,7 @@ class LoginViewModel: ObservableObject { @Published var demoUsers = UserCredentials.builtInUsers @Published var loading = false + @Published var showsConfiguration = false @Injected(\.chatClient) var chatClient @@ -28,7 +29,12 @@ class LoginViewModel: ObservableObject { LogConfig.level = .warning chatClient.connectUser( - userInfo: .init(id: credentials.id, name: credentials.name, imageURL: credentials.avatarURL), + userInfo: .init( + id: credentials.id, + name: credentials.name, + imageURL: credentials.avatarURL, + language: AppConfiguration.default.translationLanguage + ), token: token ) { [weak self] error in if let error = error { diff --git a/Gemfile.lock b/Gemfile.lock index 4bbb477bf..380bd08c2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -310,7 +310,7 @@ GEM puma (6.4.3) nio4r (~> 2.0) racc (1.8.1) - rack (3.1.10) + rack (3.1.12) rack-protection (4.1.0) base64 (>= 0.1.0) logger (>= 1.6.0) diff --git a/Package.swift b/Package.swift index 0af248202..396dc346d 100644 --- a/Package.swift +++ b/Package.swift @@ -16,7 +16,7 @@ let package = Package( ) ], dependencies: [ - .package(url: "https://github.com/GetStream/stream-chat-swift.git", from: "4.73.0"), + .package(url: "https://github.com/GetStream/stream-chat-swift.git", from: "4.74.0"), ], targets: [ .target( diff --git a/README.md b/README.md index 1b98251f5..0091fadba 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

- StreamChatSwiftUI + StreamChatSwiftUI

## SwiftUI StreamChat SDK diff --git a/Sources/StreamChatSwiftUI/ChatChannel/ChannelHeader/ChatChannelHeaderViewModifier.swift b/Sources/StreamChatSwiftUI/ChatChannel/ChannelHeader/ChatChannelHeaderViewModifier.swift index 4df3cdc29..8b51cb82c 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/ChannelHeader/ChatChannelHeaderViewModifier.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/ChannelHeader/ChatChannelHeaderViewModifier.swift @@ -80,7 +80,7 @@ public struct DefaultChatChannelHeader: ToolbarContent { .accessibilityLabel(Text(L10n.Channel.Header.Info.title)) NavigationLink(isActive: $isActive) { - LazyView(ChatChannelInfoView(channel: channel, shownFromMessageList: true)) + LazyView(ChatChannelInfoView(factory: factory, channel: channel, shownFromMessageList: true)) } label: { EmptyView() } diff --git a/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatChannelInfoHelperViews.swift b/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatChannelInfoHelperViews.swift index 5b89a0eed..16c0e7978 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatChannelInfoHelperViews.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatChannelInfoHelperViews.swift @@ -50,16 +50,21 @@ struct ChannelInfoDivider: View { } } -public struct ChatInfoOptionsView: View { +public struct ChatInfoOptionsView: View { @Injected(\.images) private var images @Injected(\.colors) private var colors @Injected(\.fonts) private var fonts @StateObject var viewModel: ChatChannelInfoViewModel + let factory: Factory - public init(viewModel: ChatChannelInfoViewModel) { + public init( + factory: Factory = DefaultViewFactory.shared, + viewModel: ChatChannelInfoViewModel + ) { _viewModel = StateObject(wrappedValue: viewModel) + self.factory = factory } public var body: some View { @@ -89,6 +94,7 @@ public struct ChatInfoOptionsView: View { title: L10n.ChatInfo.PinnedMessages.title ) { PinnedMessagesView( + factory: factory, channel: viewModel.channel, channelController: viewModel.channelController ) @@ -100,7 +106,7 @@ public struct ChatInfoOptionsView: View { icon: UIImage(systemName: "photo")!, title: L10n.ChatInfo.Media.title ) { - MediaAttachmentsView(channel: viewModel.channel) + MediaAttachmentsView(factory: factory, channel: viewModel.channel) } Divider() @@ -239,20 +245,29 @@ public struct ChannelInfoItemView: View { } } -struct ChatInfoDirectChannelView: View { +struct ChatInfoDirectChannelView: View { @Injected(\.fonts) private var fonts @Injected(\.colors) private var colors + let factory: Factory var participant: ParticipantInfo? + + init(factory: Factory = DefaultViewFactory.shared, participant: ParticipantInfo?) { + self.factory = factory + self.participant = participant + } var body: some View { VStack { - MessageAvatarView( - avatarURL: participant?.chatUser.imageURL, + let displayInfo = UserDisplayInfo( + id: participant?.chatUser.id ?? "", + name: participant?.chatUser.name ?? "", + imageURL: participant?.chatUser.imageURL, size: .init(width: 64, height: 64) ) - + factory.makeMessageAvatarView(for: displayInfo) + Text(participant?.onlineInfoText ?? "") .font(fonts.footnote) .foregroundColor(Color(colors.textLowEmphasis)) diff --git a/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatChannelInfoView.swift b/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatChannelInfoView.swift index 12ffc570e..a80699e5d 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatChannelInfoView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatChannelInfoView.swift @@ -6,29 +6,37 @@ import StreamChat import SwiftUI // View for the channel info screen. -public struct ChatChannelInfoView: View, KeyboardReadable { +public struct ChatChannelInfoView: View, KeyboardReadable { @Injected(\.images) private var images @Injected(\.colors) private var colors @Injected(\.fonts) private var fonts + let factory: Factory + @StateObject private var viewModel: ChatChannelInfoViewModel private var shownFromMessageList: Bool @Environment(\.presentationMode) var presentationMode - + public init( + factory: Factory = DefaultViewFactory.shared, channel: ChatChannel, shownFromMessageList: Bool = false ) { _viewModel = StateObject( wrappedValue: ChatChannelInfoViewModel(channel: channel) ) + self.factory = factory self.shownFromMessageList = shownFromMessageList } - init(viewModel: ChatChannelInfoViewModel) { + init( + factory: Factory = DefaultViewFactory.shared, + viewModel: ChatChannelInfoViewModel + ) { _viewModel = StateObject(wrappedValue: viewModel) + self.factory = factory shownFromMessageList = false } @@ -38,10 +46,12 @@ public struct ChatChannelInfoView: View, KeyboardReadable { LazyVStack(spacing: 0) { if viewModel.showSingleMemberDMView { ChatInfoDirectChannelView( + factory: factory, participant: viewModel.displayedParticipants.first ) } else { ChatInfoParticipantsView( + factory: factory, participants: viewModel.displayedParticipants, onItemAppear: viewModel.onParticipantAppear(_:) ) @@ -59,7 +69,7 @@ public struct ChatChannelInfoView: View, KeyboardReadable { ChannelInfoDivider() - ChatInfoOptionsView(viewModel: viewModel) + ChatInfoOptionsView(factory: factory, viewModel: viewModel) ChannelInfoDivider() .alert(isPresented: $viewModel.errorShown) { diff --git a/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatInfoParticipantsView.swift b/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatInfoParticipantsView.swift index c5bf6ed67..9b8e9e150 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatInfoParticipantsView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatInfoParticipantsView.swift @@ -6,19 +6,35 @@ import StreamChat import SwiftUI /// View for the chat info participants. -struct ChatInfoParticipantsView: View { +struct ChatInfoParticipantsView: View { @Injected(\.fonts) private var fonts @Injected(\.colors) private var colors + let factory: Factory var participants: [ParticipantInfo] var onItemAppear: (ParticipantInfo) -> Void + + init( + factory: Factory = DefaultViewFactory.shared, + participants: [ParticipantInfo], + onItemAppear: @escaping (ParticipantInfo) -> Void + ) { + self.factory = factory + self.participants = participants + self.onItemAppear = onItemAppear + } var body: some View { LazyVStack { ForEach(participants) { participant in HStack { - MessageAvatarView(avatarURL: participant.chatUser.imageURL) + let displayInfo = UserDisplayInfo( + id: participant.chatUser.id, + name: participant.chatUser.name ?? "", + imageURL: participant.chatUser.imageURL + ) + factory.makeMessageAvatarView(for: displayInfo) VStack(alignment: .leading, spacing: 4) { Text(participant.displayName) diff --git a/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/MediaAttachmentsView.swift b/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/MediaAttachmentsView.swift index e6771c0b3..177076522 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/MediaAttachmentsView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/MediaAttachmentsView.swift @@ -6,15 +6,16 @@ import StreamChat import SwiftUI /// View displaying media attachments. -public struct MediaAttachmentsView: View { +public struct MediaAttachmentsView: View { @Injected(\.images) private var images @StateObject private var viewModel: MediaAttachmentsViewModel - - private static let spacing: CGFloat = 2 + + let factory: Factory private static var itemWidth: CGFloat { + let spacing: CGFloat = 2 if UIDevice.current.userInterfaceIdiom == .phone { return (UIScreen.main.bounds.size.width / 3) - spacing * 3 } else { @@ -22,18 +23,20 @@ public struct MediaAttachmentsView: View { } } - private let columns = [GridItem(.adaptive(minimum: itemWidth), spacing: spacing)] + private let columns = [GridItem(.adaptive(minimum: itemWidth), spacing: 2)] - public init(channel: ChatChannel) { + public init(factory: Factory = DefaultViewFactory.shared, channel: ChatChannel) { _viewModel = StateObject( wrappedValue: MediaAttachmentsViewModel(channel: channel) ) + self.factory = factory } - init(viewModel: MediaAttachmentsViewModel) { + init(factory: Factory = DefaultViewFactory.shared, viewModel: MediaAttachmentsViewModel) { _viewModel = StateObject( wrappedValue: viewModel ) + self.factory = factory } public var body: some View { @@ -73,10 +76,13 @@ public struct MediaAttachmentsView: View { } .overlay( BottomRightView { - MessageAvatarView( - avatarURL: mediaItem.author.imageURL, - size: .init(width: 24, height: 24), - showOnlineIndicator: false + factory.makeMessageAvatarView( + for: UserDisplayInfo( + id: mediaItem.author.id, + name: mediaItem.author.name ?? "", + imageURL: mediaItem.author.imageURL, + size: .init(width: 24, height: 24) + ) ) .overlay( Circle() diff --git a/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/PinnedMessagesView.swift b/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/PinnedMessagesView.swift index 11c6df183..a40b08780 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/PinnedMessagesView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/PinnedMessagesView.swift @@ -6,19 +6,26 @@ import StreamChat import SwiftUI /// View displaying pinned messages in the chat info screen. -public struct PinnedMessagesView: View { +public struct PinnedMessagesView: View { @Injected(\.images) private var images @StateObject private var viewModel: PinnedMessagesViewModel + + let factory: Factory - public init(channel: ChatChannel, channelController: ChatChannelController? = nil) { + public init( + factory: Factory = DefaultViewFactory.shared, + channel: ChatChannel, + channelController: ChatChannelController? = nil + ) { _viewModel = StateObject( wrappedValue: PinnedMessagesViewModel( channel: channel, channelController: channelController ) ) + self.factory = factory } public var body: some View { @@ -27,7 +34,7 @@ public struct PinnedMessagesView: View { ScrollView { LazyVStack(spacing: 0) { ForEach(viewModel.pinnedMessages) { message in - PinnedMessageView(message: message, channel: viewModel.channel) + PinnedMessageView(factory: factory, message: message, channel: viewModel.channel) Divider() } } @@ -45,7 +52,7 @@ public struct PinnedMessagesView: View { } } -struct PinnedMessageView: View { +struct PinnedMessageView: View { @Injected(\.fonts) private var fonts @Injected(\.colors) private var colors @@ -53,14 +60,19 @@ struct PinnedMessageView: View { private let avatarSize = CGSize(width: 56, height: 56) + var factory: Factory var message: ChatMessage var channel: ChatChannel var body: some View { HStack { - MessageAvatarView( - avatarURL: message.author.imageURL, - size: avatarSize + factory.makeMessageAvatarView( + for: UserDisplayInfo( + id: message.author.id, + name: message.author.name ?? "", + imageURL: message.author.imageURL, + size: avatarSize + ) ) VStack(alignment: .leading, spacing: 4) { @@ -89,6 +101,6 @@ struct PinnedMessageView: View { return "๐Ÿ“Š \(L10n.Channel.Item.poll)" } let messageFormatter = InjectedValues[\.utils].messagePreviewFormatter - return messageFormatter.formatAttachmentContent(for: message) ?? message.adjustedText + return messageFormatter.formatAttachmentContent(for: message, in: channel) ?? message.adjustedText } } diff --git a/Sources/StreamChatSwiftUI/ChatChannel/ChatChannelView.swift b/Sources/StreamChatSwiftUI/ChatChannel/ChatChannelView.swift index 4a5de0ac0..4013d866c 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/ChatChannelView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/ChatChannelView.swift @@ -94,10 +94,10 @@ public struct ChatChannelView: View, KeyboardReadable { Divider() .navigationBarBackButtonHidden(viewModel.reactionsShown) .if(viewModel.reactionsShown, transform: { view in - view.changeBarsVisibility(shouldShow: false) + view.modifier(factory.makeChannelBarsVisibilityViewModifier(shouldShow: false)) }) .if(!viewModel.reactionsShown, transform: { view in - view.changeBarsVisibility(shouldShow: true) + view.modifier(factory.makeChannelBarsVisibilityViewModifier(shouldShow: true)) }) .if(viewModel.channelHeaderType == .regular) { view in view.modifier(factory.makeChannelHeaderViewModifier(for: channel)) diff --git a/Sources/StreamChatSwiftUI/ChatChannel/Composer/ImagePickerView.swift b/Sources/StreamChatSwiftUI/ChatChannel/Composer/ImagePickerView.swift index 0d531b8ce..f20adb592 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/Composer/ImagePickerView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/Composer/ImagePickerView.swift @@ -55,7 +55,7 @@ final class ImagePickerCoordinator: NSObject, UIImagePickerControllerDelegate, U didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any] ) { if let uiImage = info[.originalImage] as? UIImage, - let imageURL = try? uiImage.temporaryLocalFileUrl() { + let imageURL = try? uiImage.saveAsJpgToTemporaryUrl() { let addedImage = AddedAsset( image: uiImage, id: UUID().uuidString, diff --git a/Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerView.swift b/Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerView.swift index 13fe5c973..e54f4a6a7 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerView.swift @@ -300,6 +300,7 @@ public struct ComposerInputView: View, KeyboardReadable { isInComposer: true, scrolledId: .constant(nil) ) + .environment(\.channelTranslationLanguage, viewModel.channelController.channel?.membership?.language) } if !addedAssets.isEmpty { diff --git a/Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerViewModel.swift b/Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerViewModel.swift index 9f683b01e..30d1c2137 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerViewModel.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerViewModel.swift @@ -219,7 +219,7 @@ open class MessageComposerViewModel: ObservableObject { ) } - public func sendMessage( + open func sendMessage( quotedMessage: ChatMessage?, editedMessage: ChatMessage?, isSilent: Bool = false, diff --git a/Sources/StreamChatSwiftUI/ChatChannel/Composer/PhotoAttachmentPickerView.swift b/Sources/StreamChatSwiftUI/ChatChannel/Composer/PhotoAttachmentPickerView.swift index 79a9665a2..2e573dd7f 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/Composer/PhotoAttachmentPickerView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/Composer/PhotoAttachmentPickerView.swift @@ -102,7 +102,7 @@ public struct PhotoAttachmentCell: View { .allowsHitTesting(true) .onTapGesture { withAnimation { - if let assetURL = assetURL { + if let assetURL = asset.mediaType == .image ? assetJpgURL() : assetURL { onImageTap( AddedAsset( image: image, @@ -193,4 +193,13 @@ public struct PhotoAttachmentCell: View { } } } + + /// The original photo is usually in HEIC format. + /// This makes sure that the photo is converted to JPG. + /// This way it is more compatible with other platforms. + private func assetJpgURL() -> URL? { + guard let assetURL = assetURL else { return nil } + guard let assetData = try? Data(contentsOf: assetURL) else { return nil } + return try? UIImage(data: assetData)?.saveAsJpgToTemporaryUrl() + } } diff --git a/Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/TypingSuggester.swift b/Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/TypingSuggester.swift index c6b54e10d..6a713e41c 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/TypingSuggester.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/TypingSuggester.swift @@ -115,14 +115,9 @@ public struct TypingSuggester { return nil } - // Fetch the suggestion text. The suggestions can't have spaces. - // valid example: "@luke_skywa..." - // invalid example: "@luke skywa..." + // Fetch the suggestion text. let suggestionLocation = NSRange(location: suggestionStart, length: suggestionEnd - suggestionStart) let suggestionText = textString.substring(with: suggestionLocation) - guard !suggestionText.contains(" ") else { - return nil - } // A minimum number of characters can be provided to only show // suggestions after the customer has input enough characters. diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageContainerView.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageContainerView.swift index 8e93fa16a..8e517c02d 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageContainerView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageContainerView.swift @@ -7,7 +7,8 @@ import StreamChat import SwiftUI public struct MessageContainerView: View { - + @Environment(\.channelTranslationLanguage) var translationLanguage + @Injected(\.fonts) private var fonts @Injected(\.colors) private var colors @Injected(\.images) private var images @@ -225,6 +226,12 @@ public struct MessageContainerView: View { } } + if message.textContent(for: translationLanguage) != nil, + let localizedName = translationLanguage?.localizedName { + Text(L10n.Message.translatedTo(localizedName)) + .font(fonts.footnote) + .foregroundColor(Color(colors.subtitleText)) + } if showsAllInfo && !message.isDeleted { if message.isSentByCurrentUser && channel.config.readEventsEnabled { HStack(spacing: 4) { diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListConfig.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListConfig.swift index ce19ad88c..4e2ecdb64 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListConfig.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListConfig.swift @@ -19,6 +19,7 @@ public struct MessageListConfig { messagePopoverEnabled: Bool = true, doubleTapOverlayEnabled: Bool = false, becomesFirstResponderOnOpen: Bool = false, + resignsFirstResponderOnScrollDown: Bool = true, updateChannelsFromMessageList: Bool = false, maxTimeIntervalBetweenMessagesInGroup: TimeInterval = 60, cacheSizeOnChatDismiss: Int = 1024 * 1024 * 100, @@ -45,6 +46,7 @@ public struct MessageListConfig { self.messagePopoverEnabled = messagePopoverEnabled self.doubleTapOverlayEnabled = doubleTapOverlayEnabled self.becomesFirstResponderOnOpen = becomesFirstResponderOnOpen + self.resignsFirstResponderOnScrollDown = resignsFirstResponderOnScrollDown self.updateChannelsFromMessageList = updateChannelsFromMessageList self.maxTimeIntervalBetweenMessagesInGroup = maxTimeIntervalBetweenMessagesInGroup self.cacheSizeOnChatDismiss = cacheSizeOnChatDismiss @@ -72,6 +74,7 @@ public struct MessageListConfig { public let messagePopoverEnabled: Bool public let doubleTapOverlayEnabled: Bool public let becomesFirstResponderOnOpen: Bool + public let resignsFirstResponderOnScrollDown: Bool public let updateChannelsFromMessageList: Bool public let maxTimeIntervalBetweenMessagesInGroup: TimeInterval public let cacheSizeOnChatDismiss: Int diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListView.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListView.swift index fa834879b..e30209027 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListView.swift @@ -148,6 +148,7 @@ public struct MessageListView: View, KeyboardReadable { onLongPress: handleLongPress(messageDisplayInfo:), isLast: !showsLastInGroupInfo && message == messages.last ) + .environment(\.channelTranslationLanguage, channel.membership?.language) .onAppear { if index == nil { index = messageListDateUtils.index(for: message, in: messages) @@ -238,7 +239,7 @@ public struct MessageListView: View, KeyboardReadable { if scrollButtonShown != showScrollToLatestButton { showScrollToLatestButton = scrollButtonShown } - if keyboardShown && diff < -20 { + if messageListConfig.resignsFirstResponderOnScrollDown && keyboardShown && diff < -20 { keyboardShown = false resignFirstResponder() } @@ -597,3 +598,18 @@ private class MessageRenderingUtil { return skipRendering } } + +private struct ChannelTranslationLanguageKey: EnvironmentKey { + static let defaultValue: TranslationLanguage? = nil +} + +extension EnvironmentValues { + var channelTranslationLanguage: TranslationLanguage? { + get { + self[ChannelTranslationLanguageKey.self] + } + set { + self[ChannelTranslationLanguageKey.self] = newValue + } + } +} diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageView.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageView.swift index 0bd8ec75d..8883c4867 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageView.swift @@ -250,6 +250,8 @@ struct StreamTextView: View { @available(iOS 15, *) public struct LinkDetectionTextView: View { + @Environment(\.layoutDirection) var layoutDirection + @Environment(\.channelTranslationLanguage) var translationLanguage @Injected(\.colors) var colors @Injected(\.fonts) var fonts @@ -271,16 +273,10 @@ public struct LinkDetectionTextView: View { self.message = message } - private var markdownEnabled: Bool { - utils.messageListConfig.markdownSupportEnabled - } - public var body: some View { Group { if let displayedText { Text(displayedText) - } else if markdownEnabled { - Text(text) } else { Text(message.adjustedText) } @@ -289,72 +285,67 @@ public struct LinkDetectionTextView: View { .font(fonts.body) .tint(tintColor) .onAppear { - detectLinks(for: message) + displayedText = attributedString(for: message) } .onChange(of: message, perform: { updated in - detectLinks(for: updated) + displayedText = attributedString(for: updated) }) } - func detectLinks(for message: ChatMessage) { - guard utils.messageListConfig.localLinkDetectionEnabled else { return } - var attributes: [NSAttributedString.Key: Any] = [ - .foregroundColor: textColor(for: message), - .font: fonts.body - ] + private func attributedString(for message: ChatMessage) -> AttributedString { + var text = message.adjustedText - let additional = utils.messageListConfig.messageDisplayOptions.messageLinkDisplayResolver(message) - for (key, value) in additional { - if key == .foregroundColor, let value = value as? UIColor { - tintColor = Color(value) - } else { - attributes[key] = value - } + // Translation + if let translatedText = message.textContent(for: translationLanguage) { + text = translatedText } - - let attributedText = NSMutableAttributedString( - string: message.adjustedText, - attributes: attributes - ) - let attributedTextString = attributedText.string - var containsLinks = false - - message.mentionedUsers.forEach { user in - containsLinks = true - let mention = "@\(user.name ?? user.id)" - attributedTextString - .ranges(of: mention, options: [.caseInsensitive]) - .map { NSRange($0, in: attributedTextString) } - .forEach { - let messageId = message.messageId.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) - if let messageId { - attributedText.addAttribute(.link, value: "getstream://mention/\(messageId)/\(user.id)", range: $0) + // Markdown + let attributes = AttributeContainer() + .foregroundColor(textColor(for: message)) + .font(fonts.body) + var attributedString: AttributedString + if utils.messageListConfig.markdownSupportEnabled { + attributedString = utils.markdownFormatter.format( + text, + attributes: attributes, + layoutDirection: layoutDirection + ) + } else { + attributedString = AttributedString(message.adjustedText, attributes: attributes) + } + // Links and mentions + if utils.messageListConfig.localLinkDetectionEnabled { + for user in message.mentionedUsers { + let mention = "@\(user.name ?? user.id)" + let ranges = attributedString.ranges(of: mention, options: [.caseInsensitive]) + for range in ranges { + if let messageId = message.messageId.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed), + let url = URL(string: "getstream://mention/\(messageId)/\(user.id)") { + attributedString[range].link = url } } - } - - let range = NSRange(location: 0, length: message.adjustedText.utf16.count) - linkDetector.links(in: message.adjustedText).forEach { textLink in - let escapedOriginalText = NSRegularExpression.escapedPattern(for: textLink.originalText) - let pattern = "\\[([^\\]]+)\\]\\(\(escapedOriginalText)\\)" - if let regex = try? NSRegularExpression(pattern: pattern) { - containsLinks = (regex.firstMatch( - in: message.adjustedText, - options: [], - range: range - ) == nil) || !markdownEnabled - } else { - containsLinks = true } - - if !message.adjustedText.contains("](\(textLink.originalText))") { - containsLinks = true + for link in linkDetector.links(in: String(attributedString.characters)) { + if let attributedStringRange = Range(link.range, in: attributedString) { + attributedString[attributedStringRange].link = link.url + } } - attributedText.addAttribute(.link, value: textLink.url, range: textLink.range) } - - if containsLinks { - displayedText = AttributedString(attributedText) + // Finally change attributes for links (markdown links, text links, mentions) + var linkAttributes = utils.messageListConfig.messageDisplayOptions.messageLinkDisplayResolver(message) + if !linkAttributes.isEmpty { + var linkAttributeContainer = AttributeContainer() + if let uiColor = linkAttributes[.foregroundColor] as? UIColor { + linkAttributeContainer = linkAttributeContainer.foregroundColor(Color(uiColor: uiColor)) + linkAttributes.removeValue(forKey: .foregroundColor) + } + linkAttributeContainer.merge(AttributeContainer(linkAttributes)) + for (value, range) in attributedString.runs[\.link] { + guard value != nil else { continue } + attributedString[range].mergeAttributes(linkAttributeContainer) + } } + + return attributedString } } diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollAllOptionsView.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollAllOptionsView.swift index 3a82a9c61..c16bf8089 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollAllOptionsView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollAllOptionsView.swift @@ -5,7 +5,7 @@ import StreamChat import SwiftUI -struct PollAllOptionsView: View { +struct PollAllOptionsView: View { @Injected(\.colors) var colors @Injected(\.fonts) var fonts @@ -14,6 +14,8 @@ struct PollAllOptionsView: View { @ObservedObject var viewModel: PollAttachmentViewModel + let factory: Factory + var body: some View { NavigationView { ScrollView { @@ -29,6 +31,7 @@ struct PollAllOptionsView: View { ForEach(viewModel.poll.options) { option in PollOptionView( viewModel: viewModel, + factory: factory, option: option, optionFont: fonts.headline, textColor: Color(colors.text), diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollAttachmentView.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollAttachmentView.swift index 39fcb92c2..caaacda7b 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollAttachmentView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollAttachmentView.swift @@ -55,6 +55,7 @@ public struct PollAttachmentView: View { ForEach(options.prefix(PollAttachmentViewModel.numberOfVisibleOptionsShown)) { option in PollOptionView( viewModel: viewModel, + factory: factory, option: option, optionVotes: poll.voteCount(for: option), maxVotes: poll.currentMaximumVoteCount, @@ -73,7 +74,7 @@ public struct PollAttachmentView: View { ) } .fullScreenCover(isPresented: $viewModel.allOptionsShown) { - PollAllOptionsView(viewModel: viewModel) + PollAllOptionsView(viewModel: viewModel, factory: factory) } } @@ -115,7 +116,7 @@ public struct PollAttachmentView: View { Text(L10n.Message.Polls.Button.viewNumberOfComments(viewModel.poll.answersCount)) } .fullScreenCover(isPresented: $viewModel.allCommentsShown) { - PollCommentsView(poll: viewModel.poll, pollController: viewModel.pollController) + PollCommentsView(factory: factory, poll: viewModel.poll, pollController: viewModel.pollController) } } @@ -125,7 +126,7 @@ public struct PollAttachmentView: View { Text(L10n.Message.Polls.Button.viewResults) } .fullScreenCover(isPresented: $viewModel.pollResultsShown) { - PollResultsView(viewModel: viewModel) + PollResultsView(viewModel: viewModel, factory: factory) } if viewModel.showEndVoteButton { @@ -182,10 +183,11 @@ public struct PollAttachmentView: View { extension PollOption: Identifiable {} -struct PollOptionView: View { +struct PollOptionView: View { @ObservedObject var viewModel: PollAttachmentViewModel + let factory: Factory let option: PollOption var optionFont: Font = InjectedValues[\.fonts].body var optionVotes: Int? @@ -221,9 +223,13 @@ struct PollOptionView: View { ForEach( option.latestVotes.prefix(2) ) { vote in - MessageAvatarView( - avatarURL: vote.user?.imageURL, - size: .init(width: 20, height: 20) + factory.makeMessageAvatarView( + for: UserDisplayInfo( + id: vote.user?.id ?? "", + name: vote.user?.name ?? "", + imageURL: vote.user?.imageURL, + size: .init(width: 20, height: 20) + ) ) } } diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollCommentsView.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollCommentsView.swift index 7edfec010..c404d7ea5 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollCommentsView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollCommentsView.swift @@ -5,15 +5,22 @@ import StreamChat import SwiftUI -struct PollCommentsView: View { +struct PollCommentsView: View { @Injected(\.colors) var colors @Environment(\.presentationMode) var presentationMode @StateObject var viewModel: PollCommentsViewModel + let factory: Factory - init(poll: Poll, pollController: PollController, viewModel: PollCommentsViewModel? = nil) { + init( + factory: Factory, + poll: Poll, + pollController: PollController, + viewModel: PollCommentsViewModel? = nil + ) { + self.factory = factory _viewModel = StateObject( wrappedValue: viewModel ?? PollCommentsViewModel( poll: poll, @@ -33,7 +40,12 @@ struct PollCommentsView: View { .bold() HStack { if viewModel.pollController.poll?.votingVisibility != .anonymous { - MessageAvatarView(avatarURL: comment.user?.imageURL) + let displayInfo = UserDisplayInfo( + id: comment.user?.id ?? "", + name: comment.user?.name ?? "", + imageURL: comment.user?.imageURL + ) + factory.makeMessageAvatarView(for: displayInfo) } Text(authorTitle(for: comment)) Spacer() diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollOptionAllVotesView.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollOptionAllVotesView.swift index 8ced55309..ad77fa65c 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollOptionAllVotesView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollOptionAllVotesView.swift @@ -5,11 +5,13 @@ import StreamChat import SwiftUI -struct PollOptionAllVotesView: View { +struct PollOptionAllVotesView: View { @StateObject var viewModel: PollOptionAllVotesViewModel + let factory: Factory - init(poll: Poll, option: PollOption) { + init(factory: Factory, poll: Poll, option: PollOption) { + self.factory = factory _viewModel = StateObject( wrappedValue: PollOptionAllVotesViewModel( poll: poll, @@ -22,6 +24,7 @@ struct PollOptionAllVotesView: View { ScrollView { LazyVStack { PollOptionResultsView( + factory: factory, poll: viewModel.poll, option: viewModel.option, votes: viewModel.pollVotes, diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollResultsView.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollResultsView.swift index 4cc4b7816..d6a4d5bf5 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollResultsView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollResultsView.swift @@ -5,12 +5,14 @@ import StreamChat import SwiftUI -struct PollResultsView: View { +struct PollResultsView: View { @Environment(\.presentationMode) var presentationMode @ObservedObject var viewModel: PollAttachmentViewModel + let factory: Factory + @Injected(\.colors) var colors @Injected(\.fonts) var fonts @@ -42,6 +44,7 @@ struct PollResultsView: View { ForEach(viewModel.poll.options) { option in PollOptionResultsView( + factory: factory, poll: viewModel.poll, option: option, votes: Array( @@ -74,11 +77,12 @@ struct PollResultsView: View { } } -struct PollOptionResultsView: View { +struct PollOptionResultsView: View { @Injected(\.colors) var colors @Injected(\.fonts) var fonts + let factory: Factory var poll: Poll var option: PollOption var votes: [PollVote] @@ -104,9 +108,13 @@ struct PollOptionResultsView: View { ForEach(votes, id: \.displayId) { vote in HStack { if poll.votingVisibility != .anonymous { - MessageAvatarView( - avatarURL: vote.user?.imageURL, - size: .init(width: 20, height: 20) + factory.makeMessageAvatarView( + for: UserDisplayInfo( + id: vote.user?.id ?? "", + name: vote.user?.name ?? "", + imageURL: vote.user?.imageURL, + size: .init(width: 20, height: 20) + ) ) } Text(vote.user?.name ?? (vote.user?.id ?? L10n.Message.Polls.unknownVoteAuthor)) @@ -120,7 +128,7 @@ struct PollOptionResultsView: View { if allButtonShown { NavigationLink { - PollOptionAllVotesView(poll: poll, option: option) + PollOptionAllVotesView(factory: factory, poll: poll, option: option) } label: { Text(L10n.Message.Polls.Button.showAll) } diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/QuotedMessageView.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/QuotedMessageView.swift index 0d2dd31c9..74d8f7907 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/QuotedMessageView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/QuotedMessageView.swift @@ -56,6 +56,7 @@ struct QuotedMessageViewContainer: View { /// View for the quoted message. public struct QuotedMessageView: View { + @Environment(\.channelTranslationLanguage) var translationLanguage @Injected(\.images) private var images @Injected(\.fonts) private var fonts @@ -178,7 +179,9 @@ public struct QuotedMessageView: View { } private var textForMessage: String { - let textContent = quotedMessage.textContent ?? "" + let translatedTextContent = quotedMessage.textContent(for: translationLanguage) + let textContent = translatedTextContent ?? quotedMessage.textContent ?? "" + if !textContent.isEmpty { return textContent } diff --git a/Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListItem.swift b/Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListItem.swift index 27ca4a9b5..a5e2e55d2 100644 --- a/Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListItem.swift +++ b/Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListItem.swift @@ -286,13 +286,13 @@ extension ChatChannel { public var previewMessageText: String? { guard let previewMessage else { return nil } let messageFormatter = InjectedValues[\.utils].messagePreviewFormatter - return messageFormatter.format(previewMessage) + return messageFormatter.format(previewMessage, in: self) } public var lastMessageText: String? { guard let latestMessage = latestMessages.first else { return nil } let messageFormatter = InjectedValues[\.utils].messagePreviewFormatter - return messageFormatter.format(latestMessage) + return messageFormatter.format(latestMessage, in: self) } public var shouldShowTypingIndicator: Bool { diff --git a/Sources/StreamChatSwiftUI/ChatChannelList/SearchResultsView.swift b/Sources/StreamChatSwiftUI/ChatChannelList/SearchResultsView.swift index 593576b69..b21b48d4f 100644 --- a/Sources/StreamChatSwiftUI/ChatChannelList/SearchResultsView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannelList/SearchResultsView.swift @@ -169,7 +169,7 @@ struct SearchResultItem: View { guard let previewMessage = searchResult.message else { return L10n.Channel.Item.emptyMessages } - return utils.messagePreviewFormatter.format(previewMessage) + return utils.messagePreviewFormatter.format(previewMessage, in: searchResult.channel) case .messages: return searchResult.message?.text ?? "" default: diff --git a/Sources/StreamChatSwiftUI/ChatThreadList/ChatThreadListItem.swift b/Sources/StreamChatSwiftUI/ChatThreadList/ChatThreadListItem.swift index 60a0a071d..9114dd409 100644 --- a/Sources/StreamChatSwiftUI/ChatThreadList/ChatThreadListItem.swift +++ b/Sources/StreamChatSwiftUI/ChatThreadList/ChatThreadListItem.swift @@ -51,7 +51,7 @@ public struct ChatThreadListItemViewModel { parentMessageText = threadTitle } else { let formatter = InjectedValues[\.utils].messagePreviewFormatter - parentMessageText = formatter.formatContent(for: thread.parentMessage) + parentMessageText = formatter.formatContent(for: thread.parentMessage, in: thread.channel) } return L10n.Thread.Item.repliedTo(parentMessageText.trimmed) } @@ -67,7 +67,7 @@ public struct ChatThreadListItemViewModel { } let formatter = InjectedValues[\.utils].messagePreviewFormatter - return formatter.format(latestReply) + return formatter.format(latestReply, in: thread.channel) } /// The formatted latest reply timestamp. diff --git a/Sources/StreamChatSwiftUI/DefaultViewFactory.swift b/Sources/StreamChatSwiftUI/DefaultViewFactory.swift index 6cb0133c6..4f350e9e3 100644 --- a/Sources/StreamChatSwiftUI/DefaultViewFactory.swift +++ b/Sources/StreamChatSwiftUI/DefaultViewFactory.swift @@ -285,7 +285,7 @@ extension ViewFactory { } public func makeMessageAvatarView(for userDisplayInfo: UserDisplayInfo) -> some View { - MessageAvatarView(avatarURL: userDisplayInfo.imageURL) + MessageAvatarView(avatarURL: userDisplayInfo.imageURL, size: userDisplayInfo.size ?? .messageAvatarSize) } public func makeQuotedMessageAvatarView( @@ -301,6 +301,10 @@ extension ViewFactory { DefaultChannelHeaderModifier(factory: self, channel: channel) } + public func makeChannelBarsVisibilityViewModifier(shouldShow: Bool) -> some ViewModifier { + ChangeChannelBarsVisibilityModifier(shouldShow: shouldShow) + } + public func makeChannelLoadingView() -> some View { LoadingView() } diff --git a/Sources/StreamChatSwiftUI/Fonts.swift b/Sources/StreamChatSwiftUI/Fonts.swift index afa66541c..8d8041f18 100644 --- a/Sources/StreamChatSwiftUI/Fonts.swift +++ b/Sources/StreamChatSwiftUI/Fonts.swift @@ -21,6 +21,7 @@ public struct Fonts { public var headline = Font.headline public var headlineBold = Font.headline.bold() public var title = Font.title + public var title2 = Font.title2 public var title3 = Font.title3 public var emoji = Font.system(size: 50) } diff --git a/Sources/StreamChatSwiftUI/Generated/L10n.swift b/Sources/StreamChatSwiftUI/Generated/L10n.swift index 545b91c68..da90dcc0c 100644 --- a/Sources/StreamChatSwiftUI/Generated/L10n.swift +++ b/Sources/StreamChatSwiftUI/Generated/L10n.swift @@ -352,6 +352,10 @@ internal enum L10n { internal static var deletedMessagePlaceholder: String { L10n.tr("Localizable", "message.deleted-message-placeholder") } /// Only visible to you internal static var onlyVisibleToYou: String { L10n.tr("Localizable", "message.only-visible-to-you") } + /// Translated to %@ + internal static func translatedTo(_ p1: Any) -> String { + return L10n.tr("Localizable", "message.translatedTo", String(describing: p1)) + } internal enum Actions { /// Copy Message internal static var copy: String { L10n.tr("Localizable", "message.actions.copy") } diff --git a/Sources/StreamChatSwiftUI/Generated/SystemEnvironment+Version.swift b/Sources/StreamChatSwiftUI/Generated/SystemEnvironment+Version.swift index 770f85777..d41a3df3c 100644 --- a/Sources/StreamChatSwiftUI/Generated/SystemEnvironment+Version.swift +++ b/Sources/StreamChatSwiftUI/Generated/SystemEnvironment+Version.swift @@ -7,5 +7,5 @@ import Foundation enum SystemEnvironment { /// A Stream Chat version. - public static let version: String = "4.73.0" + public static let version: String = "4.74.0" } diff --git a/Sources/StreamChatSwiftUI/Info.plist b/Sources/StreamChatSwiftUI/Info.plist index 27a9b193d..b41b26637 100644 --- a/Sources/StreamChatSwiftUI/Info.plist +++ b/Sources/StreamChatSwiftUI/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 4.73.0 + 4.74.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPhotoLibraryUsageDescription diff --git a/Sources/StreamChatSwiftUI/Resources/en.lproj/Localizable.strings b/Sources/StreamChatSwiftUI/Resources/en.lproj/Localizable.strings index 3c1369c2d..3bbb5368b 100644 --- a/Sources/StreamChatSwiftUI/Resources/en.lproj/Localizable.strings +++ b/Sources/StreamChatSwiftUI/Resources/en.lproj/Localizable.strings @@ -89,6 +89,8 @@ "message.polls.unknown-vote-author" = "Anonymous"; "message.polls.votes" = "%d votes"; +"message.translatedTo" = "Translated to %@"; + "alert.actions.cancel" = "Cancel"; "alert.actions.delete" = "Delete"; "alert.actions.end" = "End"; diff --git a/Sources/StreamChatSwiftUI/Utils.swift b/Sources/StreamChatSwiftUI/Utils.swift index 349eb5206..c36970e32 100644 --- a/Sources/StreamChatSwiftUI/Utils.swift +++ b/Sources/StreamChatSwiftUI/Utils.swift @@ -10,6 +10,7 @@ import StreamChat public class Utils { // TODO: Make it public in future versions. internal var messagePreviewFormatter = MessagePreviewFormatter() + var markdownFormatter = MarkdownFormatter() public var dateFormatter: DateFormatter public var videoPreviewLoader: VideoPreviewLoader diff --git a/Sources/StreamChatSwiftUI/Utils/Common/AttributedString+Extensions.swift b/Sources/StreamChatSwiftUI/Utils/Common/AttributedString+Extensions.swift new file mode 100644 index 000000000..89c1a962e --- /dev/null +++ b/Sources/StreamChatSwiftUI/Utils/Common/AttributedString+Extensions.swift @@ -0,0 +1,27 @@ +// +// Copyright ยฉ 2025 Stream.io Inc. All rights reserved. +// + +import Foundation + +@available(iOS 15, *) +extension AttributedStringProtocol { + func ranges( + of stringToFind: T, + options: String.CompareOptions = [], + locale: Locale? = nil + ) -> [Range] where T: StringProtocol { + guard !characters.isEmpty else { return [] } + var ranges = [Range]() + var source: AttributedSubstring = self[startIndex...] + while let range = source.range(of: stringToFind, options: options, locale: locale) { + ranges.append(range) + if range.upperBound < endIndex { + source = self[range.upperBound...] + } else { + break + } + } + return ranges + } +} diff --git a/Sources/StreamChatSwiftUI/Utils/Common/ChatMessage+Extensions.swift b/Sources/StreamChatSwiftUI/Utils/Common/ChatMessage+Extensions.swift index 26f3c27ab..a44103587 100644 --- a/Sources/StreamChatSwiftUI/Utils/Common/ChatMessage+Extensions.swift +++ b/Sources/StreamChatSwiftUI/Utils/Common/ChatMessage+Extensions.swift @@ -46,6 +46,12 @@ public extension ChatMessage { return isDeleted ? L10n.Message.deletedMessagePlaceholder : adjustedText } + + func textContent(for translationLanguage: TranslationLanguage?) -> String? { + guard let translationLanguage else { return nil } + guard !isSentByCurrentUser, !isDeleted else { return nil } + return translatedText(for: translationLanguage) + } /// A boolean value that checks if the message is visible for current user only. var isOnlyVisibleForCurrentUser: Bool { @@ -95,3 +101,9 @@ public extension ChatMessage { return isSentByCurrentUser } } + +extension TranslationLanguage { + var localizedName: String? { + Locale.current.localizedString(forLanguageCode: languageCode) + } +} diff --git a/Sources/StreamChatSwiftUI/Utils/Common/UIImage+Extensions.swift b/Sources/StreamChatSwiftUI/Utils/Common/UIImage+Extensions.swift index 8e7f7c9c6..30a0eb2ef 100644 --- a/Sources/StreamChatSwiftUI/Utils/Common/UIImage+Extensions.swift +++ b/Sources/StreamChatSwiftUI/Utils/Common/UIImage+Extensions.swift @@ -42,7 +42,7 @@ extension UIImage { } extension UIImage { - func temporaryLocalFileUrl() throws -> URL? { + func saveAsJpgToTemporaryUrl() throws -> URL? { guard let imageData = jpegData(compressionQuality: 1.0) else { return nil } let imageName = "\(UUID().uuidString).jpg" let documentDirectory = NSTemporaryDirectory() diff --git a/Sources/StreamChatSwiftUI/Utils/MarkdownFormatter.swift b/Sources/StreamChatSwiftUI/Utils/MarkdownFormatter.swift new file mode 100644 index 000000000..97faebdca --- /dev/null +++ b/Sources/StreamChatSwiftUI/Utils/MarkdownFormatter.swift @@ -0,0 +1,87 @@ +// +// Copyright ยฉ 2025 Stream.io Inc. All rights reserved. +// + +import Foundation +import StreamChat +import SwiftUI + +/// Converts markdown string to AttributedString with styling attributes. +final class MarkdownFormatter { + @Injected(\.colors) private var colors + @Injected(\.fonts) private var fonts + + private let markdownParser = MarkdownParser() + + @available(iOS 15, *) + func format( + _ string: String, + attributes: AttributeContainer, + layoutDirection: LayoutDirection + ) -> AttributedString { + do { + return try markdownParser.style( + markdown: string, + options: MarkdownParser.ParsingOptions(layoutDirectionLeftToRight: layoutDirection == .leftToRight), + attributes: attributes, + inlinePresentationIntentAttributes: inlinePresentationIntentAttributes(for:), + presentationIntentAttributes: presentationIntentAttributes(for:in:) + ) + } catch { + log.debug("Failed to parse markdown with error \(error.localizedDescription)") + return AttributedString(string, attributes: attributes) + } + } + + // MARK: - Styling Attributes + + @available(iOS 15, *) + private func inlinePresentationIntentAttributes( + for inlinePresentationIntent: InlinePresentationIntent + ) -> AttributeContainer? { + nil // use default attributes + } + + @available(iOS 15, *) + private func presentationIntentAttributes( + for presentationKind: PresentationIntent.Kind, + in presentationIntent: PresentationIntent + ) -> AttributeContainer? { + switch presentationKind { + case .blockQuote: + return AttributeContainer() + .foregroundColor(Color(colors.subtitleText)) + case .codeBlock: + return AttributeContainer() + .font(fonts.body.monospaced()) + case let .header(level): + let font: Font = { + switch level { + case 1: + return fonts.title + case 2: + return fonts.title2 + case 3: + return fonts.title3 + case 4: + return fonts.headline + case 5: + return fonts.subheadline + default: + return fonts.footnote + } + }() + let foregroundColor: Color? = level >= 6 ? Color(colors.subtitleText) : nil + if let foregroundColor { + return AttributeContainer() + .font(font) + .foregroundColor(foregroundColor) + } else { + return AttributeContainer() + .font(font) + } + default: + return nil + } + } +} diff --git a/Sources/StreamChatSwiftUI/Utils/MessageCachingUtils.swift b/Sources/StreamChatSwiftUI/Utils/MessageCachingUtils.swift index 445624fec..f26aeb6bb 100644 --- a/Sources/StreamChatSwiftUI/Utils/MessageCachingUtils.swift +++ b/Sources/StreamChatSwiftUI/Utils/MessageCachingUtils.swift @@ -33,12 +33,20 @@ public struct UserDisplayInfo { public let name: String public let imageURL: URL? public let role: UserRole? + public let size: CGSize? - public init(id: String, name: String, imageURL: URL?, role: UserRole? = nil) { + public init( + id: String, + name: String, + imageURL: URL?, + role: UserRole? = nil, + size: CGSize? = nil + ) { self.id = id self.name = name self.imageURL = imageURL self.role = role + self.size = size } } diff --git a/Sources/StreamChatSwiftUI/Utils/MessagePreviewFormatter.swift b/Sources/StreamChatSwiftUI/Utils/MessagePreviewFormatter.swift index d20dbff68..2a8d5a6bd 100644 --- a/Sources/StreamChatSwiftUI/Utils/MessagePreviewFormatter.swift +++ b/Sources/StreamChatSwiftUI/Utils/MessagePreviewFormatter.swift @@ -13,33 +13,33 @@ struct MessagePreviewFormatter { init() {} /// Formats the message including the author's name. - func format(_ previewMessage: ChatMessage) -> String { + func format(_ previewMessage: ChatMessage, in channel: ChatChannel) -> String { if let poll = previewMessage.poll { return formatPoll(poll) } - return "\(previewMessage.author.name ?? previewMessage.author.id): \(formatContent(for: previewMessage))" + return "\(previewMessage.author.name ?? previewMessage.author.id): \(formatContent(for: previewMessage, in: channel))" } /// Formats only the content of the message without the author's name. - func formatContent(for previewMessage: ChatMessage) -> String { - if let attachmentPreviewText = formatAttachmentContent(for: previewMessage) { + func formatContent(for previewMessage: ChatMessage, in channel: ChatChannel) -> String { + if let attachmentPreviewText = formatAttachmentContent(for: previewMessage, in: channel) { return attachmentPreviewText } - if let textContent = previewMessage.textContent, !textContent.isEmpty { + if let textContent = previewMessage.textContent(for: channel.membership?.language), !textContent.isEmpty { return textContent } return previewMessage.adjustedText } /// Formats only the attachment content of the message in case it contains attachments. - func formatAttachmentContent(for previewMessage: ChatMessage) -> String? { + func formatAttachmentContent(for previewMessage: ChatMessage, in channel: ChatChannel) -> String? { if let poll = previewMessage.poll { return "๐Ÿ“Š \(poll.name)" } guard let attachment = previewMessage.allAttachments.first, !previewMessage.isDeleted else { return nil } - let text = previewMessage.textContent ?? previewMessage.text + let text = previewMessage.textContent(for: channel.membership?.language) ?? previewMessage.text switch attachment.type { case .audio: let defaultAudioText = L10n.Channel.Item.audio diff --git a/Sources/StreamChatSwiftUI/Utils/Modifiers.swift b/Sources/StreamChatSwiftUI/Utils/Modifiers.swift index 5765d7674..706e109d5 100644 --- a/Sources/StreamChatSwiftUI/Utils/Modifiers.swift +++ b/Sources/StreamChatSwiftUI/Utils/Modifiers.swift @@ -79,7 +79,7 @@ struct IconOverImageModifier: ViewModifier { } } -struct ChangeBarsVisibilityModifier: ViewModifier { +struct ChangeChannelBarsVisibilityModifier: ViewModifier { @Injected(\.utils) private var utils @@ -110,10 +110,6 @@ extension View { public func applyDefaultIconOverlayStyle() -> some View { modifier(IconOverImageModifier()) } - - public func changeBarsVisibility(shouldShow: Bool) -> some View { - modifier(ChangeBarsVisibilityModifier(shouldShow: shouldShow)) - } } extension Image { diff --git a/Sources/StreamChatSwiftUI/ViewFactory.swift b/Sources/StreamChatSwiftUI/ViewFactory.swift index d09530d61..7f4db11d9 100644 --- a/Sources/StreamChatSwiftUI/ViewFactory.swift +++ b/Sources/StreamChatSwiftUI/ViewFactory.swift @@ -282,6 +282,12 @@ public protocol ViewFactory: AnyObject { /// - Parameter channel: the displayed channel. func makeChannelHeaderViewModifier(for channel: ChatChannel) -> ChatHeaderViewModifier + associatedtype ChangeBarsVisibilityModifier: ViewModifier + /// Creates a view modifier that changes the visibility of bars. + /// - Parameter shouldShow: A Boolean value indicating whether the bars should be shown. + /// - Returns: A view modifier that changes the visibility of bars. + func makeChannelBarsVisibilityViewModifier(shouldShow: Bool) -> ChangeBarsVisibilityModifier + associatedtype ChannelLoadingViewType: View /// Creates a loading view for the channel. func makeChannelLoadingView() -> ChannelLoadingViewType diff --git a/StreamChatSwiftUI-XCFramework.podspec b/StreamChatSwiftUI-XCFramework.podspec index 27abace5c..276fc3537 100644 --- a/StreamChatSwiftUI-XCFramework.podspec +++ b/StreamChatSwiftUI-XCFramework.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'StreamChatSwiftUI-XCFramework' - spec.version = '4.73.0' + spec.version = '4.74.0' spec.summary = 'StreamChat SwiftUI Chat Components' spec.description = 'StreamChatSwiftUI SDK offers flexible SwiftUI components able to display data provided by StreamChat SDK.' @@ -19,7 +19,7 @@ Pod::Spec.new do |spec| spec.framework = 'Foundation', 'UIKit', 'SwiftUI' - spec.dependency 'StreamChat-XCFramework', '~> 4.73.0' + spec.dependency 'StreamChat-XCFramework', '~> 4.74.0' spec.cocoapods_version = '>= 1.11.0' end diff --git a/StreamChatSwiftUI.podspec b/StreamChatSwiftUI.podspec index e7e0d44a3..f33d6d23c 100644 --- a/StreamChatSwiftUI.podspec +++ b/StreamChatSwiftUI.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'StreamChatSwiftUI' - spec.version = '4.73.0' + spec.version = '4.74.0' spec.summary = 'StreamChat SwiftUI Chat Components' spec.description = 'StreamChatSwiftUI SDK offers flexible SwiftUI components able to display data provided by StreamChat SDK.' @@ -19,5 +19,5 @@ Pod::Spec.new do |spec| spec.framework = 'Foundation', 'UIKit', 'SwiftUI' - spec.dependency 'StreamChat', '~> 4.73.0' + spec.dependency 'StreamChat', '~> 4.74.0' end diff --git a/StreamChatSwiftUI.xcodeproj/project.pbxproj b/StreamChatSwiftUI.xcodeproj/project.pbxproj index 2c8f21e90..16fe77630 100644 --- a/StreamChatSwiftUI.xcodeproj/project.pbxproj +++ b/StreamChatSwiftUI.xcodeproj/project.pbxproj @@ -19,6 +19,12 @@ 4F7720AE2C58C45200BAEC02 /* OnLoadViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F7720AD2C58C45000BAEC02 /* OnLoadViewModifier.swift */; }; 4F7DD9A02BFC7C6100599AA6 /* ChatClient+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F7DD99F2BFC7C6100599AA6 /* ChatClient+Extensions.swift */; }; 4F7DD9A22BFCB2EF00599AA6 /* ChatClientExtensions_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F7DD9A12BFCB2EF00599AA6 /* ChatClientExtensions_Tests.swift */; }; + 4F889C562D7F000700A7BDAF /* ChatMessageExtensions_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F889C552D7F000700A7BDAF /* ChatMessageExtensions_Tests.swift */; }; + 4FA3741A2D799CA400294721 /* AppConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA374192D799CA400294721 /* AppConfigurationView.swift */; }; + 4FA3741D2D799FC300294721 /* AppConfigurationTranslationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA3741C2D799FC300294721 /* AppConfigurationTranslationView.swift */; }; + 4FA3741F2D79A64F00294721 /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA3741E2D79A64900294721 /* AppConfiguration.swift */; }; + 4FCD7DA72D632121000EEB0F /* MarkdownFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FCD7DA62D63211B000EEB0F /* MarkdownFormatter.swift */; }; + 4FCD7DBD2D633F72000EEB0F /* AttributedString+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FCD7DBC2D633F6C000EEB0F /* AttributedString+Extensions.swift */; }; 4FD3592A2C05EA8F00B1D63B /* CreatePollViewModel_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FD359292C05EA8F00B1D63B /* CreatePollViewModel_Tests.swift */; }; 4FD964622D353D88001B6838 /* FilePickerView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FD964612D353D82001B6838 /* FilePickerView_Tests.swift */; }; 4FEAB3182BFF71F70057E511 /* SwiftUI+UIAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FEAB3172BFF71F70057E511 /* SwiftUI+UIAlertController.swift */; }; @@ -609,6 +615,12 @@ 4F7720AD2C58C45000BAEC02 /* OnLoadViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnLoadViewModifier.swift; sourceTree = ""; }; 4F7DD99F2BFC7C6100599AA6 /* ChatClient+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChatClient+Extensions.swift"; sourceTree = ""; }; 4F7DD9A12BFCB2EF00599AA6 /* ChatClientExtensions_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatClientExtensions_Tests.swift; sourceTree = ""; }; + 4F889C552D7F000700A7BDAF /* ChatMessageExtensions_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageExtensions_Tests.swift; sourceTree = ""; }; + 4FA374192D799CA400294721 /* AppConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfigurationView.swift; sourceTree = ""; }; + 4FA3741C2D799FC300294721 /* AppConfigurationTranslationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfigurationTranslationView.swift; sourceTree = ""; }; + 4FA3741E2D79A64900294721 /* AppConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfiguration.swift; sourceTree = ""; }; + 4FCD7DA62D63211B000EEB0F /* MarkdownFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownFormatter.swift; sourceTree = ""; }; + 4FCD7DBC2D633F6C000EEB0F /* AttributedString+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Extensions.swift"; sourceTree = ""; }; 4FD359292C05EA8F00B1D63B /* CreatePollViewModel_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatePollViewModel_Tests.swift; sourceTree = ""; }; 4FD964612D353D82001B6838 /* FilePickerView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePickerView_Tests.swift; sourceTree = ""; }; 4FEAB3172BFF71F70057E511 /* SwiftUI+UIAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SwiftUI+UIAlertController.swift"; sourceTree = ""; }; @@ -1199,6 +1211,16 @@ path = CommonViews; sourceTree = ""; }; + 4FA3741B2D799F9E00294721 /* AppConfiguration */ = { + isa = PBXGroup; + children = ( + 4FA3741E2D79A64900294721 /* AppConfiguration.swift */, + 4FA3741C2D799FC300294721 /* AppConfigurationTranslationView.swift */, + 4FA374192D799CA400294721 /* AppConfigurationView.swift */, + ); + path = AppConfiguration; + sourceTree = ""; + }; 82A1814528FD69E8005F9D43 /* Message Delivery Status */ = { isa = PBXGroup; children = ( @@ -1617,6 +1639,7 @@ 8465FCBD27468B6900AF091E /* DemoAppSwiftUI */ = { isa = PBXGroup; children = ( + 4FA3741B2D799F9E00294721 /* AppConfiguration */, 4F65F18B2D0724F3009F69A8 /* ChannelHeader */, 8492974727ABD97F00A8EEB0 /* DemoAppSwiftUI.entitlements */, 8465FCBE27468B6900AF091E /* DemoAppSwiftUIApp.swift */, @@ -1872,24 +1895,25 @@ 8465FD312746A95600AF091E /* Utils */ = { isa = PBXGroup; children = ( + 8465FD382746A95600AF091E /* Common */, + 8465FD4A2746A95600AF091E /* BundleExtensions.swift */, + 841B64C92775BBC10016FF3B /* Errors.swift */, + AD3AB6512CB498380014D4D7 /* HideTabBarModifier.swift */, + 8465FD372746A95600AF091E /* ImageLoading.swift */, + 8465FD362746A95600AF091E /* KeyboardHandling.swift */, + 842ADEA828EB018C00F2BE36 /* LazyImageExtensions.swift */, + 8465FD352746A95600AF091E /* LazyView.swift */, + 4FCD7DA62D63211B000EEB0F /* MarkdownFormatter.swift */, + 847CEFED27C38ABE00606257 /* MessageCachingUtils.swift */, AD3AB6592CB59A660014D4D7 /* MessagePreviewFormatter.swift */, - 8465FD322746A95600AF091E /* ViewExtensions.swift */, + 8465FD342746A95600AF091E /* Modifiers.swift */, AD3AB64F2CB41B0D0014D4D7 /* NavigationContainerView.swift */, 8465FD332746A95600AF091E /* NukeImageLoader.swift */, - 8465FD342746A95600AF091E /* Modifiers.swift */, - 8465FD352746A95600AF091E /* LazyView.swift */, - 8465FD362746A95600AF091E /* KeyboardHandling.swift */, - 8465FD372746A95600AF091E /* ImageLoading.swift */, 84C0C9A228CF18F700CD0136 /* SnapshotCreator.swift */, - 8465FD4A2746A95600AF091E /* BundleExtensions.swift */, - 8465FD3B2746A95600AF091E /* StringExtensions.swift */, - 841B64C92775BBC10016FF3B /* Errors.swift */, - 847CEFED27C38ABE00606257 /* MessageCachingUtils.swift */, 84F130C02AEAA957006E7B52 /* StreamLazyImage.swift */, + 8465FD3B2746A95600AF091E /* StringExtensions.swift */, 4FEAB3172BFF71F70057E511 /* SwiftUI+UIAlertController.swift */, - 842ADEA828EB018C00F2BE36 /* LazyImageExtensions.swift */, - AD3AB6512CB498380014D4D7 /* HideTabBarModifier.swift */, - 8465FD382746A95600AF091E /* Common */, + 8465FD322746A95600AF091E /* ViewExtensions.swift */, ); path = Utils; sourceTree = ""; @@ -1897,6 +1921,7 @@ 8465FD382746A95600AF091E /* Common */ = { isa = PBXGroup; children = ( + 4FCD7DBC2D633F6C000EEB0F /* AttributedString+Extensions.swift */, 8465FD392746A95600AF091E /* AutoLayoutHelpers.swift */, 8465FD452746A95600AF091E /* Cache.swift */, 8465FD412746A95600AF091E /* ChatChannelNamer.swift */, @@ -2154,20 +2179,21 @@ 84C94D52275A135F007FE2B9 /* Utils */ = { isa = PBXGroup; children = ( + 84D6E52B2B3078D200D0056C /* AudioRecordingNameFormatter_Tests.swift */, + 84C94D61275A5BB7007FE2B9 /* ChatChannelNamer_Tests.swift */, 4F7DD9A12BFCB2EF00599AA6 /* ChatClientExtensions_Tests.swift */, - 91CC203B283C4C250049A146 /* URLUtils_Tests.swift */, + 4F889C552D7F000700A7BDAF /* ChatMessageExtensions_Tests.swift */, + 91B79FD8284E7E9C005B6E4F /* ChatUserNamer_Tests.swift */, 84C94D53275A1380007FE2B9 /* DateUtils_Tests.swift */, - 84C94D55275A1AE1007FE2B9 /* StringExtensions_Tests.swift */, - 84C94D59275A2E43007FE2B9 /* StreamChat_Utils_Tests.swift */, 84C94D5D275A3AA9007FE2B9 /* ImageCDN_Tests.swift */, - 84C94D5F275A45D2007FE2B9 /* ViewFactory_Tests.swift */, - 84C94D61275A5BB7007FE2B9 /* ChatChannelNamer_Tests.swift */, - 91B79FD8284E7E9C005B6E4F /* ChatUserNamer_Tests.swift */, - 84E1D8272976CCAF00060491 /* SortReactions_Tests.swift */, 849988AF2AE6BE4800CC95C9 /* PaddingsConfig_Tests.swift */, 84779C762AEBCA6E000A6A68 /* ReactionsIconProvider_Tests.swift */, + 84E1D8272976CCAF00060491 /* SortReactions_Tests.swift */, + 84C94D59275A2E43007FE2B9 /* StreamChat_Utils_Tests.swift */, + 84C94D55275A1AE1007FE2B9 /* StringExtensions_Tests.swift */, + 91CC203B283C4C250049A146 /* URLUtils_Tests.swift */, 84D6E5292B3077A500D0056C /* VideoDurationFormatter_Tests.swift */, - 84D6E52B2B3078D200D0056C /* AudioRecordingNameFormatter_Tests.swift */, + 84C94D5F275A45D2007FE2B9 /* ViewFactory_Tests.swift */, ); path = Utils; sourceTree = ""; @@ -2763,6 +2789,7 @@ 8465FD752746A95700AF091E /* ImageAttachmentView.swift in Sources */, 82D64C0C2AD7E5B700C5C79E /* ImageEncoders.swift in Sources */, 8465FD832746A95700AF091E /* LinkAttachmentView.swift in Sources */, + 4FCD7DA72D632121000EEB0F /* MarkdownFormatter.swift in Sources */, 82D64C082AD7E5B700C5C79E /* Operation.swift in Sources */, 82D64BEB2AD7E5B700C5C79E /* ImagePipelineTask.swift in Sources */, AD2DDA612CB040EA0040B8D4 /* NoThreadsView.swift in Sources */, @@ -2851,6 +2878,7 @@ 82D64BEE2AD7E5B700C5C79E /* AsyncTask.swift in Sources */, 8465FDD12746A95700AF091E /* Images.swift in Sources */, 844EF8ED2809AACD00CC82F9 /* NoContentView.swift in Sources */, + 4FCD7DBD2D633F72000EEB0F /* AttributedString+Extensions.swift in Sources */, 84EADEA42B2746B70046B50C /* VideoDurationFormatter.swift in Sources */, 82D64C0E2AD7E5B700C5C79E /* ImageEncoders+ImageIO.swift in Sources */, 84A1CACD2816BC420046595A /* ChatChannelInfoHelperViews.swift in Sources */, @@ -2994,6 +3022,7 @@ ADE442F02CBDAAB70066CDF7 /* ChatThreadListView_Tests.swift in Sources */, 84AB7B21277203EF00631A10 /* GalleryView_Tests.swift in Sources */, 84E0478D284A444E00BAFA17 /* ImageLoader_Mock.swift in Sources */, + 4F889C562D7F000700A7BDAF /* ChatMessageExtensions_Tests.swift in Sources */, 8423C348277DBBDA0092DCF1 /* InstantCommandsHandler_Tests.swift in Sources */, 84779C752AEBBACD000A6A68 /* BottomReactionsView_Tests.swift in Sources */, 84DEC8DF2760A1D100172876 /* MessageView_Tests.swift in Sources */, @@ -3056,6 +3085,7 @@ 84335018274BAD4B007A1B81 /* NewChatViewModel.swift in Sources */, 84B288CD274C544B00DD090B /* CreateGroupView.swift in Sources */, 845161802AE7C4E2000A9230 /* WhatsAppChannelHeader.swift in Sources */, + 4FA3741F2D79A64F00294721 /* AppConfiguration.swift in Sources */, 8465FCDC274694D200AF091E /* SceneDelegate.swift in Sources */, 8465FCD9274694D200AF091E /* LaunchAnimationState.swift in Sources */, 4F65F18A2D071798009F69A8 /* ChannelListQueryIdentifier.swift in Sources */, @@ -3066,6 +3096,7 @@ 8413C4552B4409B600190AF4 /* PinChannelHelpers.swift in Sources */, 846B8E2E2C5B8130006A6249 /* BlockedUsersViewModel.swift in Sources */, 84B288D3274D23AF00DD090B /* LoginView.swift in Sources */, + 4FA3741A2D799CA400294721 /* AppConfigurationView.swift in Sources */, 84B288D5274D286500DD090B /* LoginViewModel.swift in Sources */, 8465FCBF27468B6900AF091E /* DemoAppSwiftUIApp.swift in Sources */, 8465FCDB274694D200AF091E /* DemoUser.swift in Sources */, @@ -3073,6 +3104,7 @@ 8492974B27ABDDCB00A8EEB0 /* NotificationsHandler.swift in Sources */, 4F65F1862D06EEA7009F69A8 /* ChooseChannelQueryView.swift in Sources */, 8465FCDA274694D200AF091E /* AppDelegate.swift in Sources */, + 4FA3741D2D799FC300294721 /* AppConfigurationTranslationView.swift in Sources */, 8465FCD8274694D200AF091E /* LaunchScreen.swift in Sources */, 84B288CF274C545900DD090B /* CreateGroupViewModel.swift in Sources */, 8417AE922ADEDB6400445021 /* UserRepository.swift in Sources */, @@ -3848,7 +3880,7 @@ repositoryURL = "https://github.com/GetStream/stream-chat-swift.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 4.73.0; + minimumVersion = 4.74.0; }; }; E3A1C01A282BAC66002D1E26 /* XCRemoteSwiftPackageReference "sentry-cocoa" */ = { diff --git a/StreamChatSwiftUIArtifacts.json b/StreamChatSwiftUIArtifacts.json index df71672f1..18cd0e95f 100644 --- a/StreamChatSwiftUIArtifacts.json +++ b/StreamChatSwiftUIArtifacts.json @@ -1 +1 @@ -{"4.40.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.40.0/StreamChatSwiftUI.zip","4.41.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.41.0/StreamChatSwiftUI.zip","4.42.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.42.0/StreamChatSwiftUI.zip","4.43.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.43.0/StreamChatSwiftUI.zip","4.44.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.44.0/StreamChatSwiftUI.zip","4.45.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.45.0/StreamChatSwiftUI.zip","4.46.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.46.0/StreamChatSwiftUI.zip","4.47.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.47.0/StreamChatSwiftUI.zip","4.47.1":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.47.1/StreamChatSwiftUI.zip","4.48.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.48.0/StreamChatSwiftUI.zip","4.49.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.49.0/StreamChatSwiftUI.zip","4.50.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.50.0/StreamChatSwiftUI.zip","4.50.1":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.50.1/StreamChatSwiftUI.zip","4.51.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.51.0/StreamChatSwiftUI.zip","4.52.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.52.0/StreamChatSwiftUI.zip","4.53.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.53.0/StreamChatSwiftUI.zip","4.54.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.54.0/StreamChatSwiftUI.zip","4.55.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.55.0/StreamChatSwiftUI.zip","4.56.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.56.0/StreamChatSwiftUI.zip","4.57.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.57.0/StreamChatSwiftUI.zip","4.58.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.58.0/StreamChatSwiftUI.zip","4.59.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.59.0/StreamChatSwiftUI.zip","4.60.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.60.0/StreamChatSwiftUI.zip","4.61.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.61.0/StreamChatSwiftUI.zip","4.62.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.62.0/StreamChatSwiftUI.zip","4.63.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.63.0/StreamChatSwiftUI.zip","4.64.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.64.0/StreamChatSwiftUI.zip","4.65.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.65.0/StreamChatSwiftUI.zip","4.66.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.66.0/StreamChatSwiftUI.zip","4.67.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.67.0/StreamChatSwiftUI.zip","4.68.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.68.0/StreamChatSwiftUI.zip","4.69.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.69.0/StreamChatSwiftUI.zip","4.70.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.70.0/StreamChatSwiftUI.zip","4.71.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.71.0/StreamChatSwiftUI.zip","4.72.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.72.0/StreamChatSwiftUI.zip","4.73.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.73.0/StreamChatSwiftUI.zip"} \ No newline at end of file +{"4.40.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.40.0/StreamChatSwiftUI.zip","4.41.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.41.0/StreamChatSwiftUI.zip","4.42.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.42.0/StreamChatSwiftUI.zip","4.43.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.43.0/StreamChatSwiftUI.zip","4.44.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.44.0/StreamChatSwiftUI.zip","4.45.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.45.0/StreamChatSwiftUI.zip","4.46.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.46.0/StreamChatSwiftUI.zip","4.47.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.47.0/StreamChatSwiftUI.zip","4.47.1":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.47.1/StreamChatSwiftUI.zip","4.48.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.48.0/StreamChatSwiftUI.zip","4.49.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.49.0/StreamChatSwiftUI.zip","4.50.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.50.0/StreamChatSwiftUI.zip","4.50.1":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.50.1/StreamChatSwiftUI.zip","4.51.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.51.0/StreamChatSwiftUI.zip","4.52.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.52.0/StreamChatSwiftUI.zip","4.53.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.53.0/StreamChatSwiftUI.zip","4.54.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.54.0/StreamChatSwiftUI.zip","4.55.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.55.0/StreamChatSwiftUI.zip","4.56.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.56.0/StreamChatSwiftUI.zip","4.57.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.57.0/StreamChatSwiftUI.zip","4.58.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.58.0/StreamChatSwiftUI.zip","4.59.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.59.0/StreamChatSwiftUI.zip","4.60.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.60.0/StreamChatSwiftUI.zip","4.61.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.61.0/StreamChatSwiftUI.zip","4.62.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.62.0/StreamChatSwiftUI.zip","4.63.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.63.0/StreamChatSwiftUI.zip","4.64.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.64.0/StreamChatSwiftUI.zip","4.65.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.65.0/StreamChatSwiftUI.zip","4.66.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.66.0/StreamChatSwiftUI.zip","4.67.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.67.0/StreamChatSwiftUI.zip","4.68.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.68.0/StreamChatSwiftUI.zip","4.69.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.69.0/StreamChatSwiftUI.zip","4.70.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.70.0/StreamChatSwiftUI.zip","4.71.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.71.0/StreamChatSwiftUI.zip","4.72.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.72.0/StreamChatSwiftUI.zip","4.73.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.73.0/StreamChatSwiftUI.zip","4.74.0":"https://github.com/GetStream/stream-chat-swiftui/releases/download/4.74.0/StreamChatSwiftUI.zip"} \ No newline at end of file diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_emptySnapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_emptySnapshot.1.png deleted file mode 100644 index cef75bb04..000000000 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/FileAttachmentsView_Tests/test_fileAttachmentsView_emptySnapshot.1.png and /dev/null differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/MediaAttachmentsView_Tests/test_mediaAttachmentsView_emptySnapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/MediaAttachmentsView_Tests/test_mediaAttachmentsView_emptySnapshot.1.png deleted file mode 100644 index f0f9f09c8..000000000 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/MediaAttachmentsView_Tests/test_mediaAttachmentsView_emptySnapshot.1.png and /dev/null differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/PinnedMessagesView_Tests/test_pinnedMessagesView_emptySnapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/PinnedMessagesView_Tests/test_pinnedMessagesView_emptySnapshot.1.png deleted file mode 100644 index 02105600d..000000000 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/ChannelInfo/__Snapshots__/PinnedMessagesView_Tests/test_pinnedMessagesView_emptySnapshot.1.png and /dev/null differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/MessageComposerView_Tests.swift b/StreamChatSwiftUITests/Tests/ChatChannel/MessageComposerView_Tests.swift index 3d14cbb42..a8d3c24c1 100644 --- a/StreamChatSwiftUITests/Tests/ChatChannel/MessageComposerView_Tests.swift +++ b/StreamChatSwiftUITests/Tests/ChatChannel/MessageComposerView_Tests.swift @@ -439,4 +439,41 @@ class MessageComposerView_Tests: StreamChatTestCase { streamChat?.appearance.colors.staticColorText = .black AssertSnapshot(view, variants: .onlyUserInterfaceStyles, size: size, suffix: "themed") } + + func test_composerQuotedMessage_translated() { + let factory = DefaultViewFactory.shared + let size = CGSize(width: defaultScreenSize.width, height: 100) + + let channelController = ChatChannelTestHelpers.makeChannelController( + chatClient: chatClient, + chatChannel: .mock( + cid: .unique, + membership: .mock(id: .unique, language: .spanish) + ) + ) + let viewModel = MessageComposerViewModel(channelController: channelController, messageController: nil) + let view = ComposerInputView( + factory: factory, + text: .constant("Hello"), + selectedRangeLocation: .constant(0), + command: .constant(nil), + addedAssets: [], + addedFileURLs: [], + addedCustomAttachments: [], + quotedMessage: .constant( + .mock( + text: "Hello", + translations: [.spanish: "Hola"] + ) + ), + cooldownDuration: 0, + onCustomAttachmentTap: { _ in + }, + removeAttachmentWithId: { _ in } + ) + .environmentObject(viewModel) + .frame(width: size.width, height: size.height) + + AssertSnapshot(view, variants: .onlyUserInterfaceStyles, size: size) + } } diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/MessageContainerView_Tests.swift b/StreamChatSwiftUITests/Tests/ChatChannel/MessageContainerView_Tests.swift index fbdbdb7ca..8fd705a8d 100644 --- a/StreamChatSwiftUITests/Tests/ChatChannel/MessageContainerView_Tests.swift +++ b/StreamChatSwiftUITests/Tests/ChatChannel/MessageContainerView_Tests.swift @@ -261,6 +261,47 @@ class MessageContainerView_Tests: StreamChatTestCase { // Then assertSnapshot(matching: view, as: .image(perceptualPrecision: precision)) } + + func test_translatedText_participant_snapshot() { + // Given + let message = ChatMessage.mock( + id: .unique, + cid: .unique, + text: "Hello", + author: .mock(id: .unique), + translations: [ + .spanish: "Hola" + ] + ) + + // When + let view = testMessageViewContainer(message: message) + .environment(\.channelTranslationLanguage, .spanish) + + // Then + AssertSnapshot(view, size: CGSize(width: 375, height: 200)) + } + + func test_translatedText_myMessageIsNotTranslated_snapshot() { + // Given + let message = ChatMessage.mock( + id: .unique, + cid: .unique, + text: "Hello", + author: .mock(id: .unique), + translations: [ + .spanish: "Hola" + ], + isSentByCurrentUser: true + ) + + // When + let view = testMessageViewContainer(message: message) + .environment(\.channelTranslationLanguage, .spanish) + + // Then + AssertSnapshot(view, size: CGSize(width: 375, height: 200)) + } // MARK: - private diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/MessageView_Tests.swift b/StreamChatSwiftUITests/Tests/ChatChannel/MessageView_Tests.swift index 1d8565d8b..148f04a13 100644 --- a/StreamChatSwiftUITests/Tests/ChatChannel/MessageView_Tests.swift +++ b/StreamChatSwiftUITests/Tests/ChatChannel/MessageView_Tests.swift @@ -704,121 +704,35 @@ class MessageView_Tests: StreamChatTestCase { assertSnapshot(matching: view, as: .image(perceptualPrecision: precision)) } - func test_markdown_noLinks() { - // Given - let textMessage = ChatMessage.mock( - id: .unique, - cid: .unique, - text: "This is a **bold** text", - author: .mock(id: .unique) - ) - - // When - let view = MessageView( - factory: DefaultViewFactory.shared, - message: textMessage, - contentWidth: defaultScreenSize.width, - isFirst: true, - scrolledId: .constant(nil) - ) - .frame( - width: defaultScreenSize.width, - height: 100 - ) - - // Then - assertSnapshot(matching: view, as: .image(perceptualPrecision: precision)) - } - - func test_markdown_withLinks() { - // Given - let textMessage = ChatMessage.mock( - id: .unique, - cid: .unique, - text: "Visit Apple, click [here](https://apple.com)", - author: .mock(id: .unique) - ) - - // When - let view = MessageView( - factory: DefaultViewFactory.shared, - message: textMessage, - contentWidth: defaultScreenSize.width, - isFirst: true, - scrolledId: .constant(nil) - ) - .frame( - width: defaultScreenSize.width, - height: 100 - ) - - // Then - assertSnapshot(matching: view, as: .image(perceptualPrecision: precision)) - } - - func test_markdown_withLinksDifferentTint() { - // Given - let displayOptions = MessageDisplayOptions { _ in - [ - NSAttributedString.Key.foregroundColor: UIColor.red - ] - } - let config = MessageListConfig(messageDisplayOptions: displayOptions) - let utils = Utils(messageListConfig: config) - streamChat = StreamChat(chatClient: chatClient, utils: utils) - - let textMessage = ChatMessage.mock( - id: .unique, - cid: .unique, - text: "Visit Apple, click [here](https://apple.com)", - author: .mock(id: .unique) - ) - - // When - let view = MessageView( - factory: DefaultViewFactory.shared, - message: textMessage, - contentWidth: defaultScreenSize.width, - isFirst: true, - scrolledId: .constant(nil) - ) - .frame( - width: defaultScreenSize.width, - height: 100 - ) - - // Then - assertSnapshot(matching: view, as: .image(perceptualPrecision: precision)) + func test_linkDetection_markdownPlainAndMention() { + let mentions = [ + ChatUser.mock(id: "user_id_1", name: nil), + ChatUser.mock(id: "user_id_2", name: "Name_2") + ] + let size = messageViewSize(height: 200) + let view = messageView( + size: size, + mentions: Set(mentions), + """ + This is [markdown link](https://getstream.io) + This is plain link: https://getstream.io + This is mention 1: @user_id_1 + This is mention 2: @Name_2 + """ + ) + AssertSnapshot(view, size: size) } - func test_markdown_disabledWithLinks() { - // Given - let config = MessageListConfig(markdownSupportEnabled: false) - let utils = Utils(messageListConfig: config) - streamChat = StreamChat(chatClient: chatClient, utils: utils) - - let textMessage = ChatMessage.mock( - id: .unique, - cid: .unique, - text: "Visit Apple, click [here](https://apple.com)", - author: .mock(id: .unique) - ) - - // When - let view = MessageView( - factory: DefaultViewFactory.shared, - message: textMessage, - contentWidth: defaultScreenSize.width, - isFirst: true, - scrolledId: .constant(nil) - ) - .frame( - width: defaultScreenSize.width, - height: 100 - ) - - // Then - assertSnapshot(matching: view, as: .image(perceptualPrecision: precision)) + func test_text_withMultiline() { + let size = messageViewSize(height: 100) + let view = messageView( + size: size, + """ + This is regular text + This is the second line + """ + ) + AssertSnapshot(view, size: size) } func test_markdown_disabledWithRegularText() { @@ -915,4 +829,326 @@ class MessageView_Tests: StreamChatTestCase { // Then assertSnapshot(matching: view, as: .image(perceptualPrecision: precision)) } + + func test_markdown_text() { + let size = messageViewSize(height: 300) + let view = messageView( + size: size, + """ + This is **bold** text + This text is _italicized_ + This was ~~mistaken~~ text + This has backslashes for a newline\\ + This has html line break
Will span two lines + ***All this text is important*** + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_headers() { + let size = messageViewSize(height: 375) + let view = messageView( + size: size, + """ + # A first level heading + ## A _second_ level heading + ### A `third` level heading + #### A ~fourth~ level heading + ##### A fifth level heading + ###### A sixth level heading + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_unorderedLists() { + let size = messageViewSize(height: 550) + let view = messageView( + size: size, + """ + Unordered (no nesting) + + Fruits: + - **Oranges** (bold) + - Apples + + Trees: + * Birch + * Maple + + Animals: + + Cat + + _Dog_ (italic) + + Rabbit + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_unorderedLists_nested() { + let size = messageViewSize(height: 200) + let view = messageView( + size: size, + """ + Unordered (nested) + - First list item + - First nested + - Second nested + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_orderedList_nested_wrappedTextItem() { + // Note: alignment after wrapping is not supported (requires paragraph style support) + let size = messageViewSize(height: 400) + let view = messageView( + size: size, + """ + Unordered (wrapped text) + - First list item which has a very long text and when wrapped, should be aligned to the same item + - First nested which has a very long text and when wrapped, should be aligned to the same item + - Second nested + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_orderedLists() { + let size = messageViewSize(height: 400) + let view = messageView( + size: size, + """ + Ordered (no nesting) + + Fruits: + 1. **Oranges** (bold) + 1. Apples + + Animals: + 1. Cat + 2. _Dog_ (italic) + 3. Rabbit + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_orderedLists_nested() { + let size = messageViewSize(height: 200) + let view = messageView( + size: size, + """ + Unordered (nested) + 1. First list item + 1. First nested + 1. Second nested + 2. Second nested (2) + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_mixedLists_nested() { + let size = messageViewSize(height: 200) + let view = messageView( + size: size, + """ + Mixed (nested) + 1. First list item + - First nested + 1. Second nested + 2. Second nested (2) + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_links() { + let size = messageViewSize() + let view = messageView( + size: size, + """ + Visit Apple, click [here](https://apple.com) + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_links_customColor() { + let displayOptions = MessageDisplayOptions { _ in + [ + NSAttributedString.Key.foregroundColor: UIColor.red + ] + } + let config = MessageListConfig(messageDisplayOptions: displayOptions) + let size = messageViewSize() + let view = messageView( + size: size, + config: config, + """ + Visit Apple, click [here](https://apple.com) + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_links_markdownDisabled() { + let config = MessageListConfig(markdownSupportEnabled: false) + let size = messageViewSize() + let view = messageView( + size: size, + config: config, + """ + Visit Apple, click [here](https://apple.com) + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_links_inLists() { + let size = messageViewSize(height: 200) + let view = messageView( + size: size, + """ + This site is cool: [Stream](https://getstream.io/) + - *Hey* + - This [link](https://getstream.io/) is in a list + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_code() { + let size = messageViewSize(height: 550) + let view = messageView( + size: size, + """ + This is inline code: `git init` + + ### `Inline` in header + + Git commands: + ``` + git status + git add + git commit + ``` + + Swift: + ```swift + func formatted() -> AttributedString { + // TODO: Implement markdown formatting + } + ``` + """ + ) + AssertSnapshot(view, size: size) + } + + func test_code_inlineOnMultipleLines() { + let size = messageViewSize(height: 250) + let view = messageView( + size: size, + """ + `inline code` + + `inline code which + should render on a single line` + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_quote() { + let size = messageViewSize(height: 150) + let view = messageView( + size: size, + """ + Text that is not a quote + > Text that is a quote + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_quote_multipleLines() { + let size = messageViewSize(height: 150) + let view = messageView( + size: size, + """ + Text that is not a quote + > Quote + > should + > render + > on + > a + > single + > line + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_quote_separate() { + let size = messageViewSize(height: 250) + let view = messageView( + size: size, + """ + Text that is not a quote + > Text that is a quote + + > This is a second quote + + Another text that is not a quote + """ + ) + AssertSnapshot(view, size: size) + } + + func test_markdown_thematicBreak() { + let size = messageViewSize(height: 150) + let view = messageView( + size: size, + """ + --- + hi! + """ + ) + AssertSnapshot(view, size: size) + } + + // MARK: - + + private func messageViewSize(height: CGFloat = 100.0) -> CGSize { + CGSize(width: defaultScreenSize.width, height: height) + } + + private func messageView( + size: CGSize, + config: MessageListConfig? = nil, + mentions: Set = Set(), + _ text: String + ) -> some View { + if let config { + let utils = Utils(messageListConfig: config) + streamChat = StreamChat(chatClient: chatClient, utils: utils) + } + let textMessage = ChatMessage.mock( + id: .unique, + cid: .unique, + text: text, + author: .mock(id: .unique), + mentionedUsers: mentions + ) + return MessageView( + factory: DefaultViewFactory.shared, + message: textMessage, + contentWidth: size.width, + isFirst: true, + scrolledId: .constant(nil) + ) + .applySize(size) + } } diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/PollAttachmentView_Tests.swift b/StreamChatSwiftUITests/Tests/ChatChannel/PollAttachmentView_Tests.swift index 2679c54f6..eb9238bd5 100644 --- a/StreamChatSwiftUITests/Tests/ChatChannel/PollAttachmentView_Tests.swift +++ b/StreamChatSwiftUITests/Tests/ChatChannel/PollAttachmentView_Tests.swift @@ -97,7 +97,7 @@ final class PollAttachmentView_Tests: StreamChatTestCase { let viewModel = PollAttachmentViewModel(message: .mock(poll: poll), poll: poll) // When - let view = PollResultsView(viewModel: viewModel) + let view = PollResultsView(viewModel: viewModel, factory: DefaultViewFactory.shared) .applyDefaultSize() // Then @@ -110,7 +110,7 @@ final class PollAttachmentView_Tests: StreamChatTestCase { let viewModel = PollAttachmentViewModel(message: .mock(poll: poll), poll: poll) // When - let view = PollAllOptionsView(viewModel: viewModel) + let view = PollAllOptionsView(viewModel: viewModel, factory: DefaultViewFactory.shared) .applyDefaultSize() // Then @@ -122,7 +122,7 @@ final class PollAttachmentView_Tests: StreamChatTestCase { let poll = Poll.mock() // When - let view = PollOptionAllVotesView(poll: poll, option: poll.options[0]) + let view = PollOptionAllVotesView(factory: DefaultViewFactory.shared, poll: poll, option: poll.options[0]) .applyDefaultSize() // Then @@ -138,6 +138,7 @@ final class PollAttachmentView_Tests: StreamChatTestCase { // When let view = PollCommentsView( + factory: DefaultViewFactory.shared, poll: poll, pollController: pollController, viewModel: viewModel diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/Suggestions/TypingSuggester_Tests.swift b/StreamChatSwiftUITests/Tests/ChatChannel/Suggestions/TypingSuggester_Tests.swift index ea28d78cd..a192c6d7c 100644 --- a/StreamChatSwiftUITests/Tests/ChatChannel/Suggestions/TypingSuggester_Tests.swift +++ b/StreamChatSwiftUITests/Tests/ChatChannel/Suggestions/TypingSuggester_Tests.swift @@ -62,12 +62,12 @@ class TypingSuggester_Tests: XCTestCase { XCTAssert(suggestion?.locationRange == NSRange(location: 1, length: 2)) } - func test_typingSuggester_notFoundEmptySpace() { + func test_typingSuggester_emptySpaceAllowed() { // Given let options = TypingSuggestionOptions(symbol: "@") let typingSuggester = TypingSuggester(options: options) - let string = "@M art" - let caretLocation = 3 + let string = "@Han Solo" + let caretLocation = 6 // When let suggestion = typingSuggester.typingSuggestion( @@ -76,7 +76,8 @@ class TypingSuggester_Tests: XCTestCase { ) // Then - XCTAssert(suggestion == nil) + XCTAssertEqual("Han S", suggestion?.text) + XCTAssertEqual(NSRange(location: 1, length: 5), suggestion?.locationRange) } func test_typingSuggester_notFoundNotOnStart() { diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/GalleryView_Tests/test_galleryView_snapshotLoading.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/GalleryView_Tests/test_galleryView_snapshotLoading.1.png index dd11b446a..9db0341e9 100644 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/GalleryView_Tests/test_galleryView_snapshotLoading.1.png and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/GalleryView_Tests/test_galleryView_snapshotLoading.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageComposerView_Tests/test_composerQuotedMessage_translated.default-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageComposerView_Tests/test_composerQuotedMessage_translated.default-dark.png new file mode 100644 index 000000000..83f43292a Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageComposerView_Tests/test_composerQuotedMessage_translated.default-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageComposerView_Tests/test_composerQuotedMessage_translated.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageComposerView_Tests/test_composerQuotedMessage_translated.default-light.png new file mode 100644 index 000000000..da0b0ce7c Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageComposerView_Tests/test_composerQuotedMessage_translated.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_myMessageIsNotTranslated_snapshot.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_myMessageIsNotTranslated_snapshot.default-light.png new file mode 100644 index 000000000..2b9d3110a Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_myMessageIsNotTranslated_snapshot.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_myMessageIsNotTranslated_snapshot.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_myMessageIsNotTranslated_snapshot.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..378e2233d Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_myMessageIsNotTranslated_snapshot.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_myMessageIsNotTranslated_snapshot.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_myMessageIsNotTranslated_snapshot.rightToLeftLayout-default.png new file mode 100644 index 000000000..8e214530a Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_myMessageIsNotTranslated_snapshot.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_myMessageIsNotTranslated_snapshot.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_myMessageIsNotTranslated_snapshot.small-dark.png new file mode 100644 index 000000000..da635ee5f Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_myMessageIsNotTranslated_snapshot.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_participant_snapshot.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_participant_snapshot.default-light.png new file mode 100644 index 000000000..a244af53d Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_participant_snapshot.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_participant_snapshot.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_participant_snapshot.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..331d73e1c Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_participant_snapshot.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_participant_snapshot.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_participant_snapshot.rightToLeftLayout-default.png new file mode 100644 index 000000000..7aecf7847 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_participant_snapshot.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_participant_snapshot.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_participant_snapshot.small-dark.png new file mode 100644 index 000000000..206bd3657 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageContainerView_Tests/test_translatedText_participant_snapshot.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_code_inlineOnMultipleLines.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_code_inlineOnMultipleLines.default-light.png new file mode 100644 index 000000000..dd8d8e1b4 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_code_inlineOnMultipleLines.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_code_inlineOnMultipleLines.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_code_inlineOnMultipleLines.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..bc1731cc8 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_code_inlineOnMultipleLines.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_code_inlineOnMultipleLines.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_code_inlineOnMultipleLines.rightToLeftLayout-default.png new file mode 100644 index 000000000..1c23938e8 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_code_inlineOnMultipleLines.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_code_inlineOnMultipleLines.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_code_inlineOnMultipleLines.small-dark.png new file mode 100644 index 000000000..69b5a4493 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_code_inlineOnMultipleLines.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkDetection_markdownPlainAndMention.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkDetection_markdownPlainAndMention.default-light.png new file mode 100644 index 000000000..3cb493f41 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkDetection_markdownPlainAndMention.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkDetection_markdownPlainAndMention.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkDetection_markdownPlainAndMention.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..6aab44633 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkDetection_markdownPlainAndMention.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkDetection_markdownPlainAndMention.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkDetection_markdownPlainAndMention.rightToLeftLayout-default.png new file mode 100644 index 000000000..0d7ef9834 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkDetection_markdownPlainAndMention.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkDetection_markdownPlainAndMention.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkDetection_markdownPlainAndMention.small-dark.png new file mode 100644 index 000000000..61dc50856 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_linkDetection_markdownPlainAndMention.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_code.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_code.default-light.png new file mode 100644 index 000000000..3acedd0d0 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_code.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_code.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_code.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..525b56fdf Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_code.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_code.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_code.rightToLeftLayout-default.png new file mode 100644 index 000000000..cc0413d44 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_code.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_code.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_code.small-dark.png new file mode 100644 index 000000000..58182a42c Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_code.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_disabledWithLinks.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_disabledWithLinks.1.png deleted file mode 100644 index c28d7afaa..000000000 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_disabledWithLinks.1.png and /dev/null differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_headers.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_headers.default-light.png new file mode 100644 index 000000000..c7e3e5b96 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_headers.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_headers.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_headers.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..111f228c8 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_headers.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_headers.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_headers.rightToLeftLayout-default.png new file mode 100644 index 000000000..58ce6339f Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_headers.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_headers.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_headers.small-dark.png new file mode 100644 index 000000000..d8fcb25af Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_headers.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links.default-light.png new file mode 100644 index 000000000..6fed23ff5 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..988d8a691 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links.rightToLeftLayout-default.png new file mode 100644 index 000000000..3a5f90307 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links.small-dark.png new file mode 100644 index 000000000..e6ffe1909 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_customColor.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_customColor.default-light.png new file mode 100644 index 000000000..525f55ceb Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_customColor.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_customColor.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_customColor.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..5b8dceaee Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_customColor.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_customColor.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_customColor.rightToLeftLayout-default.png new file mode 100644 index 000000000..ba5afcf71 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_customColor.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_customColor.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_customColor.small-dark.png new file mode 100644 index 000000000..53f7718cf Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_customColor.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_inLists.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_inLists.default-light.png new file mode 100644 index 000000000..f59c4503e Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_inLists.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_inLists.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_inLists.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..9828b6ed9 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_inLists.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_inLists.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_inLists.rightToLeftLayout-default.png new file mode 100644 index 000000000..3c0595e11 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_inLists.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_inLists.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_inLists.small-dark.png new file mode 100644 index 000000000..63150f7fc Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_inLists.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_markdownDisabled.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_markdownDisabled.default-light.png new file mode 100644 index 000000000..b42770a5e Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_markdownDisabled.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_markdownDisabled.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_markdownDisabled.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..faaca1c37 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_markdownDisabled.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_markdownDisabled.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_markdownDisabled.rightToLeftLayout-default.png new file mode 100644 index 000000000..da3b6e080 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_markdownDisabled.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_markdownDisabled.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_markdownDisabled.small-dark.png new file mode 100644 index 000000000..e3c81e7d5 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_links_markdownDisabled.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_mixedLists_nested.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_mixedLists_nested.default-light.png new file mode 100644 index 000000000..5cc935c51 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_mixedLists_nested.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_mixedLists_nested.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_mixedLists_nested.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..54549914c Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_mixedLists_nested.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_mixedLists_nested.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_mixedLists_nested.rightToLeftLayout-default.png new file mode 100644 index 000000000..bbfc5f02d Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_mixedLists_nested.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_mixedLists_nested.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_mixedLists_nested.small-dark.png new file mode 100644 index 000000000..817da64c3 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_mixedLists_nested.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_noLinks.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_noLinks.1.png deleted file mode 100644 index efcf86a13..000000000 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_noLinks.1.png and /dev/null differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedList_nested_wrappedTextItem.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedList_nested_wrappedTextItem.default-light.png new file mode 100644 index 000000000..6fb83c0ca Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedList_nested_wrappedTextItem.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedList_nested_wrappedTextItem.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedList_nested_wrappedTextItem.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..c0dcc34bd Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedList_nested_wrappedTextItem.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedList_nested_wrappedTextItem.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedList_nested_wrappedTextItem.rightToLeftLayout-default.png new file mode 100644 index 000000000..c1c2f096b Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedList_nested_wrappedTextItem.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedList_nested_wrappedTextItem.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedList_nested_wrappedTextItem.small-dark.png new file mode 100644 index 000000000..9110237f6 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedList_nested_wrappedTextItem.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists.default-light.png new file mode 100644 index 000000000..667588518 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..3b709b1bb Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists.rightToLeftLayout-default.png new file mode 100644 index 000000000..a6e1ad7c9 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists.small-dark.png new file mode 100644 index 000000000..b34d1fe3b Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists_nested.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists_nested.default-light.png new file mode 100644 index 000000000..767e3a14e Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists_nested.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists_nested.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists_nested.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..a866f3fd8 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists_nested.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists_nested.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists_nested.rightToLeftLayout-default.png new file mode 100644 index 000000000..af4f5851a Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists_nested.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists_nested.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists_nested.small-dark.png new file mode 100644 index 000000000..130f07e6a Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_orderedLists_nested.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote.default-light.png new file mode 100644 index 000000000..19f7ebc24 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..15ed99e5b Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote.rightToLeftLayout-default.png new file mode 100644 index 000000000..627859192 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote.small-dark.png new file mode 100644 index 000000000..cb12c44c7 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_multipleLines.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_multipleLines.default-light.png new file mode 100644 index 000000000..b3ed7e4df Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_multipleLines.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_multipleLines.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_multipleLines.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..ac2ed93d3 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_multipleLines.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_multipleLines.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_multipleLines.rightToLeftLayout-default.png new file mode 100644 index 000000000..239b77502 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_multipleLines.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_multipleLines.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_multipleLines.small-dark.png new file mode 100644 index 000000000..0efdcc9b0 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_multipleLines.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_separate.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_separate.default-light.png new file mode 100644 index 000000000..77837d03b Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_separate.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_separate.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_separate.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..1d692cf3f Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_separate.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_separate.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_separate.rightToLeftLayout-default.png new file mode 100644 index 000000000..cbfd2a45e Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_separate.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_separate.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_separate.small-dark.png new file mode 100644 index 000000000..3a23a3568 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_quote_separate.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_text.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_text.default-light.png new file mode 100644 index 000000000..56c4dfafc Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_text.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_text.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_text.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..35f6779d9 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_text.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_text.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_text.rightToLeftLayout-default.png new file mode 100644 index 000000000..cbc9dec8d Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_text.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_text.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_text.small-dark.png new file mode 100644 index 000000000..ce0cbebba Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_text.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_thematicBreak.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_thematicBreak.default-light.png new file mode 100644 index 000000000..122044722 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_thematicBreak.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_thematicBreak.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_thematicBreak.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..0a82f373e Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_thematicBreak.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_thematicBreak.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_thematicBreak.rightToLeftLayout-default.png new file mode 100644 index 000000000..90f955f60 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_thematicBreak.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_thematicBreak.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_thematicBreak.small-dark.png new file mode 100644 index 000000000..9b65eca22 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_thematicBreak.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists.default-light.png new file mode 100644 index 000000000..4e23d8153 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..19b24f024 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists.rightToLeftLayout-default.png new file mode 100644 index 000000000..f2c8abcbd Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists.small-dark.png new file mode 100644 index 000000000..6ce672715 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists_nested.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists_nested.default-light.png new file mode 100644 index 000000000..e73a9b94e Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists_nested.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists_nested.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists_nested.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..b86036c5d Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists_nested.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists_nested.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists_nested.rightToLeftLayout-default.png new file mode 100644 index 000000000..90bb03693 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists_nested.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists_nested.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists_nested.small-dark.png new file mode 100644 index 000000000..52de6f533 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_unorderedLists_nested.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_withLinks.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_withLinks.1.png deleted file mode 100644 index 48b393ddc..000000000 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_withLinks.1.png and /dev/null differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_withLinksDifferentTint.1.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_withLinksDifferentTint.1.png deleted file mode 100644 index 22ba16972..000000000 Binary files a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_markdown_withLinksDifferentTint.1.png and /dev/null differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_text_withMultiline.default-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_text_withMultiline.default-light.png new file mode 100644 index 000000000..d2d3e22ec Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_text_withMultiline.default-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_text_withMultiline.extraExtraExtraLarge-light.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_text_withMultiline.extraExtraExtraLarge-light.png new file mode 100644 index 000000000..c9a694ad5 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_text_withMultiline.extraExtraExtraLarge-light.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_text_withMultiline.rightToLeftLayout-default.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_text_withMultiline.rightToLeftLayout-default.png new file mode 100644 index 000000000..7eaa22de0 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_text_withMultiline.rightToLeftLayout-default.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_text_withMultiline.small-dark.png b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_text_withMultiline.small-dark.png new file mode 100644 index 000000000..8b99ec391 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannel/__Snapshots__/MessageView_Tests/test_text_withMultiline.small-dark.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannelList/ChatChannelListItemView_Tests.swift b/StreamChatSwiftUITests/Tests/ChatChannelList/ChatChannelListItemView_Tests.swift index 6c62321b6..e1abbf7c5 100644 --- a/StreamChatSwiftUITests/Tests/ChatChannelList/ChatChannelListItemView_Tests.swift +++ b/StreamChatSwiftUITests/Tests/ChatChannelList/ChatChannelListItemView_Tests.swift @@ -197,7 +197,63 @@ final class ChatChannelListItemView_Tests: StreamChatTestCase { onItemTap: { _ in } ) .frame(width: defaultScreenSize.width) - + + // Then + assertSnapshot(matching: view, as: .image(perceptualPrecision: precision)) + } + + func test_channelListItem_translatedText_participant() throws { + // Given + let message = try mockTranslatedMessage( + text: "Hello", + translations: [.spanish: "Hola"], + isSentByCurrentUser: false + ) + let channel = ChatChannel.mock( + cid: .unique, + membership: .mock(id: .unique, language: .spanish), + latestMessages: [message], + previewMessage: message + ) + + // When + let view = ChatChannelListItem( + channel: channel, + channelName: "Test", + avatar: .circleImage, + onlineIndicatorShown: true, + onItemTap: { _ in } + ) + .frame(width: defaultScreenSize.width) + + // Then + assertSnapshot(matching: view, as: .image(perceptualPrecision: precision)) + } + + func test_channelListItem_translatedText_me() throws { + // Given + let message = try mockTranslatedMessage( + text: "Hello", + translations: [.spanish: "Hola"], + isSentByCurrentUser: true + ) + let channel = ChatChannel.mock( + cid: .unique, + membership: .mock(id: .unique, language: .spanish), + latestMessages: [message], + previewMessage: message + ) + + // When + let view = ChatChannelListItem( + channel: channel, + channelName: "Test", + avatar: .circleImage, + onlineIndicatorShown: true, + onItemTap: { _ in } + ) + .frame(width: defaultScreenSize.width) + // Then assertSnapshot(matching: view, as: .image(perceptualPrecision: precision)) } @@ -337,4 +393,22 @@ final class ChatChannelListItemView_Tests: StreamChatTestCase { ) ) } + + private func mockTranslatedMessage( + text: String, + translations: [TranslationLanguage: String]?, + isSentByCurrentUser: Bool + ) throws -> ChatMessage { + .mock( + id: .unique, + cid: .unique, + text: text, + type: .regular, + author: .mock(id: "user", name: "User"), + createdAt: Date(timeIntervalSince1970: 100), + translations: translations, + localState: nil, + isSentByCurrentUser: isSentByCurrentUser + ) + } } diff --git a/StreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_translatedText_me.1.png b/StreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_translatedText_me.1.png new file mode 100644 index 000000000..9edd1d560 Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_translatedText_me.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_translatedText_participant.1.png b/StreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_translatedText_participant.1.png new file mode 100644 index 000000000..66d5eb5dd Binary files /dev/null and b/StreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_translatedText_participant.1.png differ diff --git a/StreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/NoChannelsView_Tests/test_noChannelsView_snapshot.1.png b/StreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/NoChannelsView_Tests/test_noChannelsView_snapshot.1.png deleted file mode 100644 index 78485f352..000000000 Binary files a/StreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/NoChannelsView_Tests/test_noChannelsView_snapshot.1.png and /dev/null differ diff --git a/StreamChatSwiftUITests/Tests/Utils/ChatMessageExtensions_Tests.swift b/StreamChatSwiftUITests/Tests/Utils/ChatMessageExtensions_Tests.swift new file mode 100644 index 000000000..7c1be3a6f --- /dev/null +++ b/StreamChatSwiftUITests/Tests/Utils/ChatMessageExtensions_Tests.swift @@ -0,0 +1,48 @@ +// +// Copyright ยฉ 2025 Stream.io Inc. All rights reserved. +// + +import StreamChat +@testable import StreamChatSwiftUI +import XCTest + +final class ChatMessageExtensions_Tests: StreamChatTestCase { + func test_chatMessage_translatedTextContent_forParticipantReturnsTranslatedText() { + // Given + let message = ChatMessage.mock( + id: .unique, + cid: .unique, + text: "Hello", + author: .mock(id: .unique), + translations: [ + .spanish: "Hola" + ] + ) + + // When + let text = message.textContent(for: .spanish) + + // Then + XCTAssertEqual("Hola", text) + } + + func test_chatMessage_translatedTextContent_forMeDoesNotReturnTranslatedText() { + // Given + let message = ChatMessage.mock( + id: .unique, + cid: .unique, + text: "Hello", + author: .mock(id: .unique), + translations: [ + .spanish: "Hola" + ], + isSentByCurrentUser: true + ) + + // When + let text = message.textContent(for: .spanish) + + // Then + XCTAssertEqual(nil, text) + } +} diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 5620b3668..d4c10bc0d 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -7,7 +7,7 @@ require 'xcodeproj' import 'Sonarfile' import 'Allurefile' -xcode_version = ENV['XCODE_VERSION'] || '16.1' +xcode_version = ENV['XCODE_VERSION'] || '16.2' xcode_project = 'StreamChatSwiftUI.xcodeproj' sdk_names = ['StreamChatSwiftUI'] github_repo = ENV['GITHUB_REPOSITORY'] || 'GetStream/stream-chat-swiftui' @@ -23,7 +23,7 @@ before_all do |lane| if is_ci setup_ci setup_git_config - xcversion(version: xcode_version) unless [:publish_release, :sonar_upload, :allure_launch, :allure_upload, :copyright, :pod_lint].include?(lane) + xcversion(version: xcode_version) unless [:sonar_upload, :allure_launch, :allure_upload, :copyright, :pod_lint].include?(lane) end end