diff --git a/Brewfile.lock.json b/Brewfile.lock.json index 05e62462271..f4b0dd44eaf 100644 --- a/Brewfile.lock.json +++ b/Brewfile.lock.json @@ -56,45 +56,45 @@ } }, "vale": { - "version": "2.29.6", + "version": "2.30.0", "bottle": { "rebuild": 0, "root_url": "https://ghcr.io/v2/homebrew/core", "files": { "arm64_sonoma": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:05e3a10167a5eb5a006ee84b9475667ecffb905c91b96c8e2f14d1c8155c33f7", - "sha256": "05e3a10167a5eb5a006ee84b9475667ecffb905c91b96c8e2f14d1c8155c33f7" + "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:c7705e7772367c6d82cfd41dd0fe1662065aac1a50b3a35fa9fa58d652327d1c", + "sha256": "c7705e7772367c6d82cfd41dd0fe1662065aac1a50b3a35fa9fa58d652327d1c" }, "arm64_ventura": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:1c76354047dbab61dd6eabd57a7696fa1e018c734195f2a3162e8d7391e972ae", - "sha256": "1c76354047dbab61dd6eabd57a7696fa1e018c734195f2a3162e8d7391e972ae" + "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:f12ab9bf3e44bbca9c8aa65ea06058fba07c3d97739d9b44a041f607a9f4c988", + "sha256": "f12ab9bf3e44bbca9c8aa65ea06058fba07c3d97739d9b44a041f607a9f4c988" }, "arm64_monterey": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:47b93b5bdfd92c399e94b0410504316fdef6a52eb63b8a2a2f02a5d78e04c47b", - "sha256": "47b93b5bdfd92c399e94b0410504316fdef6a52eb63b8a2a2f02a5d78e04c47b" + "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:df1119b58d493a2fa7087886587e8cf602ad73419c9307a18a3e64ce4f7e0ea3", + "sha256": "df1119b58d493a2fa7087886587e8cf602ad73419c9307a18a3e64ce4f7e0ea3" }, "sonoma": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:e62675ee738a215135d0b89d10ce6cd55d0f2ca09ec38e4a5655c70470a5417b", - "sha256": "e62675ee738a215135d0b89d10ce6cd55d0f2ca09ec38e4a5655c70470a5417b" + "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:e1bfa5074b197f158b34d16a0b99a774469c01b261df0f12434e0fd8221b8c81", + "sha256": "e1bfa5074b197f158b34d16a0b99a774469c01b261df0f12434e0fd8221b8c81" }, "ventura": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:6e50c718f460a0a77f6a62faebc94e951a4f45ac7e8701f0e26dd5508b3d79a6", - "sha256": "6e50c718f460a0a77f6a62faebc94e951a4f45ac7e8701f0e26dd5508b3d79a6" + "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:c95d989401b9bc4f7d0e1ee50bf8b7411fc6285c126ec3c6245a4a5eb2aba4e8", + "sha256": "c95d989401b9bc4f7d0e1ee50bf8b7411fc6285c126ec3c6245a4a5eb2aba4e8" }, "monterey": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:80f5018f609ee0e62f47b0cda14dfdc17c63bce9f3acc71d1415e1c0f634e575", - "sha256": "80f5018f609ee0e62f47b0cda14dfdc17c63bce9f3acc71d1415e1c0f634e575" + "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:03c7c04cee73031c63520a208fa3d775ec4ebb776b7e8df79aedaf4054713e1e", + "sha256": "03c7c04cee73031c63520a208fa3d775ec4ebb776b7e8df79aedaf4054713e1e" }, "x86_64_linux": { "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:b94563d083ab001aeac3d6753b4cf3f5a2ae911c71794414794d1887b9726424", - "sha256": "b94563d083ab001aeac3d6753b4cf3f5a2ae911c71794414794d1887b9726424" + "url": "https://ghcr.io/v2/homebrew/core/vale/blobs/sha256:539523fa46cdefe2a4aa752756f5724fc85dddce2b7ca1163fb41cb3c613efc3", + "sha256": "539523fa46cdefe2a4aa752756f5724fc85dddce2b7ca1163fb41cb3c613efc3" } } } @@ -118,12 +118,12 @@ "system": { "macos": { "ventura": { - "HOMEBREW_VERSION": "4.1.17", + "HOMEBREW_VERSION": "4.1.22", "HOMEBREW_PREFIX": "/usr/local", "Homebrew/homebrew-core": "api", "CLT": "15.0.0.0.1.1694021235", "Xcode": "14.3.1", - "macOS": "13.6" + "macOS": "13.6.1" } } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 52e0594c2ff..b8ea1803e77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### 🔄 Changed +# [4.46.0](https://github.com/GetStream/stream-chat-swift/releases/tag/4.46.0) +_December 21, 2023_ + +## StreamChat +### 🐞 Fixed +- Fix duplicated Runpath Search Paths [#2937](https://github.com/GetStream/stream-chat-swift/pull/2937) +- Fix `_dispatch_lane_resume` crash in `RepeatingTimer` [#2938](https://github.com/GetStream/stream-chat-swift/pull/2938) +- Fix editing of async voice messages [#2943](https://github.com/GetStream/stream-chat-swift/pull/2943) + +## StreamChatUI +### ✅ Added +- Better support for custom mixed attachments rendering [#2947](https://github.com/GetStream/stream-chat-swift/pull/2947) +### 🐞 Fixed +- Fix duplicated Runpath Search Paths [#2937](https://github.com/GetStream/stream-chat-swift/pull/2937) +- Fix file attachments not rendering file size [#2941](https://github.com/GetStream/stream-chat-swift/pull/2941) +- Fix quoted chat message view with wrong text [#2946](https://github.com/GetStream/ios-issues-tracking/issues/683) +- Fix jumping to bottom when loading new messages [#2945](https://github.com/GetStream/stream-chat-swift/pull/2945) +- Fix deleted messages always showing custom attachments [#2947](https://github.com/GetStream/stream-chat-swift/pull/2947) +- Fix messages failing to be deleted when pending updates to the server [#2949](https://github.com/GetStream/stream-chat-swift/pull/2949) + # [4.45.0](https://github.com/GetStream/stream-chat-swift/releases/tag/4.45.0) _December 11, 2023_ @@ -29,6 +49,9 @@ _December 11, 2023_ - Fix edit action possible in giphy messages [#2926](https://github.com/GetStream/stream-chat-swift/pull/2926) - Fix not adding a space in the message input when mentioning a user [#2927](https://github.com/GetStream/stream-chat-swift/pull/2927) +## ⚠️ Important +- iOS 11 support has been dropped since Xcode 15 does not allow publishing apps with iOS 11. + # [4.44.0](https://github.com/GetStream/stream-chat-swift/releases/tag/4.44.0) _November 30, 2023_ diff --git a/DemoApp/DemoAppConfiguration.swift b/DemoApp/DemoAppConfiguration.swift index b32e29fb89b..4514a455025 100644 --- a/DemoApp/DemoAppConfiguration.swift +++ b/DemoApp/DemoAppConfiguration.swift @@ -3,7 +3,6 @@ // import Atlantis -import Foundation import GDPerformanceView_Swift import Sentry import StreamChat @@ -19,24 +18,15 @@ enum DemoAppConfiguration { #endif } - // MARK: Internal configuration - - static var isStreamInternalConfiguration: Bool { - ProcessInfo.processInfo.environment["STREAM_DEV"] != nil - } - // This function is called from `DemoAppCoordinator` before the Chat UI is created static func setInternalConfiguration() { - StreamRuntimeCheck.assertionsEnabled = isStreamInternalConfiguration - StreamRuntimeCheck._isBackgroundMappingEnabled = true - AppConfig.shared.demoAppConfig.isTokenRefreshEnabled = isStreamInternalConfiguration - + StreamRuntimeCheck.assertionsEnabled = StreamRuntimeCheck.isStreamInternalConfiguration configureAtlantisIfNeeded() } // HTTP and WebSocket Proxy with Proxyman.app private static func configureAtlantisIfNeeded() { - if isStreamInternalConfiguration || AppConfig.shared.demoAppConfig.isAtlantisEnabled { + if AppConfig.shared.demoAppConfig.isAtlantisEnabled { Atlantis.start() } else { Atlantis.stop() @@ -45,7 +35,7 @@ enum DemoAppConfiguration { // Performance tracker static func showPerformanceTracker() { - guard isStreamInternalConfiguration else { return } + guard StreamRuntimeCheck.isStreamInternalConfiguration else { return } // PerformanceMonitor seems to have a bug where it cannot find the hierarchy when trying to place its view DispatchQueue.main.asyncAfter(deadline: .now() + 3) { PerformanceMonitor.shared().performanceViewConfigurator.options = [.performance] diff --git a/DemoApp/Screens/AdvancedOptionsViewController.swift b/DemoApp/Screens/AdvancedOptionsViewController.swift deleted file mode 100644 index 53bf08ec25a..00000000000 --- a/DemoApp/Screens/AdvancedOptionsViewController.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright © 2023 Stream.io Inc. All rights reserved. -// - -import UIKit - -class MainButton: UIButton { - override func updateConstraints() { - super.updateConstraints() - translatesAutoresizingMaskIntoConstraints = false - } - - override func layoutSubviews() { - super.layoutSubviews() - clipsToBounds = true - layer.cornerRadius = bounds.height / 2.0 - } -} - -class AdvancedOptionsViewController: UIViewController { - @IBOutlet var mainStackView: UIStackView! { - didSet { - mainStackView.preservesSuperviewLayoutMargins = true - mainStackView.isLayoutMarginsRelativeArrangement = true - } - } -} diff --git a/DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift b/DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift index 6b610136dc2..5f7241ff342 100644 --- a/DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift +++ b/DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift @@ -18,6 +18,8 @@ struct DemoAppConfig { var isMessageDebuggerEnabled: Bool /// A Boolean value to define if channel pinning example is enabled. var isChannelPinningEnabled: Bool + /// A Boolean value to define if custom location attachments are enabled. + var isLocationAttachmentsEnabled: Bool } class AppConfig { @@ -33,8 +35,18 @@ class AppConfig { isAtlantisEnabled: false, isTokenRefreshEnabled: false, isMessageDebuggerEnabled: false, - isChannelPinningEnabled: false + isChannelPinningEnabled: false, + isLocationAttachmentsEnabled: false ) + + StreamRuntimeCheck._isBackgroundMappingEnabled = true + + if StreamRuntimeCheck.isStreamInternalConfiguration { + demoAppConfig.isAtlantisEnabled = true + demoAppConfig.isMessageDebuggerEnabled = true + demoAppConfig.isLocationAttachmentsEnabled = true + demoAppConfig.isTokenRefreshEnabled = true + } } } @@ -137,6 +149,8 @@ class AppConfigViewController: UITableViewController { case isAtlantisEnabled case isMessageDebuggerEnabled case isChannelPinningEnabled + case isLocationAttachmentsEnabled + case isBackgroundMappingEnabled } enum ComponentsConfigOption: String, CaseIterable { @@ -270,6 +284,14 @@ class AppConfigViewController: UITableViewController { cell.accessoryView = makeSwitchButton(demoAppConfig.isChannelPinningEnabled) { [weak self] newValue in self?.demoAppConfig.isChannelPinningEnabled = newValue } + case .isLocationAttachmentsEnabled: + cell.accessoryView = makeSwitchButton(demoAppConfig.isLocationAttachmentsEnabled) { [weak self] newValue in + self?.demoAppConfig.isLocationAttachmentsEnabled = newValue + } + case .isBackgroundMappingEnabled: + cell.accessoryView = makeSwitchButton(StreamRuntimeCheck._isBackgroundMappingEnabled) { newValue in + StreamRuntimeCheck._isBackgroundMappingEnabled = newValue + } } } diff --git a/DemoApp/Screens/UserProfileViewController.swift b/DemoApp/Screens/UserProfileViewController.swift index 8ce2660a530..a3a50a6905a 100644 --- a/DemoApp/Screens/UserProfileViewController.swift +++ b/DemoApp/Screens/UserProfileViewController.swift @@ -40,7 +40,7 @@ class UserProfileViewController: UIViewController, CurrentChatUserControllerDele updateButton.backgroundColor = .systemBlue updateButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 15, bottom: 0.0, right: 15) updateButton.addTarget(self, action: #selector(didTapUpdateButton), for: .touchUpInside) - updateButton.isHidden = !DemoAppConfiguration.isStreamInternalConfiguration + updateButton.isHidden = !StreamRuntimeCheck.isStreamInternalConfiguration NSLayoutConstraint.activate([ imageView.widthAnchor.constraint(equalToConstant: 60), diff --git a/DemoApp/StreamChat/Components/CustomAttachments/DemoAttachmentViewCatalog.swift b/DemoApp/StreamChat/Components/CustomAttachments/DemoAttachmentViewCatalog.swift new file mode 100644 index 00000000000..88903b1330b --- /dev/null +++ b/DemoApp/StreamChat/Components/CustomAttachments/DemoAttachmentViewCatalog.swift @@ -0,0 +1,21 @@ +// +// Copyright © 2023 Stream.io Inc. All rights reserved. +// + +import StreamChat +import StreamChatUI + +class DemoAttachmentViewCatalog: AttachmentViewCatalog { + override class func attachmentViewInjectorClassFor(message: ChatMessage, components: Components) -> AttachmentViewInjector.Type? { + let hasMultipleAttachmentTypes = message.attachmentCounts.keys.count > 1 + let hasLocationAttachment = message.attachmentCounts.keys.contains(.location) + if AppConfig.shared.demoAppConfig.isLocationAttachmentsEnabled && hasLocationAttachment { + if hasMultipleAttachmentTypes { + return MixedAttachmentViewInjector.self + } + return LocationAttachmentViewInjector.self + } + + return super.attachmentViewInjectorClassFor(message: message, components: components) + } +} diff --git a/DemoApp/StreamChat/Components/CustomAttachments/DemoChatMessageListVC.swift b/DemoApp/StreamChat/Components/CustomAttachments/DemoChatMessageListVC.swift new file mode 100644 index 00000000000..2b407a420dc --- /dev/null +++ b/DemoApp/StreamChat/Components/CustomAttachments/DemoChatMessageListVC.swift @@ -0,0 +1,7 @@ +// +// Copyright © 2023 Stream.io Inc. All rights reserved. +// + +import StreamChatUI + +class DemoChatMessageListVC: ChatMessageListVC {} diff --git a/DemoApp/StreamChat/Components/CustomAttachments/DemoComposerVC.swift b/DemoApp/StreamChat/Components/CustomAttachments/DemoComposerVC.swift new file mode 100644 index 00000000000..669afa102f4 --- /dev/null +++ b/DemoApp/StreamChat/Components/CustomAttachments/DemoComposerVC.swift @@ -0,0 +1,56 @@ +// +// Copyright © 2023 Stream.io Inc. All rights reserved. +// + +import StreamChat +import StreamChatUI +import UIKit + +class DemoComposerVC: ComposerVC { + /// For demo purposes the locations are hard-coded. + var dummyLocations: [(latitude: Double, longitude: Double)] = [ + (38.708442, -9.136822), // Lisbon, Portugal + (37.983810, 23.727539), // Athens, Greece + (53.149118, -6.079341), // Greystones, Ireland + (41.11722, 20.80194), // Ohrid, Macedonia + (51.5074, -0.1278), // London, United Kingdom + (52.5200, 13.4050), // Berlin, Germany + (40.4168, -3.7038), // Madrid, Spain + (50.4501, 30.5234), // Kyiv, Ukraine + (41.9028, 12.4964), // Rome, Italy + (48.8566, 2.3522), // Paris, France + (44.4268, 26.1025), // Bucharest, Romania + (48.2082, 16.3738), // Vienna, Austria + (47.4979, 19.0402) // Budapest, Hungary + ] + + override var attachmentsPickerActions: [UIAlertAction] { + var actions = super.attachmentsPickerActions + + let alreadyHasLocation = content.attachments.map(\.type).contains(.location) + if AppConfig.shared.demoAppConfig.isLocationAttachmentsEnabled && !alreadyHasLocation { + let sendLocationAction = UIAlertAction( + title: "Location", + style: .default, + handler: { [weak self] _ in self?.sendLocation() } + ) + actions.append(sendLocationAction) + } + + return actions + } + + func sendLocation() { + guard let location = dummyLocations.randomElement() else { return } + let locationAttachmentPayload = LocationAttachmentPayload( + coordinate: .init(latitude: location.latitude, longitude: location.longitude) + ) + + content.attachments.append(AnyAttachmentPayload(payload: locationAttachmentPayload)) + + // In case you would want to send the location directly, without composer preview: +// channelController?.createNewMessage(text: "", attachments: [.init( +// payload: locationAttachmentPayload +// )]) + } +} diff --git a/DemoApp/StreamChat/Components/CustomAttachments/DemoQuotedChatMessageView.swift b/DemoApp/StreamChat/Components/CustomAttachments/DemoQuotedChatMessageView.swift new file mode 100644 index 00000000000..d754a999808 --- /dev/null +++ b/DemoApp/StreamChat/Components/CustomAttachments/DemoQuotedChatMessageView.swift @@ -0,0 +1,28 @@ +// +// Copyright © 2023 Stream.io Inc. All rights reserved. +// + +import StreamChat +import StreamChatUI +import UIKit + +class DemoQuotedChatMessageView: QuotedChatMessageView { + override func setAttachmentPreview(for message: ChatMessage) { + let locationAttachments = message.attachments(payloadType: LocationAttachmentPayload.self) + if let locationPayload = locationAttachments.first?.payload { + attachmentPreviewView.contentMode = .scaleAspectFit + attachmentPreviewView.image = UIImage( + systemName: "mappin.circle.fill", + withConfiguration: UIImage.SymbolConfiguration(font: .boldSystemFont(ofSize: 12)) + ) + attachmentPreviewView.tintColor = .systemRed + textView.text = """ + Location: + (\(locationPayload.coordinate.latitude),\(locationPayload.coordinate.longitude)) + """ + return + } + + super.setAttachmentPreview(for: message) + } +} diff --git a/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentPayload+AttachmentViewProvider.swift b/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentPayload+AttachmentViewProvider.swift new file mode 100644 index 00000000000..7c7bbeaeab7 --- /dev/null +++ b/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentPayload+AttachmentViewProvider.swift @@ -0,0 +1,19 @@ +// +// Copyright © 2023 Stream.io Inc. All rights reserved. +// + +import StreamChatUI +import UIKit + +/// Location Attachment Composer Preview +extension LocationAttachmentPayload: AttachmentPreviewProvider { + public static let preferredAxis: NSLayoutConstraint.Axis = .vertical + + public func previewView(components: Components) -> UIView { + /// For simplicity, we are using the same view for the Composer preview, + /// but a different one could be provided. + let preview = LocationAttachmentSnapshotView() + preview.coordinate = coordinate + return preview + } +} diff --git a/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentPayload.swift b/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentPayload.swift new file mode 100644 index 00000000000..c01b63526f0 --- /dev/null +++ b/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentPayload.swift @@ -0,0 +1,22 @@ +// +// Copyright © 2023 Stream.io Inc. All rights reserved. +// + +import StreamChat + +public extension AttachmentType { + static let location = Self(rawValue: "custom_location") +} + +struct LocationCoordinate: Codable { + let latitude: Double + let longitude: Double +} + +public struct LocationAttachmentPayload: AttachmentPayload { + public static var type: AttachmentType = .location + + var coordinate: LocationCoordinate +} + +public typealias ChatMessageLocationAttachment = ChatMessageAttachment diff --git a/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentSnapshotView.swift b/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentSnapshotView.swift new file mode 100644 index 00000000000..eecfe00938c --- /dev/null +++ b/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentSnapshotView.swift @@ -0,0 +1,131 @@ +// +// Copyright © 2023 Stream.io Inc. All rights reserved. +// + +import MapKit +import StreamChatUI +import UIKit + +class LocationAttachmentSnapshotView: _View { + static var snapshotsCache: NSCache = .init() + + var coordinate: LocationCoordinate? { + didSet { + updateContent() + } + } + + var snapshotter: MKMapSnapshotter? + + var didTapOnLocation: (() -> Void)? + + lazy var imageView: UIImageView = { + let view = UIImageView() + view.translatesAutoresizingMaskIntoConstraints = false + view.isUserInteractionEnabled = true + view.clipsToBounds = true + view.layer.cornerRadius = 16 + view.contentMode = .scaleAspectFill + return view + }() + + lazy var activityIndicatorView: UIActivityIndicatorView = { + let view = UIActivityIndicatorView() + view.translatesAutoresizingMaskIntoConstraints = false + view.style = .medium + return view + }() + + let mapOptions: MKMapSnapshotter.Options = .init() + + override func setUp() { + super.setUp() + + let tapGestureRecognizer = UITapGestureRecognizer( + target: self, + action: #selector(handleTapOnWorkoutAttachment) + ) + imageView.addGestureRecognizer(tapGestureRecognizer) + } + + override func setUpLayout() { + super.setUpLayout() + + addSubview(activityIndicatorView) + addSubview(imageView) + + NSLayoutConstraint.activate([ + imageView.leadingAnchor.constraint(equalTo: leadingAnchor), + imageView.trailingAnchor.constraint(equalTo: trailingAnchor), + imageView.topAnchor.constraint(equalTo: topAnchor), + imageView.bottomAnchor.constraint(equalTo: bottomAnchor), + activityIndicatorView.centerXAnchor.constraint(equalTo: imageView.centerXAnchor), + activityIndicatorView.centerYAnchor.constraint(equalTo: imageView.centerYAnchor) + ]) + } + + @objc func handleTapOnWorkoutAttachment() { + didTapOnLocation?() + } + + override func updateContent() { + super.updateContent() + + imageView.image = nil + + guard let coordinate = self.coordinate else { + return + } + + mapOptions.region = .init( + center: CLLocationCoordinate2D( + latitude: coordinate.latitude, + longitude: coordinate.longitude + ), + span: MKCoordinateSpan( + latitudeDelta: 0.01, + longitudeDelta: 0.01 + ) + ) + mapOptions.size = CGSize(width: 250, height: 150) + + if imageView.image == nil { + activityIndicatorView.startAnimating() + } + + let key = NSString(string: "\(coordinate.latitude),\(coordinate.longitude)") + if let snapshotImage = Self.snapshotsCache.object(forKey: key) { + imageView.image = snapshotImage + } else { + snapshotter?.cancel() + snapshotter = MKMapSnapshotter(options: mapOptions) + snapshotter?.start { snapshot, _ in + guard let snapshot = snapshot else { return } + + let image = UIGraphicsImageRenderer(size: self.mapOptions.size).image { _ in + snapshot.image.draw(at: .zero) + + let pinView = MKPinAnnotationView(annotation: nil, reuseIdentifier: nil) + let pinImage = pinView.image + + var point = snapshot.point(for: CLLocationCoordinate2D( + latitude: coordinate.latitude, + longitude: coordinate.longitude + )) + + point.x -= pinView.bounds.width / 2 + point.y -= pinView.bounds.height / 2 + point.x += pinView.centerOffset.x + point.y += pinView.centerOffset.y + pinImage?.draw(at: point) + } + + DispatchQueue.main.async { + self.activityIndicatorView.stopAnimating() + self.imageView.image = image + Self.snapshotsCache.setObject(image, forKey: key) + } + } + } + } +} diff --git a/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewDelegate.swift b/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewDelegate.swift new file mode 100644 index 00000000000..b10a69125f2 --- /dev/null +++ b/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewDelegate.swift @@ -0,0 +1,19 @@ +// +// Copyright © 2023 Stream.io Inc. All rights reserved. +// + +import StreamChat +import StreamChatUI + +protocol LocationAttachmentViewDelegate: ChatMessageContentViewDelegate { + func didTapOnLocationAttachment( + _ attachment: ChatMessageLocationAttachment + ) +} + +extension DemoChatMessageListVC: LocationAttachmentViewDelegate { + func didTapOnLocationAttachment(_ attachment: ChatMessageLocationAttachment) { + let mapViewController = LocationDetailViewController(locationAttachment: attachment) + navigationController?.pushViewController(mapViewController, animated: true) + } +} diff --git a/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewInjector.swift b/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewInjector.swift new file mode 100644 index 00000000000..a5a417476e7 --- /dev/null +++ b/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewInjector.swift @@ -0,0 +1,48 @@ +// +// Copyright © 2023 Stream.io Inc. All rights reserved. +// + +import StreamChat +import StreamChatUI +import UIKit + +class LocationAttachmentViewInjector: AttachmentViewInjector { + lazy var locationAttachmentView = LocationAttachmentSnapshotView() + + var locationAttachment: ChatMessageLocationAttachment? { + attachments(payloadType: LocationAttachmentPayload.self).first + } + + override func contentViewDidLayout(options: ChatMessageLayoutOptions) { + super.contentViewDidLayout(options: options) + + contentView.bubbleContentContainer.insertArrangedSubview(locationAttachmentView, at: 0) + + NSLayoutConstraint.activate([ + locationAttachmentView.widthAnchor.constraint(equalToConstant: 250), + locationAttachmentView.heightAnchor.constraint(equalToConstant: 150) + ]) + + locationAttachmentView.didTapOnLocation = { [weak self] in + self?.handleTapOnLocationAttachment() + } + } + + override func contentViewDidUpdateContent() { + super.contentViewDidUpdateContent() + + locationAttachmentView.coordinate = locationAttachment?.coordinate + } + + func handleTapOnLocationAttachment() { + guard let locationAttachmentDelegate = contentView.delegate as? LocationAttachmentViewDelegate else { + return + } + + guard let locationAttachment = self.locationAttachment else { + return + } + + locationAttachmentDelegate.didTapOnLocationAttachment(locationAttachment) + } +} diff --git a/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationDetailViewController.swift b/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationDetailViewController.swift new file mode 100644 index 00000000000..2bc78adb6d3 --- /dev/null +++ b/DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationDetailViewController.swift @@ -0,0 +1,46 @@ +// +// Copyright © 2023 Stream.io Inc. All rights reserved. +// + +import MapKit +import UIKit + +class LocationDetailViewController: UIViewController { + let locationAttachment: ChatMessageLocationAttachment + + init(locationAttachment: ChatMessageLocationAttachment) { + self.locationAttachment = locationAttachment + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + let mapView: MKMapView = { + let view = MKMapView() + view.isZoomEnabled = true + return view + }() + + override func viewDidLoad() { + super.viewDidLoad() + + let locationCoordinate = CLLocationCoordinate2D( + latitude: locationAttachment.coordinate.latitude, + longitude: locationAttachment.coordinate.longitude + ) + + mapView.region = .init( + center: locationCoordinate, + span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5) + ) + + let annotation = MKPointAnnotation() + annotation.coordinate = locationCoordinate + mapView.addAnnotation(annotation) + + view = mapView + } +} diff --git a/DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift b/DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift index ca7b54b44eb..99755b6d198 100644 --- a/DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift +++ b/DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift @@ -169,7 +169,10 @@ final class DemoChatChannelListRouter: ChatChannelListRouter { self.rootViewController.presentAlert(title: "User ID is not valid") return } - channelController.addMembers(userIds: [id]) { error in + channelController.addMembers( + userIds: [id], + message: "Members added to the channel" + ) { error in if let error = error { self.rootViewController.presentAlert( title: "Couldn't add user \(id) to channel \(cid)", diff --git a/DemoApp/StreamChat/StreamChatWrapper+DemoApp.swift b/DemoApp/StreamChat/StreamChatWrapper+DemoApp.swift index ac035f6be08..8983c27ba97 100644 --- a/DemoApp/StreamChat/StreamChatWrapper+DemoApp.swift +++ b/DemoApp/StreamChat/StreamChatWrapper+DemoApp.swift @@ -22,6 +22,7 @@ extension StreamChatWrapper { // Create Client client = ChatClient(config: config) + client?.registerAttachment(LocationAttachmentPayload.self) // L10N let localizationProvider = Appearance.default.localizationProvider @@ -45,6 +46,10 @@ extension StreamChatWrapper { Components.default.channelListSearchStrategy = .messages // Customize UI components + Components.default.attachmentViewCatalog = DemoAttachmentViewCatalog.self + Components.default.messageListVC = DemoChatMessageListVC.self + Components.default.quotedMessageView = DemoQuotedChatMessageView.self + Components.default.messageComposerVC = DemoComposerVC.self Components.default.channelContentView = DemoChatChannelListItemView.self Components.default.channelListRouter = DemoChatChannelListRouter.self Components.default.channelVC = DemoChatChannelVC.self @@ -52,5 +57,6 @@ extension StreamChatWrapper { Components.default.messageActionsVC = DemoChatMessageActionsVC.self Components.default.reactionsSorting = { $0.type.position < $1.type.position } Components.default.messageLayoutOptionsResolver = DemoChatMessageLayoutOptionsResolver() + Components.default.mixedAttachmentInjector.register(.location, with: LocationAttachmentViewInjector.self) } } diff --git a/DemoApp/StreamRuntimeCheck+StreamInternal.swift b/DemoApp/StreamRuntimeCheck+StreamInternal.swift new file mode 100644 index 00000000000..8bbac18f681 --- /dev/null +++ b/DemoApp/StreamRuntimeCheck+StreamInternal.swift @@ -0,0 +1,12 @@ +// +// Copyright © 2023 Stream.io Inc. All rights reserved. +// + +import Foundation +import StreamChat + +extension StreamRuntimeCheck { + static var isStreamInternalConfiguration: Bool { + ProcessInfo.processInfo.environment["STREAM_DEV"] != nil + } +} diff --git a/Documentation.docc/Documentation.md b/Documentation.docc/Documentation.md index 848e04c2a71..4ad8f07ee65 100644 --- a/Documentation.docc/Documentation.md +++ b/Documentation.docc/Documentation.md @@ -11,7 +11,7 @@ This is the official iOS SDK for [Stream Chat](https://getstream.io/chat/sdk/ios - **`SwiftUI` support:** We have developed a brand new SDK to help you have smoother Stream Chat integration in your SwiftUI apps. - **First-class support for `Combine`**: The StreamChat SDK (Low Level Client) has Combine wrappers to make it really easy use in an app that uses `Combine`. - **Fully open-source implementation:** You have access to the complete source code of the SDK here on GitHub. -- **Supports iOS 11+:** We proudly support older versions of iOS, so your app can stay available to almost everyone. +- **Supports iOS 12+:** We proudly support older versions of iOS, so your app can stay available to almost everyone. ## Quick Links diff --git a/README.md b/README.md index b6d55064973..7bfd593fa42 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ The **StreamChatSwiftUI SDK** is our UI SDK for SwiftUI components. If your appl - **`SwiftUI` support:** We have developed a brand new SDK to help you have smoother Stream Chat integration in your SwiftUI apps. - **First-class support for `Combine`**: The StreamChat SDK (Low Level Client) has Combine wrappers to make it really easy use in an app that uses `Combine`. - **Fully open-source implementation:** You have access to the complete source code of the SDK here on GitHub. -- **Supports iOS 11+:** We proudly support older versions of iOS, so your app can stay available to almost everyone. +- **Supports iOS 12+:** We proudly support older versions of iOS, so your app can stay available to almost everyone. ## **Quick Links** diff --git a/Sources/StreamChat/ChatClient.swift b/Sources/StreamChat/ChatClient.swift index 027437a20a8..7b4c0dfee6d 100644 --- a/Sources/StreamChat/ChatClient.swift +++ b/Sources/StreamChat/ChatClient.swift @@ -58,7 +58,7 @@ public class ChatClient { .video: VideoAttachmentPayload.self, .audio: AudioAttachmentPayload.self, .file: FileAttachmentPayload.self, - .voiceRecording: VideoAttachmentPayload.self + .voiceRecording: VoiceRecordingAttachmentPayload.self ] let connectionRepository: ConnectionRepository diff --git a/Sources/StreamChat/Controllers/MessageController/MessageController.swift b/Sources/StreamChat/Controllers/MessageController/MessageController.swift index 2af1ef023ff..346df1bde6d 100644 --- a/Sources/StreamChat/Controllers/MessageController/MessageController.swift +++ b/Sources/StreamChat/Controllers/MessageController/MessageController.swift @@ -715,12 +715,11 @@ public class ChatMessageController: DataController, DelegateCallable, DataStoreP extension ChatMessageController { struct Environment { var messageObserverBuilder: ( - _ isBackgroundMappingEnabled: Bool, - _ databaseContainer: DatabaseContainer, + _ context: NSManagedObjectContext, _ fetchRequest: NSFetchRequest, _ itemCreator: @escaping (MessageDTO) throws -> ChatMessage, _ fetchedResultsControllerType: NSFetchedResultsController.Type - ) -> EntityDatabaseObserverWrapper = EntityDatabaseObserverWrapper.init + ) -> EntityDatabaseObserver = EntityDatabaseObserver.init var repliesObserverBuilder: ( _ isBackgroundMappingEnabled: Bool, @@ -745,10 +744,9 @@ extension ChatMessageController { // MARK: - Private private extension ChatMessageController { - func createMessageObserver() -> EntityDatabaseObserverWrapper { + func createMessageObserver() -> EntityDatabaseObserver { let observer = environment.messageObserverBuilder( - StreamRuntimeCheck._isBackgroundMappingEnabled, - client.databaseContainer, + client.databaseContainer.viewContext, MessageDTO.message(withID: messageId), { try $0.asModel() }, NSFetchedResultsController.self diff --git a/Sources/StreamChat/Database/DTOs/MessageDTO.swift b/Sources/StreamChat/Database/DTOs/MessageDTO.swift index 67b49e58fa6..400c931aac7 100644 --- a/Sources/StreamChat/Database/DTOs/MessageDTO.swift +++ b/Sources/StreamChat/Database/DTOs/MessageDTO.swift @@ -537,7 +537,7 @@ extension MessageDTO { var isLocalOnly: Bool { if let localMessageState = self.localMessageState { - return localMessageState.isWaitingToBeSentToServer + return localMessageState.isLocalOnly } return type == MessageType.ephemeral.rawValue || type == MessageType.error.rawValue diff --git a/Sources/StreamChat/Generated/SystemEnvironment+Version.swift b/Sources/StreamChat/Generated/SystemEnvironment+Version.swift index ea500e52016..ebdc4b91e62 100644 --- a/Sources/StreamChat/Generated/SystemEnvironment+Version.swift +++ b/Sources/StreamChat/Generated/SystemEnvironment+Version.swift @@ -7,5 +7,5 @@ import Foundation extension SystemEnvironment { /// A Stream Chat version. - public static let version: String = "4.45.0" + public static let version: String = "4.46.0" } diff --git a/Sources/StreamChat/Info.plist b/Sources/StreamChat/Info.plist index 5700a9a7b6d..bb6bdb18465 100644 --- a/Sources/StreamChat/Info.plist +++ b/Sources/StreamChat/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 4.45.0 + 4.46.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) diff --git a/Sources/StreamChat/Models/ChatMessage.swift b/Sources/StreamChat/Models/ChatMessage.swift index 249040a7089..4482dcf8ba8 100644 --- a/Sources/StreamChat/Models/ChatMessage.swift +++ b/Sources/StreamChat/Models/ChatMessage.swift @@ -389,7 +389,7 @@ public extension ChatMessage { var isLocalOnly: Bool { if let localState = self.localState { - return localState.isWaitingToBeSentToServer + return localState.isLocalOnly } return type == .ephemeral || type == .error @@ -463,13 +463,9 @@ public enum LocalMessageState: String { /// Deleting of the message failed after multiple of tries. The system is not trying to delete this message anymore. case deletingFailed - var isWaitingToBeSentToServer: Bool { - switch self { - case .pendingSync, .syncing, .syncingFailed, .pendingSend, .sending, .sendingFailed: - return true - case .deleting, .deletingFailed: - return false - } + /// If the message is available only locally. The message is not on the server. + var isLocalOnly: Bool { + self == .pendingSend || self == .sendingFailed || self == .sending } } diff --git a/Sources/StreamChat/Utils/InternetConnection/InternetConnection.swift b/Sources/StreamChat/Utils/InternetConnection/InternetConnection.swift index e08c386e6aa..9bfb573bc1f 100644 --- a/Sources/StreamChat/Utils/InternetConnection/InternetConnection.swift +++ b/Sources/StreamChat/Utils/InternetConnection/InternetConnection.swift @@ -22,9 +22,6 @@ extension Notification { } /// An Internet Connection monitor. -/// -/// Basically, it's a wrapper over legacy monitor based on `Reachability` (iOS 11 only) -/// and default monitor based on `Network`.`NWPathMonitor` (iOS 12+). class InternetConnection { /// The current Internet connection status. private(set) var status: InternetConnection.Status { diff --git a/Sources/StreamChat/Utils/Timers.swift b/Sources/StreamChat/Utils/Timers.swift index 4e2b459c33f..965ed52dd6f 100644 --- a/Sources/StreamChat/Utils/Timers.swift +++ b/Sources/StreamChat/Utils/Timers.swift @@ -83,6 +83,7 @@ private class RepeatingTimer: RepeatingTimerControl { case resumed } + private let queue = DispatchQueue(label: "io.getstream.repeating-timer") private var state: State = .suspended private let timer: DispatchSourceTimer @@ -97,24 +98,30 @@ private class RepeatingTimer: RepeatingTimerControl { timer.cancel() // If the timer is suspended, calling cancel without resuming // triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902 - resume() + if state == .suspended { + timer.resume() + } } func resume() { - if state == .resumed { - return - } + queue.async { + if self.state == .resumed { + return + } - state = .resumed - timer.resume() + self.state = .resumed + self.timer.resume() + } } func suspend() { - if state == .suspended { - return - } + queue.async { + if self.state == .suspended { + return + } - state = .suspended - timer.suspend() + self.state = .suspended + self.timer.suspend() + } } } diff --git a/Sources/StreamChatUI/ChatMessageList/Attachments/AttachmentViewCatalog.swift b/Sources/StreamChatUI/ChatMessageList/Attachments/AttachmentViewCatalog.swift index b6bae0be32b..df94e012abe 100644 --- a/Sources/StreamChatUI/ChatMessageList/Attachments/AttachmentViewCatalog.swift +++ b/Sources/StreamChatUI/ChatMessageList/Attachments/AttachmentViewCatalog.swift @@ -15,8 +15,6 @@ open class AttachmentViewCatalog { message: ChatMessage, components: Components ) -> AttachmentViewInjector.Type? { - if message.isDeleted { return nil } - let attachmentCounts = message.attachmentCounts if attachmentCounts.keys.contains(.image) || attachmentCounts.keys.contains(.video) { diff --git a/Sources/StreamChatUI/ChatMessageList/Attachments/ChatFileAttachmentListView+ItemView.swift b/Sources/StreamChatUI/ChatMessageList/Attachments/ChatFileAttachmentListView+ItemView.swift index 65a4c08a5d0..73642b06508 100644 --- a/Sources/StreamChatUI/ChatMessageList/Attachments/ChatFileAttachmentListView+ItemView.swift +++ b/Sources/StreamChatUI/ChatMessageList/Attachments/ChatFileAttachmentListView+ItemView.swift @@ -108,7 +108,7 @@ extension ChatMessageFileAttachmentListView { fileNameLabel.text = content?.payload.title ?? content?.type.rawValue switch content?.uploadingState?.state { - case .uploaded: + case .uploaded, .none: fileSizeLabel.text = content?.payload.file.sizeString case .uploadingFailed: fileSizeLabel.text = L10n.Message.Sending.attachmentUploadingFailed diff --git a/Sources/StreamChatUI/ChatMessageList/Attachments/MixedAttachmentViewInjector.swift b/Sources/StreamChatUI/ChatMessageList/Attachments/MixedAttachmentViewInjector.swift index cf658f15067..d43c3fadadb 100644 --- a/Sources/StreamChatUI/ChatMessageList/Attachments/MixedAttachmentViewInjector.swift +++ b/Sources/StreamChatUI/ChatMessageList/Attachments/MixedAttachmentViewInjector.swift @@ -3,15 +3,52 @@ // import Foundation +import StreamChat /// The injector used to combine multiple types of attachment views. open class MixedAttachmentViewInjector: AttachmentViewInjector { - open lazy var injectors = [ - contentView.components.galleryAttachmentInjector.init(contentView), - contentView.components.filesAttachmentInjector.init(contentView), - contentView.components.voiceRecordingAttachmentInjector.init(contentView) + /// The registry of injectors associated with their attachment type, that support mixed attachment rendering. + /// + /// **Note:** It is an array and not a dictionary, since it defines the order of the rendering of the different types of attachments. + /// In order to customise the order, this static property can be changed. + /// + /// By default, this is the order of how mixed attachments are rendered: + /// + /// 1. Images and Videos + /// 2. Files + /// 3. Voice Messages + public static var injectorsRegistry: [(type: AttachmentType, injector: AttachmentViewInjector.Type)] = [ + (.video, GalleryAttachmentViewInjector.self), + (.image, GalleryAttachmentViewInjector.self), + (.file, FilesAttachmentViewInjector.self), + (.voiceRecording, VoiceRecordingAttachmentViewInjector.self) ] + private var _injectors: [AttachmentViewInjector] { + Self.injectorsRegistry + .filter { + contentView.content?.attachmentCounts.keys.contains($0.type) == true + } + .map { + $0.injector.init(contentView) + } + } + + // This property needs to be lazy so that we only create the injectors once. + open lazy var injectors: [AttachmentViewInjector] = _injectors + + public required init(_ contentView: ChatMessageContentView) { + super.init(contentView) + } + + /// Register a custom attachment injector if the attachment can be mixed with other types of attachments. + /// + /// **Advanced:** You can use the `injectorsRegistry` property directly in case you want to change the default order + /// of how different types of attachments are rendered. + public static func register(_ type: AttachmentType, with injector: AttachmentViewInjector.Type) { + injectorsRegistry.append((type, injector)) + } + override open func contentViewDidLayout(options: ChatMessageLayoutOptions) { injectors.forEach { $0.contentViewDidLayout(options: options) } } diff --git a/Sources/StreamChatUI/ChatMessageList/ChatMessageListVC.swift b/Sources/StreamChatUI/ChatMessageList/ChatMessageListVC.swift index db940cb1db2..a2a9eae68c1 100644 --- a/Sources/StreamChatUI/ChatMessageList/ChatMessageListVC.swift +++ b/Sources/StreamChatUI/ChatMessageList/ChatMessageListVC.swift @@ -278,6 +278,10 @@ open class ChatMessageListVC: _ViewController, return nil } + if message.isDeleted { + return nil + } + return components.attachmentViewCatalog.attachmentViewInjectorClassFor( message: message, components: components @@ -1177,12 +1181,14 @@ private extension ChatMessageListVC { } // Scroll to the bottom if the new message was sent by - // the current user, or moved by the current user, and the first page is loaded. + // the current user, or moved by the current user (ex: Giphy publish), + // and the first page is loaded. func scrollToBottomIfNeeded(with changes: [ListChange], newestChange: ListChange?) { guard isFirstPageLoaded else { return } guard let newMessage = newestChange?.item else { return } - let newestChangeIsInsertionOrMove = newestChange?.isInsertion == true || newestChange?.isMove == true - if newestChangeIsInsertionOrMove && newMessage.isSentByCurrentUser { + let numberOfInsertions = changes.filter(\.isInsertion).count + let isNewMessage = newestChange?.isInsertion == true && numberOfInsertions == 1 + if (isNewMessage || newestChange?.isMove == true) && newMessage.isSentByCurrentUser { scrollToBottom() } } diff --git a/Sources/StreamChatUI/CommonViews/QuotedChatMessageView/QuotedChatMessageView.swift b/Sources/StreamChatUI/CommonViews/QuotedChatMessageView/QuotedChatMessageView.swift index 244350ed079..59f58b78341 100644 --- a/Sources/StreamChatUI/CommonViews/QuotedChatMessageView/QuotedChatMessageView.swift +++ b/Sources/StreamChatUI/CommonViews/QuotedChatMessageView/QuotedChatMessageView.swift @@ -163,17 +163,11 @@ open class QuotedChatMessageView: _View, ThemeProvider, SwiftUIRepresentable { guard let message = content?.message else { return } guard let avatarAlignment = content?.avatarAlignment else { return } - if let currentUserLang = content?.channel?.membership?.language, - let translatedText = content?.message.translatedText(for: currentUserLang) { - textView.text = translatedText - } else { - textView.text = message.text - } - contentContainerView.backgroundColor = message.linkAttachments.isEmpty ? appearance.colorPalette.popoverBackground : appearance.colorPalette.highlightedAccentBackground1 - + + textView.text = message.text setAvatar(imageUrl: message.author.imageURL) setAvatarAlignment(avatarAlignment) @@ -183,6 +177,11 @@ open class QuotedChatMessageView: _View, ThemeProvider, SwiftUIRepresentable { setAttachmentPreview(for: message) showAttachmentPreview() } + + if let currentUserLang = content?.channel?.membership?.language, + let translatedText = content?.message.translatedText(for: currentUserLang) { + textView.text = translatedText + } } /// Sets the avatar image from a url or sets the placeholder image if the url is `nil`. @@ -232,15 +231,11 @@ open class QuotedChatMessageView: _View, ThemeProvider, SwiftUIRepresentable { if let filePayload = message.fileAttachments.first?.payload { attachmentPreviewView.contentMode = .scaleAspectFit attachmentPreviewView.image = appearance.images.fileIcons[filePayload.file.type] ?? appearance.images.fileFallback - if textView.text.isEmpty { - textView.text = filePayload.title - } + textView.text = message.text.isEmpty ? filePayload.title : message.text } else if let imagePayload = message.imageAttachments.first?.payload { attachmentPreviewView.contentMode = .scaleAspectFill setAttachmentPreviewImage(url: imagePayload.imageURL) - if textView.text.isEmpty { - textView.text = L10n.Composer.QuotedMessage.photo - } + textView.text = message.text.isEmpty ? L10n.Composer.QuotedMessage.photo : message.text } else if let linkPayload = message.linkAttachments.first?.payload { attachmentPreviewView.contentMode = .scaleAspectFill setAttachmentPreviewImage(url: linkPayload.previewURL) @@ -248,19 +243,15 @@ open class QuotedChatMessageView: _View, ThemeProvider, SwiftUIRepresentable { } else if let giphyPayload = message.giphyAttachments.first?.payload { attachmentPreviewView.contentMode = .scaleAspectFill setAttachmentPreviewImage(url: giphyPayload.previewURL) - if textView.text.isEmpty { - textView.text = L10n.Composer.QuotedMessage.giphy - } + textView.text = message.text.isEmpty ? L10n.Composer.QuotedMessage.giphy : message.text } else if let videoPayload = message.videoAttachments.first?.payload { attachmentPreviewView.contentMode = .scaleAspectFill + textView.text = message.text.isEmpty ? videoPayload.title : message.text if let thumbnailURL = videoPayload.thumbnailURL { setVideoAttachmentThumbnail(url: thumbnailURL) } else { setVideoAttachmentPreviewImage(url: videoPayload.videoURL) } - if textView.text.isEmpty { - textView.text = videoPayload.title - } } else if let voiceRecordingPayload = message.voiceRecordingAttachments.first?.payload { voiceRecordingAttachmentQuotedPreview.content = .init( title: voiceRecordingPayload.title ?? message.text, @@ -272,6 +263,7 @@ open class QuotedChatMessageView: _View, ThemeProvider, SwiftUIRepresentable { } else { voiceRecordingAttachmentQuotedPreview.isHidden = true contentContainerView.isHidden = false + textView.text = nil } } diff --git a/Sources/StreamChatUI/Info.plist b/Sources/StreamChatUI/Info.plist index 5700a9a7b6d..bb6bdb18465 100644 --- a/Sources/StreamChatUI/Info.plist +++ b/Sources/StreamChatUI/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 4.45.0 + 4.46.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) diff --git a/Sources/StreamChatUI/MessageActionsPopup/ChatMessageActionsVC.swift b/Sources/StreamChatUI/MessageActionsPopup/ChatMessageActionsVC.swift index 3c6baf0d1f7..44a94717f62 100644 --- a/Sources/StreamChatUI/MessageActionsPopup/ChatMessageActionsVC.swift +++ b/Sources/StreamChatUI/MessageActionsPopup/ChatMessageActionsVC.swift @@ -89,7 +89,9 @@ open class ChatMessageActionsVC: _ViewController, ThemeProvider { let currentUser = messageController.dataStore.currentUser(), let message = message, message.isDeleted == false - else { return [] } + else { + return [] + } switch message.localState { case nil: @@ -149,13 +151,17 @@ open class ChatMessageActionsVC: _ViewController, ThemeProvider { editActionItem(), deleteActionItem() ] - case .pendingSend, .pendingSync, .syncingFailed, .deletingFailed: + case .pendingSend, + .pendingSync, + .syncingFailed, + .deletingFailed, + .sending, + .syncing, + .deleting: return [ editActionItem(), deleteActionItem() ] - case .sending, .syncing, .deleting: - return [] } } diff --git a/StreamChat-XCFramework.podspec b/StreamChat-XCFramework.podspec index 500e78c723d..3244591ffd4 100644 --- a/StreamChat-XCFramework.podspec +++ b/StreamChat-XCFramework.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "StreamChat-XCFramework" - spec.version = "4.45.0" + spec.version = "4.46.0" spec.summary = "StreamChat iOS Client" spec.description = "stream-chat-swift is the official Swift client for Stream Chat, a service for building chat applications." diff --git a/StreamChat.podspec b/StreamChat.podspec index 6ce00e7bb30..6aae769f17f 100644 --- a/StreamChat.podspec +++ b/StreamChat.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "StreamChat" - spec.version = "4.45.0" + spec.version = "4.46.0" spec.summary = "StreamChat iOS Chat Client" spec.description = "stream-chat-swift is the official Swift client for Stream Chat, a service for building chat applications." diff --git a/StreamChat.xcodeproj/project.pbxproj b/StreamChat.xcodeproj/project.pbxproj index a0cd0a24b60..7db50fe51f8 100644 --- a/StreamChat.xcodeproj/project.pbxproj +++ b/StreamChat.xcodeproj/project.pbxproj @@ -332,7 +332,6 @@ 793060EF25778897005CF846 /* StreamChatTestTools.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 793060E625778896005CF846 /* StreamChatTestTools.framework */; }; 7931818C24FD2660002F8C84 /* ChannelListController+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7931818B24FD2660002F8C84 /* ChannelListController+Combine.swift */; }; 7931818E24FD4275002F8C84 /* ChannelListController+Combine_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7931818D24FD4275002F8C84 /* ChannelListController+Combine_Tests.swift */; }; - 79330604256FEBE600FBB586 /* AdvancedOptionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79330603256FEBE600FBB586 /* AdvancedOptionsViewController.swift */; }; 7933060B256FF94800FBB586 /* DemoChatChannelListRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7933060A256FF94800FBB586 /* DemoChatChannelListRouter.swift */; }; 7933064925712C8B00FBB586 /* DemoUsers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7933064825712C8B00FBB586 /* DemoUsers.swift */; }; 79342EEC2632C7770018F0F7 /* ChannelVisibilityEventMiddleware_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79342EEB2632C7770018F0F7 /* ChannelVisibilityEventMiddleware_Tests.swift */; }; @@ -1188,6 +1187,16 @@ AD016A1425CAFAF2009EBAD2 /* ChatChannelListVC_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD46C4DA25C431C500B405F8 /* ChatChannelListVC_Tests.swift */; }; AD050B9E265D5E12006649A5 /* QuotedChatMessageView+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD050B8C265D5E09006649A5 /* QuotedChatMessageView+SwiftUI.swift */; }; AD050BA8265D600B006649A5 /* QuotedChatMessageView+SwiftUI_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD050BA7265D600B006649A5 /* QuotedChatMessageView+SwiftUI_Tests.swift */; }; + AD053B9A2B335854003612B6 /* DemoComposerVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD053B992B335854003612B6 /* DemoComposerVC.swift */; }; + AD053B9D2B3358E2003612B6 /* LocationAttachmentPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD053B9C2B3358E2003612B6 /* LocationAttachmentPayload.swift */; }; + AD053B9F2B335929003612B6 /* LocationAttachmentViewInjector.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD053B9E2B335929003612B6 /* LocationAttachmentViewInjector.swift */; }; + AD053BA12B3359DD003612B6 /* DemoAttachmentViewCatalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD053BA02B3359DD003612B6 /* DemoAttachmentViewCatalog.swift */; }; + AD053BA32B335A13003612B6 /* LocationAttachmentPayload+AttachmentViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD053BA22B335A13003612B6 /* LocationAttachmentPayload+AttachmentViewProvider.swift */; }; + AD053BA52B335A63003612B6 /* DemoQuotedChatMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD053BA42B335A63003612B6 /* DemoQuotedChatMessageView.swift */; }; + AD053BA72B33624C003612B6 /* LocationAttachmentViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD053BA62B33624C003612B6 /* LocationAttachmentViewDelegate.swift */; }; + AD053BA92B336331003612B6 /* LocationDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD053BA82B336331003612B6 /* LocationDetailViewController.swift */; }; + AD053BAB2B33638B003612B6 /* LocationAttachmentSnapshotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD053BAA2B33638B003612B6 /* LocationAttachmentSnapshotView.swift */; }; + AD053BAD2B336493003612B6 /* DemoChatMessageListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD053BAC2B336493003612B6 /* DemoChatMessageListVC.swift */; }; AD0AD6C02A25140A00CB96CB /* MessagesPaginationState_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD0AD6BF2A25140A00CB96CB /* MessagesPaginationState_Tests.swift */; }; AD0EC6D52A45AAAF005220B1 /* ChatMessageListVC_Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD0EC6D42A45AAAF005220B1 /* ChatMessageListVC_Mock.swift */; }; AD154C6D25DC3BA000850925 /* ChatCommandSuggestionView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD154C6C25DC3BA000850925 /* ChatCommandSuggestionView_Tests.swift */; }; @@ -1257,6 +1266,8 @@ AD70DC3A2ADEC3C400CFC3B7 /* MessageModerationDetailsDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD70DC382ADEC3C400CFC3B7 /* MessageModerationDetailsDTO.swift */; }; AD70DC3C2ADEF09C00CFC3B7 /* MessageModerationDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD70DC3B2ADEF09C00CFC3B7 /* MessageModerationDetails.swift */; }; AD70DC3D2ADEF09C00CFC3B7 /* MessageModerationDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD70DC3B2ADEF09C00CFC3B7 /* MessageModerationDetails.swift */; }; + AD7110C42B3434F700AFFE28 /* StreamRuntimeCheck+StreamInternal.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7110C32B3434F700AFFE28 /* StreamRuntimeCheck+StreamInternal.swift */; }; + AD7110C52B34351800AFFE28 /* StreamRuntimeCheck+StreamInternal.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7110C32B3434F700AFFE28 /* StreamRuntimeCheck+StreamInternal.swift */; }; AD7112F325F12AA800932AEE /* ChatUserAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7112F225F12AA800932AEE /* ChatUserAvatarView.swift */; }; AD71130225F138BA00932AEE /* ChatChannelAvatarView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E791E4D525D3E2DA00B0E076 /* ChatChannelAvatarView_Tests.swift */; }; AD71131225F138D500932AEE /* ChatUserAvatarView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD71131125F138D500932AEE /* ChatUserAvatarView_Tests.swift */; }; @@ -1407,6 +1418,7 @@ ADE40043291B1A510000C98B /* AttachmentUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE40042291B1A510000C98B /* AttachmentUploader.swift */; }; ADE40044291B1A510000C98B /* AttachmentUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE40042291B1A510000C98B /* AttachmentUploader.swift */; }; ADE88A142949453200C0F084 /* ChatMessageListRouter_Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE88A132949453200C0F084 /* ChatMessageListRouter_Mock.swift */; }; + ADEDA1FA2B2BC46C00020460 /* RepeatingTimer_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADEDA1F92B2BC46C00020460 /* RepeatingTimer_Tests.swift */; }; ADEE651829BF712D00186129 /* ChatMessageListView_Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADEE651429BF711200186129 /* ChatMessageListView_Mock.swift */; }; ADEE651929BF713200186129 /* ChatMessageCell_Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADEE651629BF712500186129 /* ChatMessageCell_Mock.swift */; }; ADEE651E29BF715600186129 /* ChatMessageListVCDelegate_Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADEE651C29BF715300186129 /* ChatMessageListVCDelegate_Mock.swift */; }; @@ -2955,7 +2967,6 @@ 793060EE25778897005CF846 /* StreamChatTestToolsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StreamChatTestToolsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 7931818B24FD2660002F8C84 /* ChannelListController+Combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChannelListController+Combine.swift"; sourceTree = ""; }; 7931818D24FD4275002F8C84 /* ChannelListController+Combine_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChannelListController+Combine_Tests.swift"; sourceTree = ""; }; - 79330603256FEBE600FBB586 /* AdvancedOptionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedOptionsViewController.swift; sourceTree = ""; }; 7933060A256FF94800FBB586 /* DemoChatChannelListRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoChatChannelListRouter.swift; sourceTree = ""; }; 7933064825712C8B00FBB586 /* DemoUsers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoUsers.swift; sourceTree = ""; }; 79342EEB2632C7770018F0F7 /* ChannelVisibilityEventMiddleware_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelVisibilityEventMiddleware_Tests.swift; sourceTree = ""; }; @@ -3724,6 +3735,16 @@ AD016A3325CB0AE6009EBAD2 /* StreamChatUITestPlan.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = StreamChatUITestPlan.xctestplan; sourceTree = ""; }; AD050B8C265D5E09006649A5 /* QuotedChatMessageView+SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QuotedChatMessageView+SwiftUI.swift"; sourceTree = ""; }; AD050BA7265D600B006649A5 /* QuotedChatMessageView+SwiftUI_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QuotedChatMessageView+SwiftUI_Tests.swift"; sourceTree = ""; }; + AD053B992B335854003612B6 /* DemoComposerVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoComposerVC.swift; sourceTree = ""; }; + AD053B9C2B3358E2003612B6 /* LocationAttachmentPayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationAttachmentPayload.swift; sourceTree = ""; }; + AD053B9E2B335929003612B6 /* LocationAttachmentViewInjector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationAttachmentViewInjector.swift; sourceTree = ""; }; + AD053BA02B3359DD003612B6 /* DemoAttachmentViewCatalog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoAttachmentViewCatalog.swift; sourceTree = ""; }; + AD053BA22B335A13003612B6 /* LocationAttachmentPayload+AttachmentViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LocationAttachmentPayload+AttachmentViewProvider.swift"; sourceTree = ""; }; + AD053BA42B335A63003612B6 /* DemoQuotedChatMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoQuotedChatMessageView.swift; sourceTree = ""; }; + AD053BA62B33624C003612B6 /* LocationAttachmentViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationAttachmentViewDelegate.swift; sourceTree = ""; }; + AD053BA82B336331003612B6 /* LocationDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationDetailViewController.swift; sourceTree = ""; }; + AD053BAA2B33638B003612B6 /* LocationAttachmentSnapshotView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationAttachmentSnapshotView.swift; sourceTree = ""; }; + AD053BAC2B336493003612B6 /* DemoChatMessageListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoChatMessageListVC.swift; sourceTree = ""; }; AD0AD6BF2A25140A00CB96CB /* MessagesPaginationState_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesPaginationState_Tests.swift; sourceTree = ""; }; AD0EC6D42A45AAAF005220B1 /* ChatMessageListVC_Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageListVC_Mock.swift; sourceTree = ""; }; AD154C6C25DC3BA000850925 /* ChatCommandSuggestionView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatCommandSuggestionView_Tests.swift; sourceTree = ""; }; @@ -3775,6 +3796,7 @@ AD70DC352ADEC0F600CFC3B7 /* MessageModerationDetailsPayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageModerationDetailsPayload.swift; sourceTree = ""; }; AD70DC382ADEC3C400CFC3B7 /* MessageModerationDetailsDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageModerationDetailsDTO.swift; sourceTree = ""; }; AD70DC3B2ADEF09C00CFC3B7 /* MessageModerationDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageModerationDetails.swift; sourceTree = ""; }; + AD7110C32B3434F700AFFE28 /* StreamRuntimeCheck+StreamInternal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StreamRuntimeCheck+StreamInternal.swift"; sourceTree = ""; }; AD7112F225F12AA800932AEE /* ChatUserAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatUserAvatarView.swift; sourceTree = ""; }; AD71131125F138D500932AEE /* ChatUserAvatarView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatUserAvatarView_Tests.swift; sourceTree = ""; }; AD75CB6A27886746005F5FF7 /* OptionsSelectorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionsSelectorViewController.swift; sourceTree = ""; }; @@ -3872,6 +3894,7 @@ ADE88A132949453200C0F084 /* ChatMessageListRouter_Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageListRouter_Mock.swift; sourceTree = ""; }; ADEA7F21261D2F8C00CA2289 /* chewbacca.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = chewbacca.jpg; sourceTree = ""; }; ADEA7F22261D2F8C00CA2289 /* r2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = r2.jpg; sourceTree = ""; }; + ADEDA1F92B2BC46C00020460 /* RepeatingTimer_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepeatingTimer_Tests.swift; sourceTree = ""; }; ADEE651429BF711200186129 /* ChatMessageListView_Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageListView_Mock.swift; sourceTree = ""; }; ADEE651629BF712500186129 /* ChatMessageCell_Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageCell_Mock.swift; sourceTree = ""; }; ADEE651A29BF714300186129 /* ChatMessageListVCDataSource_Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageListVCDataSource_Mock.swift; sourceTree = ""; }; @@ -5042,6 +5065,7 @@ 792DDA67256FB69F001DB91B /* Info.plist */, 792DDA59256FB69E001DB91B /* AppDelegate.swift */, A3227E7D284A511200EBE6CC /* DemoAppConfiguration.swift */, + AD7110C32B3434F700AFFE28 /* StreamRuntimeCheck+StreamInternal.swift */, 792DDA5B256FB69E001DB91B /* SceneDelegate.swift */, 8440861528FFE85F0027849C /* Shared */, A3227E56284A47F700EBE6CC /* StreamChat */, @@ -6063,6 +6087,7 @@ A3227E77284A4CAD00EBE6CC /* DemoChatMessageContentView.swift */, 79B8B64A285CBDC00059FB2D /* DemoChatMessageLayoutOptionsResolver.swift */, A3227E71284A4BF700EBE6CC /* HiddenChannelListVC.swift */, + AD053B982B33581A003612B6 /* CustomAttachments */, A3227E67284A4AA400EBE6CC /* Banner */, ); path = Components; @@ -6071,7 +6096,6 @@ A3227ECA284A607D00EBE6CC /* Screens */ = { isa = PBXGroup; children = ( - 79330603256FEBE600FBB586 /* AdvancedOptionsViewController.swift */, AD6BEFF42786474A00E184B4 /* AppConfigViewController */, A3227E5D284A494000EBE6CC /* Create Chat */, A3227E6A284A4B0D00EBE6CC /* LoginViewController */, @@ -6833,6 +6857,7 @@ A364D0BB27D12AD50029857A /* Utils */ = { isa = PBXGroup; children = ( + ADEDA1F92B2BC46C00020460 /* RepeatingTimer_Tests.swift */, 40D3962B2A0910CF0020DDC9 /* ArraySampling_Tests.swift */, CF1F1D442824243F002E2977 /* CooldownTracker_Tests.swift */, 40789D3E29F6AFC40018C2BB /* Debouncer_Tests.swift */, @@ -7688,6 +7713,31 @@ path = ChatMessageView; sourceTree = ""; }; + AD053B982B33581A003612B6 /* CustomAttachments */ = { + isa = PBXGroup; + children = ( + AD053BA02B3359DD003612B6 /* DemoAttachmentViewCatalog.swift */, + AD053B992B335854003612B6 /* DemoComposerVC.swift */, + AD053BA42B335A63003612B6 /* DemoQuotedChatMessageView.swift */, + AD053BAC2B336493003612B6 /* DemoChatMessageListVC.swift */, + AD053B9B2B33589C003612B6 /* LocationAttachment */, + ); + path = CustomAttachments; + sourceTree = ""; + }; + AD053B9B2B33589C003612B6 /* LocationAttachment */ = { + isa = PBXGroup; + children = ( + AD053B9C2B3358E2003612B6 /* LocationAttachmentPayload.swift */, + AD053BA22B335A13003612B6 /* LocationAttachmentPayload+AttachmentViewProvider.swift */, + AD053B9E2B335929003612B6 /* LocationAttachmentViewInjector.swift */, + AD053BA62B33624C003612B6 /* LocationAttachmentViewDelegate.swift */, + AD053BAA2B33638B003612B6 /* LocationAttachmentSnapshotView.swift */, + AD053BA82B336331003612B6 /* LocationDetailViewController.swift */, + ); + path = LocationAttachment; + sourceTree = ""; + }; AD4473DD263AC2B80030E583 /* ChatCommandSuggestionView */ = { isa = PBXGroup; children = ( @@ -10114,6 +10164,9 @@ buildActionMask = 2147483647; files = ( 6428DD5526201DCC0065DA1D /* BannerShowingConnectionDelegate.swift in Sources */, + AD053BA52B335A63003612B6 /* DemoQuotedChatMessageView.swift in Sources */, + AD053BAB2B33638B003612B6 /* LocationAttachmentSnapshotView.swift in Sources */, + AD053BAD2B336493003612B6 /* DemoChatMessageListVC.swift in Sources */, C10B5C722A1F794A006A5BCB /* MembersViewController.swift in Sources */, AD6BEFF02786070800E184B4 /* SwitchButton.swift in Sources */, A3227E60284A497300EBE6CC /* GroupUserCell.swift in Sources */, @@ -10122,6 +10175,7 @@ A31783DD285B79EB005009B9 /* Bundle+PushProvider.swift in Sources */, A3227E72284A4BF700EBE6CC /* HiddenChannelListVC.swift in Sources */, AD75CB6B27886746005F5FF7 /* OptionsSelectorViewController.swift in Sources */, + AD053B9A2B335854003612B6 /* DemoComposerVC.swift in Sources */, 648EC576261EF9D400B8F05F /* DemoAppCoordinator.swift in Sources */, A3227E70284A4BC700EBE6CC /* DemoChatChannelVC.swift in Sources */, 849C1B6A283686EE00F9DC42 /* UserDefaults+Shared.swift in Sources */, @@ -10133,12 +10187,14 @@ 792DDA5A256FB69E001DB91B /* AppDelegate.swift in Sources */, 792DDAA125711AF2001DB91B /* CreateChatViewController.swift in Sources */, A3227E7E284A511200EBE6CC /* DemoAppConfiguration.swift in Sources */, + AD7110C42B3434F700AFFE28 /* StreamRuntimeCheck+StreamInternal.swift in Sources */, AD6BEFF227862F9300E184B4 /* AppConfigViewController.swift in Sources */, 792DDA5C256FB69E001DB91B /* SceneDelegate.swift in Sources */, C1CEF9072A1BC4E800414931 /* UserProfileViewController.swift in Sources */, A3227E62284A499500EBE6CC /* SearchUserCell.swift in Sources */, A3227E76284A4C6400EBE6CC /* MessageReactionType+Position.swift in Sources */, A3227E74284A4C3300EBE6CC /* DemoChatMessageActionsVC.swift in Sources */, + AD053BA72B33624C003612B6 /* LocationAttachmentViewDelegate.swift in Sources */, 43016E1626B734410054E805 /* ChatUser+CustomFields.swift in Sources */, 794E20F52577DF4D00790DAB /* NameGroupViewController.swift in Sources */, A3227EC9284A52EE00EBE6CC /* PushNotifications.swift in Sources */, @@ -10150,9 +10206,13 @@ A3227E5B284A489000EBE6CC /* UIViewController+Alert.swift in Sources */, A3227E6D284A4B6A00EBE6CC /* UserCredentialsCell.swift in Sources */, 84A33ABA28F86B8500CEC8FD /* StreamChatWrapper+DemoApp.swift in Sources */, + AD053BA92B336331003612B6 /* LocationDetailViewController.swift in Sources */, A3227E59284A484300EBE6CC /* UIImage+Resized.swift in Sources */, 79B8B64B285CBDC00059FB2D /* DemoChatMessageLayoutOptionsResolver.swift in Sources */, - 79330604256FEBE600FBB586 /* AdvancedOptionsViewController.swift in Sources */, + AD053BA32B335A13003612B6 /* LocationAttachmentPayload+AttachmentViewProvider.swift in Sources */, + AD053B9D2B3358E2003612B6 /* LocationAttachmentPayload.swift in Sources */, + AD053BA12B3359DD003612B6 /* DemoAttachmentViewCatalog.swift in Sources */, + AD053B9F2B335929003612B6 /* LocationAttachmentViewInjector.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -10806,6 +10866,7 @@ AD45333A25D153CF00CD9D47 /* ConnectionController+SwiftUI_Tests.swift in Sources */, 8A0D64AE24E5853F0017A3C0 /* DataController_Tests.swift in Sources */, 8486CAF926FA51EE00A9AD96 /* EventDTOConverterMiddleware_Tests.swift in Sources */, + ADEDA1FA2B2BC46C00020460 /* RepeatingTimer_Tests.swift in Sources */, C14A46562845064E00EF498E /* ThreadSafeWeakCollection_Tests.swift in Sources */, ADC40C3226E26E9F005B616C /* UserSearchController_Tests.swift in Sources */, 40D3962C2A0910CF0020DDC9 /* ArraySampling_Tests.swift in Sources */, @@ -11003,6 +11064,7 @@ A3698DD828215E2F00814143 /* Settings.swift in Sources */, A3813B4C2825C8030076E838 /* CustomChatMessageListRouter.swift in Sources */, 84A33ABD28F86C3700CEC8FD /* Bundle+PushProvider.swift in Sources */, + AD7110C52B34351800AFFE28 /* StreamRuntimeCheck+StreamInternal.swift in Sources */, A3698D802820187200814143 /* DebugMenu.swift in Sources */, A34407C127D8C33F0044F150 /* ViewController.swift in Sources */, A3BD4850281AC16C0090D511 /* ChannelVC.swift in Sources */, @@ -12277,11 +12339,7 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Stream.io Inc. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = io.stream.StreamChatUI; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -12309,11 +12367,7 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Stream.io Inc. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = io.stream.StreamChatUI; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -12341,11 +12395,7 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Stream.io Inc. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = io.stream.StreamChatUI; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -12437,10 +12487,7 @@ DISABLE_DIAMOND_PROBLEM_DIAGNOSTIC = YES; INFOPLIST_FILE = DemoApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.3; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited)"; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = io.getstream.iOS.ChatDemoApp; PRODUCT_NAME = ChatSample; @@ -12465,10 +12512,7 @@ DISABLE_DIAMOND_PROBLEM_DIAGNOSTIC = YES; INFOPLIST_FILE = DemoApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.3; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited)"; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = io.getstream.iOS.ChatDemoApp; PRODUCT_NAME = ChatSample; @@ -12494,10 +12538,7 @@ DISABLE_DIAMOND_PROBLEM_DIAGNOSTIC = YES; INFOPLIST_FILE = DemoApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.3; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited)"; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = io.getstream.iOS.ChatDemoApp; PRODUCT_NAME = ChatSample; @@ -12677,11 +12718,7 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Stream.io Inc. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited)"; MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = io.getstream.StreamChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -12711,11 +12748,7 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Stream.io Inc. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited)"; MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = io.getstream.StreamChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -13399,11 +13432,7 @@ INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Stream.io Inc. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = "$(inherited)"; MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = io.getstream.StreamChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; diff --git a/StreamChatArtifacts.json b/StreamChatArtifacts.json index 7236c17f662..206b9e62f35 100644 --- a/StreamChatArtifacts.json +++ b/StreamChatArtifacts.json @@ -1 +1 @@ -{"4.7.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.7.0/StreamChat-All.zip","4.8.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.8.0/StreamChat-All.zip","4.9.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.9.0/StreamChat-All.zip","4.10.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.10.0/StreamChat-All.zip","4.10.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.10.1/StreamChat-All.zip","4.11.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.11.0/StreamChat-All.zip","4.12.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.12.0/StreamChat-All.zip","4.13.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.13.0/StreamChat-All.zip","4.13.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.13.1/StreamChat-All.zip","4.14.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.14.0/StreamChat-All.zip","4.15.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.15.0/StreamChat-All.zip","4.15.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.15.1/StreamChat-All.zip","4.16.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.16.0/StreamChat-All.zip","4.17.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.17.0/StreamChat-All.zip","4.18.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.18.0/StreamChat-All.zip","4.19.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.19.0/StreamChat-All.zip","4.20.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.20.0/StreamChat-All.zip","4.21.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.21.0/StreamChat-All.zip","4.21.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.21.1/StreamChat-All.zip","4.21.2":"https://github.com/GetStream/stream-chat-swift/releases/download/4.21.2/StreamChat-All.zip","4.22.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.22.0/StreamChat-All.zip","4.23.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.23.0/StreamChat-All.zip","4.24.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.24.0/StreamChat-All.zip","4.24.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.24.1/StreamChat-All.zip","4.25.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.25.0/StreamChat-All.zip","4.25.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.25.1/StreamChat-All.zip","4.26.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.26.0/StreamChat-All.zip","4.27.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.27.0/StreamChat-All.zip","4.27.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.27.1/StreamChat-All.zip","4.28.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.28.0/StreamChat-All.zip","4.29.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.29.0/StreamChat-All.zip","4.30.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.30.0/StreamChat-All.zip","4.31.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.31.0/StreamChat-All.zip","4.32.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.32.0/StreamChat-All.zip","4.33.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.33.0/StreamChat-All.zip","4.34.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.34.0/StreamChat-All.zip","4.35.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.35.0/StreamChat-All.zip","4.35.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.35.1/StreamChat-All.zip","4.35.2":"https://github.com/GetStream/stream-chat-swift/releases/download/4.35.2/StreamChat-All.zip","4.36.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.36.0/StreamChat-All.zip","4.37.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.37.0/StreamChat-All.zip","4.37.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.37.1/StreamChat-All.zip","4.38.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.38.0/StreamChat-All.zip","4.39.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.39.0/StreamChat-All.zip","4.40.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.40.0/StreamChat-All.zip","4.41.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.41.0/StreamChat-All.zip","4.42.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.42.0/StreamChat-All.zip","4.43.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.43.0/StreamChat-All.zip","4.44.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.44.0/StreamChat-All.zip","4.45.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.45.0/StreamChat-All.zip"} \ No newline at end of file +{"4.7.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.7.0/StreamChat-All.zip","4.8.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.8.0/StreamChat-All.zip","4.9.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.9.0/StreamChat-All.zip","4.10.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.10.0/StreamChat-All.zip","4.10.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.10.1/StreamChat-All.zip","4.11.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.11.0/StreamChat-All.zip","4.12.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.12.0/StreamChat-All.zip","4.13.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.13.0/StreamChat-All.zip","4.13.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.13.1/StreamChat-All.zip","4.14.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.14.0/StreamChat-All.zip","4.15.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.15.0/StreamChat-All.zip","4.15.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.15.1/StreamChat-All.zip","4.16.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.16.0/StreamChat-All.zip","4.17.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.17.0/StreamChat-All.zip","4.18.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.18.0/StreamChat-All.zip","4.19.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.19.0/StreamChat-All.zip","4.20.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.20.0/StreamChat-All.zip","4.21.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.21.0/StreamChat-All.zip","4.21.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.21.1/StreamChat-All.zip","4.21.2":"https://github.com/GetStream/stream-chat-swift/releases/download/4.21.2/StreamChat-All.zip","4.22.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.22.0/StreamChat-All.zip","4.23.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.23.0/StreamChat-All.zip","4.24.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.24.0/StreamChat-All.zip","4.24.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.24.1/StreamChat-All.zip","4.25.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.25.0/StreamChat-All.zip","4.25.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.25.1/StreamChat-All.zip","4.26.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.26.0/StreamChat-All.zip","4.27.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.27.0/StreamChat-All.zip","4.27.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.27.1/StreamChat-All.zip","4.28.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.28.0/StreamChat-All.zip","4.29.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.29.0/StreamChat-All.zip","4.30.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.30.0/StreamChat-All.zip","4.31.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.31.0/StreamChat-All.zip","4.32.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.32.0/StreamChat-All.zip","4.33.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.33.0/StreamChat-All.zip","4.34.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.34.0/StreamChat-All.zip","4.35.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.35.0/StreamChat-All.zip","4.35.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.35.1/StreamChat-All.zip","4.35.2":"https://github.com/GetStream/stream-chat-swift/releases/download/4.35.2/StreamChat-All.zip","4.36.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.36.0/StreamChat-All.zip","4.37.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.37.0/StreamChat-All.zip","4.37.1":"https://github.com/GetStream/stream-chat-swift/releases/download/4.37.1/StreamChat-All.zip","4.38.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.38.0/StreamChat-All.zip","4.39.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.39.0/StreamChat-All.zip","4.40.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.40.0/StreamChat-All.zip","4.41.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.41.0/StreamChat-All.zip","4.42.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.42.0/StreamChat-All.zip","4.43.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.43.0/StreamChat-All.zip","4.44.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.44.0/StreamChat-All.zip","4.45.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.45.0/StreamChat-All.zip","4.46.0":"https://github.com/GetStream/stream-chat-swift/releases/download/4.46.0/StreamChat-All.zip"} \ No newline at end of file diff --git a/StreamChatUI-XCFramework.podspec b/StreamChatUI-XCFramework.podspec index 2869957b0e8..32498b88314 100644 --- a/StreamChatUI-XCFramework.podspec +++ b/StreamChatUI-XCFramework.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "StreamChatUI-XCFramework" - spec.version = "4.45.0" + spec.version = "4.46.0" spec.summary = "StreamChat UI Components" spec.description = "StreamChatUI SDK offers flexible UI components able to display data provided by StreamChat SDK." diff --git a/StreamChatUI.podspec b/StreamChatUI.podspec index 0e57c591eb3..ffed19431dc 100644 --- a/StreamChatUI.podspec +++ b/StreamChatUI.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "StreamChatUI" - spec.version = "4.45.0" + spec.version = "4.46.0" spec.summary = "StreamChat UI Components" spec.description = "StreamChatUI SDK offers flexible UI components able to display data provided by StreamChat SDK." diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_add_member.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_add_member.json index 4d728ad0838..84b15dd5322 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_add_member.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_add_member.json @@ -1,6 +1,6 @@ { "channel": { - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", "config": { "automod": "AI", "automod_behavior": "block", @@ -44,11 +44,11 @@ "replies": true, "search": true, "typing_events": true, - "updated_at": "2023-10-11T15:32:43.916111Z", + "updated_at": "2023-11-20T14:10:02.094893Z", "uploads": true, "url_enrichment": true }, - "created_at": "2023-11-15T00:27:44.714175Z", + "created_at": "2023-12-15T00:12:30.38985Z", "created_by": { "banned": false, "birthland": "Tatooine", @@ -58,17 +58,17 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "disabled": false, "frozen": false, "hidden": false, - "id": "cb3c193c-834d-11ee-bdff-067c57e250f4", - "last_message_at": "2023-11-15T00:27:46.027246Z", + "id": "a2debd8e-9ade-11ee-b565-5690af276325", + "last_message_at": "2023-12-15T00:12:31.087657Z", "member_count": 4, "name": "Sync Mock Server", "own_capabilities": [ @@ -91,6 +91,7 @@ "send-reaction", "send-reply", "send-typing-events", + "skip-slow-mode", "typing-events", "update-channel", "update-channel-members", @@ -98,19 +99,19 @@ "upload-file" ], "type": "messaging", - "updated_at": "2023-11-15T00:27:44.714175Z" + "updated_at": "2023-12-15T00:12:30.38985Z" }, - "duration": "27.24ms", + "duration": "23.18ms", "members": [ { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Serenno", @@ -120,7 +121,7 @@ "id": "count_dooku", "image": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", - "last_active": "2023-11-14T10:24:34.838296393Z", + "last_active": "2023-12-07T18:29:19.766312Z", "name": "Count Dooku", "online": false, "role": "user", @@ -131,12 +132,12 @@ { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Corellia", @@ -146,23 +147,23 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "language": "en", - "last_active": "2023-11-14T22:39:17.086929689Z", + "last_active": "2023-12-14T20:51:08.102314Z", "name": "Han Solo", "online": false, "role": "user", - "updated_at": "2023-11-03T12:36:17.463836Z" + "updated_at": "2023-11-22T08:53:16.760416Z" }, "user_id": "han_solo" }, { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "owner", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Tatooine", @@ -172,23 +173,23 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "user_id": "luke_skywalker" }, { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:46.413012Z", + "created_at": "2023-12-15T00:12:31.356415Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:46.413012Z", + "updated_at": "2023-12-15T00:12:31.356415Z", "user": { "banned": false, "birthland": "Polis Massa", @@ -196,11 +197,11 @@ "id": "leia_organa", "image": "https://vignette.wikia.nocookie.net/starwars/images/f/fc/Leia_Organa_TLJ.png", "language": "en", - "last_active": "2023-11-14T20:30:35.584675304Z", + "last_active": "2023-12-14T22:20:46.128246934Z", "name": "Leia Organa", "online": false, "role": "user", - "updated_at": "2023-11-03T12:35:39.044743Z" + "updated_at": "2023-11-22T17:26:05.164436Z" }, "user_id": "leia_organa" } diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_attachment.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_attachment.json index f91eedd0ddd..c42d6f58b9c 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_attachment.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_attachment.json @@ -1,4 +1,4 @@ { - "duration": "171.26ms", - "file": "https://us-east.stream-io-cdn.com/102399/images/5835083a-dcb0-4cc2-a15c-74c0e41179b6.yoda.jpg?Key-Pair-Id=APKAIHG36VEWPDULE23Q&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly91cy1lYXN0LnN0cmVhbS1pby1jZG4uY29tLzEwMjM5OS9pbWFnZXMvNTgzNTA4M2EtZGNiMC00Y2MyLWExNWMtNzRjMGU0MTE3OWI2LnlvZGEuanBnPypvaD01MTUqb3c9NTE1KiIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTcwMTIxNzY2Nn19fV19&Signature=cEM2794Ikip4i9IDw07w16KKyakxlbmmpEjfx4QJzMEr6d0BIJjDE1GU1UXrUmUzYkaZSaAs0yMLJ4hu1V7EcEXAPcZyWyCIn2ocR~C7NjvrwoCJjaz8U85zJgH4lJrOkdzEHTrv9Y3AoCz2TUT1j-DzewU9dAjpVKMKqkOn9lRIKVWe6C~fjP5eMLPR7UsnEpA6LzF0m6VtBRiWxeQTvbjuP1tewvYmYdkok2CH42S6ffIj1RwHYUq3x5M1It-8z96qxSgdV08YulfsbQXmkYztyNPs7HBR~xQohJRxYDh5HFLJNuM0GuAbp2LSpneZ81p959hvgdmwHA7aCFx9ww__&oh=515&ow=515" + "duration": "148.69ms", + "file": "https://us-east.stream-io-cdn.com/102399/images/27622c33-685e-416e-ae14-4261be89f209.yoda.jpg?Key-Pair-Id=APKAIHG36VEWPDULE23Q&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly91cy1lYXN0LnN0cmVhbS1pby1jZG4uY29tLzEwMjM5OS9pbWFnZXMvMjc2MjJjMzMtNjg1ZS00MTZlLWFlMTQtNDI2MWJlODlmMjA5LnlvZGEuanBnPypvaD01MTUqb3c9NTE1KiIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTcwMzgwODc1MX19fV19&Signature=iXzVIugka3vHKuw1X3DS-7Xx3kh1o-Ak8ss8fHH38cTbpfGz4ezeEOkyLHaZjLuOzgBYDuu6k1WR1NlO7IepnIPMBMnlll-bLCy27wXWAFDAmUbF-MSs5lIpkeCJgjsWPr~PAN5LeCtofJj8JzKSmytMGmVJYh9ZZo6pPnf57sPpptMfqECj2I30QPKsJ8-A1amURy0f0~YIfX7QjRobf6dSXF5Vf5f7fDSTO9Q-JGopHRMh5TeU03xG3yD~e91MlD0scahz1~2Q1bPDPTNRIHERjYQik7cJ2AicgTWywxv7fAf98ruiR4sRM3MmUiQCU7D2Yk1CancNZ~9L5CMhlQ__&oh=515&ow=515" } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_channel_creation.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_channel_creation.json index ca20afab59a..9774a483a45 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_channel_creation.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_channel_creation.json @@ -1,6 +1,6 @@ { "channel": { - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", "config": { "automod": "AI", "automod_behavior": "block", @@ -44,11 +44,11 @@ "replies": true, "search": true, "typing_events": true, - "updated_at": "2023-10-11T15:32:43.916111Z", + "updated_at": "2023-11-20T14:10:02.094893Z", "uploads": true, "url_enrichment": true }, - "created_at": "2023-11-15T00:27:44.714175Z", + "created_at": "2023-12-15T00:12:30.38985Z", "created_by": { "banned": false, "birthland": "Tatooine", @@ -58,16 +58,16 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "disabled": false, "frozen": false, "hidden": false, - "id": "cb3c193c-834d-11ee-bdff-067c57e250f4", + "id": "a2debd8e-9ade-11ee-b565-5690af276325", "member_count": 3, "name": "Sync Mock Server", "own_capabilities": [ @@ -90,6 +90,7 @@ "send-reaction", "send-reply", "send-typing-events", + "skip-slow-mode", "typing-events", "update-channel", "update-channel-members", @@ -97,19 +98,19 @@ "upload-file" ], "type": "messaging", - "updated_at": "2023-11-15T00:27:44.714175Z" + "updated_at": "2023-12-15T00:12:30.38985Z" }, - "duration": "217.08ms", + "duration": "92.12ms", "members": [ { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Serenno", @@ -119,7 +120,7 @@ "id": "count_dooku", "image": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", - "last_active": "2023-11-14T10:24:34.838296393Z", + "last_active": "2023-12-07T18:29:19.766312Z", "name": "Count Dooku", "online": false, "role": "user", @@ -130,12 +131,12 @@ { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Corellia", @@ -145,23 +146,23 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "language": "en", - "last_active": "2023-11-14T22:39:17.086929689Z", + "last_active": "2023-12-14T20:51:08.102314Z", "name": "Han Solo", "online": false, "role": "user", - "updated_at": "2023-11-03T12:36:17.463836Z" + "updated_at": "2023-11-22T08:53:16.760416Z" }, "user_id": "han_solo" }, { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "owner", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Tatooine", @@ -171,11 +172,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "user_id": "luke_skywalker" } @@ -183,12 +184,12 @@ "membership": { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "owner", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Tatooine", @@ -198,18 +199,18 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } }, "messages": [], "pinned_messages": [], "read": [ { - "last_read": "2023-11-15T00:27:44.764357709Z", + "last_read": "2023-12-15T00:12:30.434248893Z", "unread_messages": 0, "user": { "banned": false, @@ -220,7 +221,7 @@ "id": "count_dooku", "image": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", - "last_active": "2023-11-14T10:24:34.838296393Z", + "last_active": "2023-12-07T18:29:19.766312Z", "name": "Count Dooku", "online": false, "role": "user", @@ -228,7 +229,7 @@ } }, { - "last_read": "2023-11-15T00:27:44.764357709Z", + "last_read": "2023-12-15T00:12:30.434248893Z", "unread_messages": 0, "user": { "banned": false, @@ -239,15 +240,15 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "language": "en", - "last_active": "2023-11-14T22:39:17.086929689Z", + "last_active": "2023-12-14T20:51:08.102314Z", "name": "Han Solo", "online": false, "role": "user", - "updated_at": "2023-11-03T12:36:17.463836Z" + "updated_at": "2023-11-22T08:53:16.760416Z" } }, { - "last_read": "2023-11-15T00:27:44.764357709Z", + "last_read": "2023-12-15T00:12:30.434248893Z", "unread_messages": 0, "user": { "banned": false, @@ -258,11 +259,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } } ], diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_channel_removal.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_channel_removal.json index 60f7f96dc05..693fc4e0175 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_channel_removal.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_channel_removal.json @@ -1,6 +1,6 @@ { "channel": { - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", "config": { "automod": "AI", "automod_behavior": "block", @@ -44,26 +44,26 @@ "replies": true, "search": true, "typing_events": true, - "updated_at": "2023-10-11T15:32:43.916111Z", + "updated_at": "2023-11-20T14:10:02.094893Z", "uploads": true, "url_enrichment": true }, - "created_at": "2023-11-15T00:27:44.714175Z", + "created_at": "2023-12-15T00:12:30.38985Z", "created_by": null, - "deleted_at": "2023-11-15T00:27:48.435811Z", + "deleted_at": "2023-12-15T00:12:33.478572Z", "disabled": false, "frozen": false, - "id": "cb3c193c-834d-11ee-bdff-067c57e250f4", + "id": "a2debd8e-9ade-11ee-b565-5690af276325", "members": [ { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Serenno", @@ -73,7 +73,7 @@ "id": "count_dooku", "image": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", - "last_active": "2023-11-14T10:24:34.838296393Z", + "last_active": "2023-12-07T18:29:19.766312Z", "name": "Count Dooku", "online": false, "role": "user", @@ -84,12 +84,12 @@ { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Corellia", @@ -99,23 +99,23 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "language": "en", - "last_active": "2023-11-14T22:39:17.086929689Z", + "last_active": "2023-12-14T20:51:08.102314Z", "name": "Han Solo", "online": false, "role": "user", - "updated_at": "2023-11-03T12:36:17.463836Z" + "updated_at": "2023-11-22T08:53:16.760416Z" }, "user_id": "han_solo" }, { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "owner", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Tatooine", @@ -125,23 +125,23 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "user_id": "luke_skywalker" }, { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:46.413012Z", + "created_at": "2023-12-15T00:12:31.356415Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:46.413012Z", + "updated_at": "2023-12-15T00:12:31.356415Z", "user": { "banned": false, "birthland": "Polis Massa", @@ -149,16 +149,16 @@ "id": "leia_organa", "image": "https://vignette.wikia.nocookie.net/starwars/images/f/fc/Leia_Organa_TLJ.png", "language": "en", - "last_active": "2023-11-14T20:30:35.584675304Z", + "last_active": "2023-12-14T22:20:46.128246934Z", "name": "Leia Organa", "online": false, "role": "user", - "updated_at": "2023-11-03T12:35:39.044743Z" + "updated_at": "2023-11-22T17:26:05.164436Z" }, "user_id": "leia_organa" } ], - "truncated_at": "2023-11-15T00:27:48.435811Z", + "truncated_at": "2023-12-15T00:12:33.478572Z", "truncated_by": { "banned": false, "birthland": "Tatooine", @@ -168,14 +168,14 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "type": "messaging", - "updated_at": "2023-11-15T00:27:48.217004Z" + "updated_at": "2023-12-15T00:12:33.304775Z" }, - "duration": "26.31ms" + "duration": "22.94ms" } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_channels.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_channels.json index 320bb4e40a0..5d2df4386d4 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_channels.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_channels.json @@ -2,7 +2,7 @@ "channels": [ { "channel": { - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", "config": { "automod": "AI", "automod_behavior": "block", @@ -46,11 +46,11 @@ "replies": true, "search": true, "typing_events": true, - "updated_at": "2023-10-11T15:32:43.916111Z", + "updated_at": "2023-11-20T14:10:02.094893Z", "uploads": true, "url_enrichment": true }, - "created_at": "2023-11-15T00:27:44.714175Z", + "created_at": "2023-12-15T00:12:30.38985Z", "created_by": { "banned": false, "birthland": "Tatooine", @@ -60,16 +60,16 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "disabled": false, "frozen": false, "hidden": false, - "id": "cb3c193c-834d-11ee-bdff-067c57e250f4", + "id": "a2debd8e-9ade-11ee-b565-5690af276325", "member_count": 3, "name": "Sync Mock Server", "own_capabilities": [ @@ -92,6 +92,7 @@ "send-reaction", "send-reply", "send-typing-events", + "skip-slow-mode", "typing-events", "update-channel", "update-channel-members", @@ -99,18 +100,18 @@ "upload-file" ], "type": "messaging", - "updated_at": "2023-11-15T00:27:44.714175Z" + "updated_at": "2023-12-15T00:12:30.38985Z" }, "members": [ { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Serenno", @@ -120,7 +121,7 @@ "id": "count_dooku", "image": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", - "last_active": "2023-11-14T10:24:34.838296393Z", + "last_active": "2023-12-07T18:29:19.766312Z", "name": "Count Dooku", "online": false, "role": "user", @@ -131,12 +132,12 @@ { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Corellia", @@ -146,23 +147,23 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "language": "en", - "last_active": "2023-11-14T22:39:17.086929689Z", + "last_active": "2023-12-14T20:51:08.102314Z", "name": "Han Solo", "online": false, "role": "user", - "updated_at": "2023-11-03T12:36:17.463836Z" + "updated_at": "2023-11-22T08:53:16.760416Z" }, "user_id": "han_solo" }, { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "owner", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Tatooine", @@ -172,11 +173,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "user_id": "luke_skywalker" } @@ -184,11 +185,11 @@ "membership": { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Tatooine", @@ -198,18 +199,18 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } }, "messages": [], "pinned_messages": [], "read": [ { - "last_read": "2023-11-15T00:27:44.764357709Z", + "last_read": "2023-12-15T00:12:30.434248893Z", "unread_messages": 0, "user": { "banned": false, @@ -220,7 +221,7 @@ "id": "count_dooku", "image": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", - "last_active": "2023-11-14T10:24:34.838296393Z", + "last_active": "2023-12-07T18:29:19.766312Z", "name": "Count Dooku", "online": false, "role": "user", @@ -228,7 +229,7 @@ } }, { - "last_read": "2023-11-15T00:27:44.764357709Z", + "last_read": "2023-12-15T00:12:30.434248893Z", "unread_messages": 0, "user": { "banned": false, @@ -239,15 +240,15 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "language": "en", - "last_active": "2023-11-14T22:39:17.086929689Z", + "last_active": "2023-12-14T20:51:08.102314Z", "name": "Han Solo", "online": false, "role": "user", - "updated_at": "2023-11-03T12:36:17.463836Z" + "updated_at": "2023-11-22T08:53:16.760416Z" } }, { - "last_read": "2023-11-15T00:27:44.764357709Z", + "last_read": "2023-12-15T00:12:30.434248893Z", "unread_messages": 0, "user": { "banned": false, @@ -258,16 +259,16 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } } ], "watcher_count": 1 } ], - "duration": "358.15ms" + "duration": "96.71ms" } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_events.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_events.json index ad7d032d89d..04d8de33089 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_events.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_events.json @@ -1,10 +1,10 @@ { - "duration": "4.88ms", + "duration": "4.38ms", "event": { - "channel_id": "cb3c193c-834d-11ee-bdff-067c57e250f4", + "channel_id": "a2debd8e-9ade-11ee-b565-5690af276325", "channel_type": "messaging", - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", - "created_at": "2023-11-15T00:27:45.745309061Z", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", + "created_at": "2023-12-15T00:12:30.837441782Z", "type": "typing.start", "user": { "banned": false, @@ -15,11 +15,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } } } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_message.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_message.json index 6c694cd26c8..cdba41cdcb4 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_message.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_message.json @@ -1,13 +1,13 @@ { - "duration": "173.64ms", + "duration": "187.43ms", "message": { "attachments": [], "before_message_send_failed": true, - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", - "created_at": "2023-11-15T00:27:46.027246Z", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", + "created_at": "2023-12-15T00:12:31.087657Z", "deleted_reply_count": 0, "html": "

Test

\n", - "id": "cc30e296-834d-11ee-bdff-067c57e250f4", + "id": "a3471802-9ade-11ee-b565-5690af276325", "latest_reactions": [], "mentioned_users": [], "own_reactions": [], @@ -22,7 +22,7 @@ "silent": false, "text": "Test", "type": "regular", - "updated_at": "2023-11-15T00:27:46.027246Z", + "updated_at": "2023-12-15T00:12:31.087657Z", "user": { "banned": false, "birthland": "Tatooine", @@ -32,11 +32,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } } } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_message_ephemeral.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_message_ephemeral.json index b696db9f36d..398cca61f3b 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_message_ephemeral.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_message_ephemeral.json @@ -1,5 +1,5 @@ { - "duration": "124.61ms", + "duration": "119.11ms", "message": { "args": "Test", "attachments": [ @@ -31,68 +31,68 @@ "fixed_height": { "frames": "", "height": "200", - "size": "1256848", - "url": "https://media3.giphy.com/media/6mZX5zzQbeJq/200.gif?cid=c4b036750v23glexptnxsl12ff2rsv4sq6c4t5k2cyxkdcoi&ep=v1_gifs_search&rid=200.gif&ct=g", - "width": "150" + "size": "845116", + "url": "https://media1.giphy.com/media/YRhJCaRX2fZW2ulZzq/200.gif?cid=c4b03675zb3zq5fqpmpgb9t9xteoxf7zgvhrfbosp7nuid01&ep=v1_gifs_search&rid=200.gif&ct=g", + "width": "267" }, "fixed_height_downsampled": { "frames": "", "height": "200", - "size": "114061", - "url": "https://media3.giphy.com/media/6mZX5zzQbeJq/200_d.gif?cid=c4b036750v23glexptnxsl12ff2rsv4sq6c4t5k2cyxkdcoi&ep=v1_gifs_search&rid=200_d.gif&ct=g", - "width": "150" + "size": "93753", + "url": "https://media1.giphy.com/media/YRhJCaRX2fZW2ulZzq/200_d.gif?cid=c4b03675zb3zq5fqpmpgb9t9xteoxf7zgvhrfbosp7nuid01&ep=v1_gifs_search&rid=200_d.gif&ct=g", + "width": "267" }, "fixed_height_still": { "frames": "", "height": "200", - "size": "11585", - "url": "https://media3.giphy.com/media/6mZX5zzQbeJq/200_s.gif?cid=c4b036750v23glexptnxsl12ff2rsv4sq6c4t5k2cyxkdcoi&ep=v1_gifs_search&rid=200_s.gif&ct=g", - "width": "150" + "size": "15823", + "url": "https://media1.giphy.com/media/YRhJCaRX2fZW2ulZzq/200_s.gif?cid=c4b03675zb3zq5fqpmpgb9t9xteoxf7zgvhrfbosp7nuid01&ep=v1_gifs_search&rid=200_s.gif&ct=g", + "width": "267" }, "fixed_width": { "frames": "", - "height": "267", - "size": "1327577", - "url": "https://media3.giphy.com/media/6mZX5zzQbeJq/200w.gif?cid=c4b036750v23glexptnxsl12ff2rsv4sq6c4t5k2cyxkdcoi&ep=v1_gifs_search&rid=200w.gif&ct=g", + "height": "150", + "size": "451519", + "url": "https://media1.giphy.com/media/YRhJCaRX2fZW2ulZzq/200w.gif?cid=c4b03675zb3zq5fqpmpgb9t9xteoxf7zgvhrfbosp7nuid01&ep=v1_gifs_search&rid=200w.gif&ct=g", "width": "200" }, "fixed_width_downsampled": { "frames": "", - "height": "267", - "size": "122410", - "url": "https://media3.giphy.com/media/6mZX5zzQbeJq/200w_d.gif?cid=c4b036750v23glexptnxsl12ff2rsv4sq6c4t5k2cyxkdcoi&ep=v1_gifs_search&rid=200w_d.gif&ct=g", + "height": "150", + "size": "56164", + "url": "https://media1.giphy.com/media/YRhJCaRX2fZW2ulZzq/200w_d.gif?cid=c4b03675zb3zq5fqpmpgb9t9xteoxf7zgvhrfbosp7nuid01&ep=v1_gifs_search&rid=200w_d.gif&ct=g", "width": "200" }, "fixed_width_still": { "frames": "", - "height": "267", - "size": "17811", - "url": "https://media3.giphy.com/media/6mZX5zzQbeJq/200w_s.gif?cid=c4b036750v23glexptnxsl12ff2rsv4sq6c4t5k2cyxkdcoi&ep=v1_gifs_search&rid=200w_s.gif&ct=g", + "height": "150", + "size": "13751", + "url": "https://media1.giphy.com/media/YRhJCaRX2fZW2ulZzq/200w_s.gif?cid=c4b03675zb3zq5fqpmpgb9t9xteoxf7zgvhrfbosp7nuid01&ep=v1_gifs_search&rid=200w_s.gif&ct=g", "width": "200" }, "original": { - "frames": "74", - "height": "320", - "size": "2013057", - "url": "https://media3.giphy.com/media/6mZX5zzQbeJq/giphy.gif?cid=c4b036750v23glexptnxsl12ff2rsv4sq6c4t5k2cyxkdcoi&ep=v1_gifs_search&rid=giphy.gif&ct=g", - "width": "240" + "frames": "75", + "height": "360", + "size": "2523659", + "url": "https://media1.giphy.com/media/YRhJCaRX2fZW2ulZzq/giphy.gif?cid=c4b03675zb3zq5fqpmpgb9t9xteoxf7zgvhrfbosp7nuid01&ep=v1_gifs_search&rid=giphy.gif&ct=g", + "width": "480" } }, - "thumb_url": "https://media3.giphy.com/media/6mZX5zzQbeJq/giphy.gif?cid=c4b036750v23glexptnxsl12ff2rsv4sq6c4t5k2cyxkdcoi&ep=v1_gifs_search&rid=giphy.gif&ct=g", + "thumb_url": "https://media1.giphy.com/media/YRhJCaRX2fZW2ulZzq/giphy.gif?cid=c4b03675zb3zq5fqpmpgb9t9xteoxf7zgvhrfbosp7nuid01&ep=v1_gifs_search&rid=giphy.gif&ct=g", "title": "Test", - "title_link": "https://giphy.com/gifs/water-test-slide-6mZX5zzQbeJq", + "title_link": "https://giphy.com/gifs/justin-test-YRhJCaRX2fZW2ulZzq", "type": "giphy" } ], - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", "command": "giphy", "command_info": { "name": "Giphy" }, - "created_at": "2023-11-15T00:27:47.040258Z", + "created_at": "2023-12-15T00:12:31.848889Z", "deleted_reply_count": 0, "html": "

/giphy Test

\n", - "id": "ccccc1ca-834d-11ee-bdff-067c57e250f4", + "id": "a3c1bbca-9ade-11ee-b565-5690af276325", "latest_reactions": [], "mentioned_users": [], "own_reactions": [], @@ -107,7 +107,7 @@ "silent": false, "text": "/giphy Test", "type": "ephemeral", - "updated_at": "2023-11-15T00:27:47.040258Z", + "updated_at": "2023-12-15T00:12:31.848889Z", "user": { "banned": false, "birthland": "Tatooine", @@ -117,11 +117,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } } } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_reaction.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_reaction.json index 3db6abb4727..aab76935292 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_reaction.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_reaction.json @@ -1,19 +1,19 @@ { - "duration": "21.96ms", + "duration": "24.38ms", "message": { "attachments": [], - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", - "created_at": "2023-11-15T00:27:46.027246Z", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", + "created_at": "2023-12-15T00:12:31.087657Z", "deleted_reply_count": 0, "html": "

Test

\n", - "id": "cc30e296-834d-11ee-bdff-067c57e250f4", + "id": "a3471802-9ade-11ee-b565-5690af276325", "latest_reactions": [ { - "created_at": "2023-11-15T00:27:46.235917Z", - "message_id": "cc30e296-834d-11ee-bdff-067c57e250f4", + "created_at": "2023-12-15T00:12:31.234279Z", + "message_id": "a3471802-9ade-11ee-b565-5690af276325", "score": 1, "type": "like", - "updated_at": "2023-11-15T00:27:46.235917Z", + "updated_at": "2023-12-15T00:12:31.234279Z", "user": { "banned": false, "birthland": "Tatooine", @@ -23,11 +23,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "user_id": "luke_skywalker" } @@ -35,11 +35,11 @@ "mentioned_users": [], "own_reactions": [ { - "created_at": "2023-11-15T00:27:46.235917Z", - "message_id": "cc30e296-834d-11ee-bdff-067c57e250f4", + "created_at": "2023-12-15T00:12:31.234279Z", + "message_id": "a3471802-9ade-11ee-b565-5690af276325", "score": 1, "type": "like", - "updated_at": "2023-11-15T00:27:46.235917Z", + "updated_at": "2023-12-15T00:12:31.234279Z", "user": { "banned": false, "birthland": "Tatooine", @@ -49,11 +49,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "user_id": "luke_skywalker" } @@ -73,7 +73,7 @@ "silent": false, "text": "Test", "type": "regular", - "updated_at": "2023-11-15T00:27:46.242610888Z", + "updated_at": "2023-12-15T00:12:31.242136166Z", "user": { "banned": false, "birthland": "Tatooine", @@ -83,19 +83,19 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } }, "reaction": { - "created_at": "2023-11-15T00:27:46.235917Z", - "message_id": "cc30e296-834d-11ee-bdff-067c57e250f4", + "created_at": "2023-12-15T00:12:31.234279Z", + "message_id": "a3471802-9ade-11ee-b565-5690af276325", "score": 1, "type": "like", - "updated_at": "2023-11-15T00:27:46.235917Z", + "updated_at": "2023-12-15T00:12:31.234279Z", "user": { "banned": false, "birthland": "Tatooine", @@ -105,11 +105,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "user_id": "luke_skywalker" } diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_truncate.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_truncate.json index c4b046bcd09..3b9def3941f 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_truncate.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_truncate.json @@ -1,6 +1,6 @@ { "channel": { - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", "config": { "automod": "AI", "automod_behavior": "block", @@ -44,11 +44,11 @@ "replies": true, "search": true, "typing_events": true, - "updated_at": "2023-10-11T15:32:43.916111Z", + "updated_at": "2023-11-20T14:10:02.094893Z", "uploads": true, "url_enrichment": true }, - "created_at": "2023-11-15T00:27:44.714175Z", + "created_at": "2023-12-15T00:12:30.38985Z", "created_by": { "banned": false, "birthland": "Tatooine", @@ -58,27 +58,27 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "disabled": false, "frozen": false, - "id": "cb3c193c-834d-11ee-bdff-067c57e250f4", + "id": "a2debd8e-9ade-11ee-b565-5690af276325", "last_message_at": "0001-01-01T00:00:00Z", "member_count": 4, "members": [ { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Serenno", @@ -88,7 +88,7 @@ "id": "count_dooku", "image": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", - "last_active": "2023-11-14T10:24:34.838296393Z", + "last_active": "2023-12-07T18:29:19.766312Z", "name": "Count Dooku", "online": false, "role": "user", @@ -99,12 +99,12 @@ { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Corellia", @@ -114,23 +114,23 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "language": "en", - "last_active": "2023-11-14T22:39:17.086929689Z", + "last_active": "2023-12-14T20:51:08.102314Z", "name": "Han Solo", "online": false, "role": "user", - "updated_at": "2023-11-03T12:36:17.463836Z" + "updated_at": "2023-11-22T08:53:16.760416Z" }, "user_id": "han_solo" }, { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "owner", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Tatooine", @@ -140,23 +140,23 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "user_id": "luke_skywalker" }, { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:46.413012Z", + "created_at": "2023-12-15T00:12:31.356415Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:46.413012Z", + "updated_at": "2023-12-15T00:12:31.356415Z", "user": { "banned": false, "birthland": "Polis Massa", @@ -164,17 +164,17 @@ "id": "leia_organa", "image": "https://vignette.wikia.nocookie.net/starwars/images/f/fc/Leia_Organa_TLJ.png", "language": "en", - "last_active": "2023-11-14T20:30:35.584675304Z", + "last_active": "2023-12-14T22:20:46.128246934Z", "name": "Leia Organa", "online": false, "role": "user", - "updated_at": "2023-11-03T12:35:39.044743Z" + "updated_at": "2023-11-22T17:26:05.164436Z" }, "user_id": "leia_organa" } ], "name": "Sync Mock Server", - "truncated_at": "2023-11-15T00:27:48.211098Z", + "truncated_at": "2023-12-15T00:12:33.300413Z", "truncated_by": { "banned": false, "birthland": "Tatooine", @@ -184,23 +184,23 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "type": "messaging", - "updated_at": "2023-11-15T00:27:48.217004Z" + "updated_at": "2023-12-15T00:12:33.304775Z" }, - "duration": "66.59ms", + "duration": "77.39ms", "message": { "attachments": [], - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", - "created_at": "2023-11-15T00:27:48.211099Z", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", + "created_at": "2023-12-15T00:12:33.300414Z", "deleted_reply_count": 0, "html": "

Channel truncated

\n", - "id": "cd90d06a-834d-11ee-bdff-067c57e250f4", + "id": "a4af0bdc-9ade-11ee-b565-5690af276325", "latest_reactions": [], "mentioned_users": [], "own_reactions": [], @@ -215,7 +215,7 @@ "silent": false, "text": "Channel truncated", "type": "system", - "updated_at": "2023-11-15T00:27:48.211099Z", + "updated_at": "2023-12-15T00:12:33.300414Z", "user": { "banned": false, "birthland": "Tatooine", @@ -225,11 +225,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } } } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_unsplash_link.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_unsplash_link.json index f3225ce2176..5944c0ee3b2 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_unsplash_link.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_unsplash_link.json @@ -1,23 +1,23 @@ { - "duration": "499.01ms", + "duration": "988.26ms", "message": { "attachments": [ { - "image_url": "https://images.unsplash.com/photo-1568574728383-06fca083883d?blend=000000&blend-alpha=10&blend-mode=normal&blend-w=1&crop=faces%2Cedges&h=630&mark=https%3A%2F%2Fimages.unsplash.com%2Fopengraph%2Flogo.png&mark-align=top%2Cleft&mark-pad=50&mark-w=64&w=1200&auto=format&fit=crop&q=60&ixid=M3wxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNzAwMDA4MDY3fA&ixlib=rb-4.0.3", + "image_url": "https://images.unsplash.com/photo-1568574728383-06fca083883d?blend=000000&blend-alpha=10&blend-mode=normal&blend-w=1&crop=faces%2Cedges&h=630&mark=https%3A%2F%2Fimages.unsplash.com%2Fopengraph%2Flogo.png&mark-align=top%2Cleft&mark-pad=50&mark-w=64&w=1200&auto=format&fit=crop&q=60&ixid=M3wxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNzAyNTk5MTUyfA&ixlib=rb-4.0.3", "og_scrape_url": "https://unsplash.com/photos/1_2d3MRbI9c", "text": "Download this photo by Joao Branco on Unsplash", - "thumb_url": "https://images.unsplash.com/photo-1568574728383-06fca083883d?blend=000000&blend-alpha=10&blend-mode=normal&blend-w=1&crop=faces%2Cedges&h=630&mark=https%3A%2F%2Fimages.unsplash.com%2Fopengraph%2Flogo.png&mark-align=top%2Cleft&mark-pad=50&mark-w=64&w=1200&auto=format&fit=crop&q=60&ixid=M3wxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNzAwMDA4MDY3fA&ixlib=rb-4.0.3", + "thumb_url": "https://images.unsplash.com/photo-1568574728383-06fca083883d?blend=000000&blend-alpha=10&blend-mode=normal&blend-w=1&crop=faces%2Cedges&h=630&mark=https%3A%2F%2Fimages.unsplash.com%2Fopengraph%2Flogo.png&mark-align=top%2Cleft&mark-pad=50&mark-w=64&w=1200&auto=format&fit=crop&q=60&ixid=M3wxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNzAyNTk5MTUyfA&ixlib=rb-4.0.3", "title": "Photo by Joao Branco on Unsplash", "title_link": "https://unsplash.com/photos/green-pine-tree-mountain-slope-scenery-1_2d3MRbI9c", "type": "image" } ], "before_message_send_failed": true, - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", - "created_at": "2023-11-15T00:27:48.022325Z", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", + "created_at": "2023-12-15T00:12:33.164769Z", "deleted_reply_count": 0, "html": "

https://unsplash.com/photos/1_2d3MRbI9c

\n", - "id": "cd2c8380-834d-11ee-bdff-067c57e250f4", + "id": "a4092c9e-9ade-11ee-b565-5690af276325", "latest_reactions": [], "mentioned_users": [], "own_reactions": [], @@ -32,7 +32,7 @@ "silent": false, "text": "https://unsplash.com/photos/1_2d3MRbI9c", "type": "regular", - "updated_at": "2023-11-15T00:27:48.022325Z", + "updated_at": "2023-12-15T00:12:33.164769Z", "user": { "banned": false, "birthland": "Tatooine", @@ -42,11 +42,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } } } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_youtube_link.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_youtube_link.json index 4c0e810da69..2bb7c32cb00 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_youtube_link.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/http_youtube_link.json @@ -1,5 +1,5 @@ { - "duration": "181.86ms", + "duration": "149.24ms", "message": { "attachments": [ { @@ -15,11 +15,11 @@ } ], "before_message_send_failed": true, - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", - "created_at": "2023-11-15T00:27:47.361924Z", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", + "created_at": "2023-12-15T00:12:32.087968Z", "deleted_reply_count": 0, "html": "

https://youtube.com/watch?v=xOX7MsrbaPY

\n", - "id": "ccf810be-834d-11ee-bdff-067c57e250f4", + "id": "a3e31950-9ade-11ee-b565-5690af276325", "latest_reactions": [], "mentioned_users": [], "own_reactions": [], @@ -34,7 +34,7 @@ "silent": false, "text": "https://youtube.com/watch?v=xOX7MsrbaPY", "type": "regular", - "updated_at": "2023-11-15T00:27:47.361924Z", + "updated_at": "2023-12-15T00:12:32.087968Z", "user": { "banned": false, "birthland": "Tatooine", @@ -44,11 +44,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } } } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_events.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_events.json index 9aafca39d94..52298dad456 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_events.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_events.json @@ -1,8 +1,8 @@ { - "channel_id": "cb3c193c-834d-11ee-bdff-067c57e250f4", + "channel_id": "a2debd8e-9ade-11ee-b565-5690af276325", "channel_type": "messaging", - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", - "created_at": "2023-11-15T00:27:45.745309061Z", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", + "created_at": "2023-12-15T00:12:30.837441782Z", "type": "typing.start", "user": { "banned": false, @@ -13,10 +13,10 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_events_channel.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_events_channel.json index 147480b43a0..d3c6e2bae10 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_events_channel.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_events_channel.json @@ -1,6 +1,6 @@ { "channel": { - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", "config": { "automod": "AI", "automod_behavior": "block", @@ -44,11 +44,11 @@ "replies": true, "search": true, "typing_events": true, - "updated_at": "2023-10-11T15:32:43.916111Z", + "updated_at": "2023-11-20T14:10:02.094893Z", "uploads": true, "url_enrichment": true }, - "created_at": "2023-11-15T00:27:44.714175Z", + "created_at": "2023-12-15T00:12:30.38985Z", "created_by": { "banned": false, "birthland": "Tatooine", @@ -58,27 +58,27 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "disabled": false, "frozen": false, - "id": "cb3c193c-834d-11ee-bdff-067c57e250f4", - "last_message_at": "2023-11-15T00:27:46.027246Z", + "id": "a2debd8e-9ade-11ee-b565-5690af276325", + "last_message_at": "2023-12-15T00:12:31.087657Z", "member_count": 4, "members": [ { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Serenno", @@ -88,7 +88,7 @@ "id": "count_dooku", "image": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/b/b8/Dooku_Headshot.jpg", - "last_active": "2023-11-14T10:24:34.838296393Z", + "last_active": "2023-12-07T18:29:19.766312Z", "name": "Count Dooku", "online": false, "role": "user", @@ -99,12 +99,12 @@ { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Corellia", @@ -114,23 +114,23 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/e/e2/TFAHanSolo.png", "language": "en", - "last_active": "2023-11-14T22:39:17.086929689Z", + "last_active": "2023-12-14T20:51:08.102314Z", "name": "Han Solo", "online": false, "role": "user", - "updated_at": "2023-11-03T12:36:17.463836Z" + "updated_at": "2023-11-22T08:53:16.760416Z" }, "user_id": "han_solo" }, { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:44.728248Z", + "created_at": "2023-12-15T00:12:30.397536Z", "notifications_muted": false, "role": "owner", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:44.728248Z", + "updated_at": "2023-12-15T00:12:30.397536Z", "user": { "banned": false, "birthland": "Tatooine", @@ -140,23 +140,23 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "user_id": "luke_skywalker" }, { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:46.413012Z", + "created_at": "2023-12-15T00:12:31.356415Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:46.413012Z", + "updated_at": "2023-12-15T00:12:31.356415Z", "user": { "banned": false, "birthland": "Polis Massa", @@ -164,23 +164,23 @@ "id": "leia_organa", "image": "https://vignette.wikia.nocookie.net/starwars/images/f/fc/Leia_Organa_TLJ.png", "language": "en", - "last_active": "2023-11-14T20:30:35.584675304Z", + "last_active": "2023-12-14T22:20:46.128246934Z", "name": "Leia Organa", "online": false, "role": "user", - "updated_at": "2023-11-03T12:35:39.044743Z" + "updated_at": "2023-11-22T17:26:05.164436Z" }, "user_id": "leia_organa" } ], "name": "Sync Mock Server", "type": "messaging", - "updated_at": "2023-11-15T00:27:44.714175Z" + "updated_at": "2023-12-15T00:12:30.38985Z" }, - "channel_id": "cb3c193c-834d-11ee-bdff-067c57e250f4", + "channel_id": "a2debd8e-9ade-11ee-b565-5690af276325", "channel_type": "messaging", - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", - "created_at": "2023-11-15T00:27:46.432217012Z", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", + "created_at": "2023-12-15T00:12:31.373013792Z", "type": "channel.updated", "user": { "banned": false, @@ -191,10 +191,10 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_events_member.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_events_member.json index ce5443ea44e..3f087708298 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_events_member.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_events_member.json @@ -1,17 +1,17 @@ { - "channel_id": "cb3c193c-834d-11ee-bdff-067c57e250f4", + "channel_id": "a2debd8e-9ade-11ee-b565-5690af276325", "channel_type": "messaging", - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", - "created_at": "2023-11-15T00:27:46.423542938Z", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", + "created_at": "2023-12-15T00:12:31.366556688Z", "member": { "banned": false, "channel_role": "channel_member", - "created_at": "2023-11-15T00:27:46.413012Z", + "created_at": "2023-12-15T00:12:31.356415Z", "notifications_muted": false, "role": "member", "shadow_banned": false, "status": "member", - "updated_at": "2023-11-15T00:27:46.413012Z", + "updated_at": "2023-12-15T00:12:31.356415Z", "user": { "banned": false, "birthland": "Polis Massa", @@ -19,11 +19,11 @@ "id": "leia_organa", "image": "https://vignette.wikia.nocookie.net/starwars/images/f/fc/Leia_Organa_TLJ.png", "language": "en", - "last_active": "2023-11-14T20:30:35.584675304Z", + "last_active": "2023-12-14T22:20:46.128246934Z", "name": "Leia Organa", "online": false, "role": "user", - "updated_at": "2023-11-03T12:35:39.044743Z" + "updated_at": "2023-11-22T17:26:05.164436Z" }, "user_id": "leia_organa" }, @@ -35,10 +35,10 @@ "id": "leia_organa", "image": "https://vignette.wikia.nocookie.net/starwars/images/f/fc/Leia_Organa_TLJ.png", "language": "en", - "last_active": "2023-11-14T20:30:35.584675304Z", + "last_active": "2023-12-14T22:20:46.128246934Z", "name": "Leia Organa", "online": false, "role": "user", - "updated_at": "2023-11-03T12:35:39.044743Z" + "updated_at": "2023-11-22T17:26:05.164436Z" } } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_health_check.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_health_check.json index ceea7b314b4..480b310269a 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_health_check.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_health_check.json @@ -1,7 +1,7 @@ { "cid": "*", - "connection_id": "6553de46-0a05-4049-0000-00000006cbf7", - "created_at": "2023-11-15T00:27:43.909392714Z", + "connection_id": "65784d0c-0a05-41f4-0000-00000052f0ad", + "created_at": "2023-12-15T00:12:30.027121683Z", "me": { "banned": false, "birthland": "Tatooine", @@ -14,15 +14,15 @@ "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "invisible": false, "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "mutes": [], "name": "Luke Skywalker", "online": true, "role": "user", - "total_unread_count": 2, - "unread_channels": 1, - "unread_count": 2, - "updated_at": "2023-11-14T15:34:20.696818Z" + "total_unread_count": 0, + "unread_channels": 0, + "unread_count": 0, + "updated_at": "2023-12-11T18:27:14.887862Z" }, "type": "health.check" } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_message.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_message.json index a87dc8a1ab2..08f2916616a 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_message.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_message.json @@ -1,16 +1,16 @@ { - "channel_id": "cb3c193c-834d-11ee-bdff-067c57e250f4", + "channel_id": "a2debd8e-9ade-11ee-b565-5690af276325", "channel_type": "messaging", - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", - "created_at": "2023-11-15T00:27:46.061134139Z", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", + "created_at": "2023-12-15T00:12:31.112288342Z", "message": { "attachments": [], "before_message_send_failed": true, - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", - "created_at": "2023-11-15T00:27:46.027246Z", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", + "created_at": "2023-12-15T00:12:31.087657Z", "deleted_reply_count": 0, "html": "

Test

\n", - "id": "cc30e296-834d-11ee-bdff-067c57e250f4", + "id": "a3471802-9ade-11ee-b565-5690af276325", "latest_reactions": [], "mentioned_users": [], "own_reactions": [], @@ -25,7 +25,7 @@ "silent": false, "text": "Test", "type": "regular", - "updated_at": "2023-11-15T00:27:46.027246Z", + "updated_at": "2023-12-15T00:12:31.087657Z", "user": { "banned": false, "birthland": "Tatooine", @@ -35,17 +35,14 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } }, - "total_unread_count": 2, "type": "message.new", - "unread_channels": 2, - "unread_count": 2, "user": { "banned": false, "birthland": "Tatooine", @@ -55,11 +52,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "watcher_count": 1 } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_reaction.json b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_reaction.json index 3868ab6ad37..d1a8a7fcd78 100644 --- a/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_reaction.json +++ b/TestTools/StreamChatTestMockServer/Fixtures/JSONs/ws_reaction.json @@ -1,22 +1,22 @@ { - "channel_id": "cb3c193c-834d-11ee-bdff-067c57e250f4", + "channel_id": "a2debd8e-9ade-11ee-b565-5690af276325", "channel_type": "messaging", - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", - "created_at": "2023-11-15T00:27:46.248749761Z", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", + "created_at": "2023-12-15T00:12:31.247987101Z", "message": { "attachments": [], - "cid": "messaging:cb3c193c-834d-11ee-bdff-067c57e250f4", - "created_at": "2023-11-15T00:27:46.027246Z", + "cid": "messaging:a2debd8e-9ade-11ee-b565-5690af276325", + "created_at": "2023-12-15T00:12:31.087657Z", "deleted_reply_count": 0, "html": "

Test

\n", - "id": "cc30e296-834d-11ee-bdff-067c57e250f4", + "id": "a3471802-9ade-11ee-b565-5690af276325", "latest_reactions": [ { - "created_at": "2023-11-15T00:27:46.235917Z", - "message_id": "cc30e296-834d-11ee-bdff-067c57e250f4", + "created_at": "2023-12-15T00:12:31.234279Z", + "message_id": "a3471802-9ade-11ee-b565-5690af276325", "score": 1, "type": "like", - "updated_at": "2023-11-15T00:27:46.235917Z", + "updated_at": "2023-12-15T00:12:31.234279Z", "user": { "banned": false, "birthland": "Tatooine", @@ -26,11 +26,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "user_id": "luke_skywalker" } @@ -52,7 +52,7 @@ "silent": false, "text": "Test", "type": "regular", - "updated_at": "2023-11-15T00:27:46.242610888Z", + "updated_at": "2023-12-15T00:12:31.242136166Z", "user": { "banned": false, "birthland": "Tatooine", @@ -62,19 +62,19 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } }, "reaction": { - "created_at": "2023-11-15T00:27:46.235917Z", - "message_id": "cc30e296-834d-11ee-bdff-067c57e250f4", + "created_at": "2023-12-15T00:12:31.234279Z", + "message_id": "a3471802-9ade-11ee-b565-5690af276325", "score": 1, "type": "like", - "updated_at": "2023-11-15T00:27:46.235917Z", + "updated_at": "2023-12-15T00:12:31.234279Z", "user": { "banned": false, "birthland": "Tatooine", @@ -84,11 +84,11 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" }, "user_id": "luke_skywalker" }, @@ -102,10 +102,10 @@ "image": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "image_url": "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg", "language": "en", - "last_active": "2023-11-15T00:27:43.886025469Z", + "last_active": "2023-12-15T00:12:30.011341522Z", "name": "Luke Skywalker", "online": true, "role": "user", - "updated_at": "2023-11-14T15:34:20.696818Z" + "updated_at": "2023-12-11T18:27:14.887862Z" } } \ No newline at end of file diff --git a/TestTools/StreamChatTestMockServer/MockServer/StreamMockServer.swift b/TestTools/StreamChatTestMockServer/MockServer/StreamMockServer.swift index 604f707e66d..29a06203564 100644 --- a/TestTools/StreamChatTestMockServer/MockServer/StreamMockServer.swift +++ b/TestTools/StreamChatTestMockServer/MockServer/StreamMockServer.swift @@ -144,6 +144,13 @@ public extension StreamMockServer { func setCooldown(in channel: inout [String: Any]) { let cooldown = channelConfigs.coolDown - channel[channelKey.cooldownDuration.rawValue] = cooldown.isEnabled ? cooldown.duration : nil + if cooldown.isEnabled { + channel[channelKey.cooldownDuration.rawValue] = cooldown.duration + var ownCapabilities = channel[channelKey.ownCapabilities.rawValue] as? [String] + ownCapabilities?.removeAll { $0 == ChannelCapability.skipSlowMode.rawValue } + channel[channelKey.ownCapabilities.rawValue] = ownCapabilities + } else { + channel[channelKey.cooldownDuration.rawValue] = nil + } } } diff --git a/Tests/StreamChatTests/Controllers/MessageController/MessageController_Tests.swift b/Tests/StreamChatTests/Controllers/MessageController/MessageController_Tests.swift index b758fdb9561..ac31600e8f1 100644 --- a/Tests/StreamChatTests/Controllers/MessageController/MessageController_Tests.swift +++ b/Tests/StreamChatTests/Controllers/MessageController/MessageController_Tests.swift @@ -2341,7 +2341,7 @@ private class TestDelegate: QueueAwareDelegate, ChatMessageControllerDelegate { private class TestEnvironment { var messageUpdater: MessageUpdater_Mock! - var messageObserver: EntityDatabaseObserverWrapper_Mock! + var messageObserver: EntityDatabaseObserver_Mock! var repliesObserver: ListDatabaseObserverWrapper_Mock! var messageObserver_synchronizeError: Error? @@ -2350,11 +2350,10 @@ private class TestEnvironment { .Environment = .init( messageObserverBuilder: { [unowned self] in self.messageObserver = .init( - isBackground: $0, - database: $1, - fetchRequest: $2, - itemCreator: $3, - fetchedResultsControllerType: $4 + context: $0, + fetchRequest: $1, + itemCreator: $2, + fetchedResultsControllerType: $3 ) self.messageObserver.synchronizeError = self.messageObserver_synchronizeError return self.messageObserver! diff --git a/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift b/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift index 16bcb9087c3..dc8738695aa 100644 --- a/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift +++ b/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift @@ -3385,9 +3385,9 @@ final class MessageDTO_Tests: XCTestCase { // MARK: - isLocalOnly - func test_isLocalOnly_whenLocalMessageStateIsWaitingToBeSentToServer_returnsTrue() throws { + func test_isLocalOnly_whenLocalMessageStateIsLocalOnly_returnsTrue() throws { let message = try createMessage(with: .dummy(channel: .dummy())) - message.localMessageState = .pendingSync + message.localMessageState = .pendingSend XCTAssertEqual(message.isLocalOnly, true) } diff --git a/Tests/StreamChatTests/Models/ChatMessage_Tests.swift b/Tests/StreamChatTests/Models/ChatMessage_Tests.swift index 5070544a5b7..ad3bbb118a8 100644 --- a/Tests/StreamChatTests/Models/ChatMessage_Tests.swift +++ b/Tests/StreamChatTests/Models/ChatMessage_Tests.swift @@ -302,27 +302,27 @@ final class ChatMessage_Tests: XCTestCase { XCTAssertEqual(message.deliveryStatus, .read) } - func test_isWaitingToBeSentToServer_returnsTheCorrectValue() { + func test_isLocalOnly_returnsTheCorrectValue() { let stateToLocalOnly: [LocalMessageState: Bool] = [ - .pendingSync: true, - .syncing: true, - .syncingFailed: true, .pendingSend: true, .sending: true, .sendingFailed: true, + .pendingSync: false, + .syncing: false, + .syncingFailed: false, .deleting: false, .deletingFailed: false ] stateToLocalOnly.forEach { state, value in - XCTAssertEqual(state.isWaitingToBeSentToServer, value) + XCTAssertEqual(state.isLocalOnly, value) } } - func test_isLocalOnly_whenLocalStateIsWaitingToBeSentToServer_returnsTrue() { + func test_isLocalOnly_whenLocalStateIsLocalOnly_returnsTrue() { let message: ChatMessage = .mock( type: .regular, - localState: .pendingSync + localState: .pendingSend ) XCTAssertEqual(message.isLocalOnly, true) diff --git a/Tests/StreamChatTests/Utils/RepeatingTimer_Tests.swift b/Tests/StreamChatTests/Utils/RepeatingTimer_Tests.swift new file mode 100644 index 00000000000..004c98a79e7 --- /dev/null +++ b/Tests/StreamChatTests/Utils/RepeatingTimer_Tests.swift @@ -0,0 +1,40 @@ +// +// Copyright © 2023 Stream.io Inc. All rights reserved. +// + +@testable import StreamChat +import XCTest + +final class RepeatingTimer_Tests: XCTestCase { + func test_state_isThreadSafe() { + DispatchQueue.concurrentPerform(iterations: 10000) { _ in + let repeatingTimer: RepeatingTimerControl? = DefaultTimer.scheduleRepeating( + timeInterval: 0.4, + queue: .main, + onFire: {} + ) + repeatingTimer?.resume() + repeatingTimer?.suspend() + } + } + + func test_deinit_whenResumed_doesNotCrash() { + var repeatingTimer: RepeatingTimerControl? = DefaultTimer.scheduleRepeating( + timeInterval: 0.4, + queue: .main, + onFire: {} + ) + repeatingTimer?.resume() + repeatingTimer = nil + } + + func test_deinit_whenSuspended_doesNotCrash() { + var repeatingTimer: RepeatingTimerControl? = DefaultTimer.scheduleRepeating( + timeInterval: 0.4, + queue: .main, + onFire: {} + ) + repeatingTimer?.suspend() + repeatingTimer = nil + } +} diff --git a/Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift b/Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift index 13554772ed8..0fb47d43855 100644 --- a/Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift +++ b/Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift @@ -151,7 +151,7 @@ final class ChannelUpdater_Tests: XCTestCase { try database.createChannel(cid: cid, withMessages: false) // Local only message try database.createMessage(cid: cid, localState: .sendingFailed) - try database.createMessage(cid: cid, localState: .deletingFailed) + try database.createMessage(cid: cid, localState: .pendingSend) // Not local only message try database.createMessage(cid: cid, localState: .syncing) @@ -187,7 +187,7 @@ final class ChannelUpdater_Tests: XCTestCase { try database.createChannel(cid: cid, withMessages: false) // Local only message try database.createMessage(cid: cid, localState: .sendingFailed) - try database.createMessage(cid: cid, localState: .deletingFailed) + try database.createMessage(cid: cid, localState: .pendingSend) // Not local only message try database.createMessage(cid: cid, localState: .syncing) diff --git a/Tests/StreamChatUITests/Mocks/ChatMessageList/ChatMessageListVCDataSource_Mock.swift b/Tests/StreamChatUITests/Mocks/ChatMessageList/ChatMessageListVCDataSource_Mock.swift index 3ebaaa1b67f..0fd2b8c18ae 100644 --- a/Tests/StreamChatUITests/Mocks/ChatMessageList/ChatMessageListVCDataSource_Mock.swift +++ b/Tests/StreamChatUITests/Mocks/ChatMessageList/ChatMessageListVCDataSource_Mock.swift @@ -29,7 +29,7 @@ class ChatMessageListVCDataSource_Mock: ChatMessageListVCDataSource { } func chatMessageListVC(_ vc: StreamChatUI.ChatMessageListVC, messageAt indexPath: IndexPath) -> StreamChat.ChatMessage? { - messages[indexPath.item] + messages[safe: indexPath.item] } func chatMessageListVC(_ vc: StreamChatUI.ChatMessageListVC, messageLayoutOptionsAt indexPath: IndexPath) -> StreamChatUI.ChatMessageLayoutOptions { diff --git a/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/Attachments/ChatFileAttachmentListView+ItemView_Tests.swift b/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/Attachments/ChatFileAttachmentListView+ItemView_Tests.swift index 6814be14df5..ed3f4c32b19 100644 --- a/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/Attachments/ChatFileAttachmentListView+ItemView_Tests.swift +++ b/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/Attachments/ChatFileAttachmentListView+ItemView_Tests.swift @@ -29,6 +29,20 @@ final class ChatFileAttachmentListViewItemView_Tests: XCTestCase { AssertSnapshot(fileAttachmentView, variants: [.defaultLight]) } + func test_appearance_pdf_whenUploadingStateIsNil() { + fileAttachmentView.content = .mock(id: .unique, localState: nil) + AssertSnapshot(fileAttachmentView, variants: [.defaultLight]) + } + + func test_appearance_pdf_whenSizeIsZero() { + fileAttachmentView.content = .mock( + id: .unique, + file: AttachmentFile(type: .pdf, size: 0, mimeType: "application/pdf"), + localState: nil + ) + AssertSnapshot(fileAttachmentView, variants: [.defaultLight]) + } + func test_appearanceCustomization_usingAppearance() { var appearance = Appearance() appearance.colorPalette.subtitleText = .red diff --git a/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/Attachments/__Snapshots__/ChatFileAttachmentListView+ItemView_Tests/test_appearance_pdf_whenSizeIsZero.default-light.png b/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/Attachments/__Snapshots__/ChatFileAttachmentListView+ItemView_Tests/test_appearance_pdf_whenSizeIsZero.default-light.png new file mode 100644 index 00000000000..b4e3bc9dce4 Binary files /dev/null and b/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/Attachments/__Snapshots__/ChatFileAttachmentListView+ItemView_Tests/test_appearance_pdf_whenSizeIsZero.default-light.png differ diff --git a/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/Attachments/__Snapshots__/ChatFileAttachmentListView+ItemView_Tests/test_appearance_pdf_whenUploadingStateIsNil.default-light.png b/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/Attachments/__Snapshots__/ChatFileAttachmentListView+ItemView_Tests/test_appearance_pdf_whenUploadingStateIsNil.default-light.png new file mode 100644 index 00000000000..2c3b09cf2e8 Binary files /dev/null and b/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/Attachments/__Snapshots__/ChatFileAttachmentListView+ItemView_Tests/test_appearance_pdf_whenUploadingStateIsNil.default-light.png differ diff --git a/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessageListVC_Tests.swift b/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessageListVC_Tests.swift index af05fac8d77..9c56e3d98c3 100644 --- a/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessageListVC_Tests.swift +++ b/Tests/StreamChatUITests/SnapshotTests/ChatMessageList/ChatMessageListVC_Tests.swift @@ -42,6 +42,7 @@ final class ChatMessageListVC_Tests: XCTestCase { override func tearDown() { mockedDataSource = nil mockedDelegate = nil + AttachmentViewCatalog_Mock.attachmentViewInjectorClassForCallCount = 0 super.tearDown() } @@ -475,7 +476,21 @@ final class ChatMessageListVC_Tests: XCTestCase { XCTAssertEqual(mockedListView.scrollToBottomCallCount, 1) } - func test_updateMessages_whenNewestMessageMovedByCurrentUser_whenFistPageNotLoaded_shouldScrollToBottom() { + func test_updateMessages_whenNewestMessageInsertedByCurrentUser_whenMultipleInsertions_shouldScrollToBottom() { + mockedDataSource.mockedIsFirstPageLoaded = true + mockedListView.mockIsLastCellFullyVisible = true + + sut.updateMessages(with: [ + .insert(.mock(isSentByCurrentUser: false), index: .init(item: 0, section: 0)), + .insert(.mock(isSentByCurrentUser: false), index: .init(item: 0, section: 0)), + .insert(.mock(isSentByCurrentUser: true), index: .init(item: 0, section: 0)) + ]) + mockedListView.updateMessagesCompletion?() + + XCTAssertEqual(mockedListView.scrollToBottomCallCount, 0) + } + + func test_updateMessages_whenNewestMessageMovedByCurrentUser_whenFistPageNotLoaded_shouldNotScrollToBottom() { mockedDataSource.mockedIsFirstPageLoaded = false mockedListView.mockIsLastCellFullyVisible = true @@ -1082,4 +1097,42 @@ final class ChatMessageListVC_Tests: XCTestCase { sut.handlePan(.init()) XCTAssertEqual(handlerMock.handleCallCount, 0) } + + // MARK: - attachmentViewInjectorClassForMessage + + func test_attachmentViewInjectorClassForMessage_shouldAskForAttachmentInjector() { + sut.components.attachmentViewCatalog = AttachmentViewCatalog_Mock.self + mockedDataSource.messages = [.unique] + + _ = sut.attachmentViewInjectorClassForMessage(at: .init(item: 0, section: 0)) + + XCTAssertEqual(AttachmentViewCatalog_Mock.attachmentViewInjectorClassForCallCount, 1) + } + + func test_attachmentViewInjectorClassForMessage_whenMessageIsNil_returnsNil() { + sut.components.attachmentViewCatalog = AttachmentViewCatalog_Mock.self + mockedDataSource.messages = [] + + _ = sut.attachmentViewInjectorClassForMessage(at: .init(item: 0, section: 0)) + + XCTAssertEqual(AttachmentViewCatalog_Mock.attachmentViewInjectorClassForCallCount, 0) + } + + func test_attachmentViewInjectorClassForMessage_whenMessageIsDeleted_returnsNil() { + sut.components.attachmentViewCatalog = AttachmentViewCatalog_Mock.self + mockedDataSource.messages = [.mock(deletedAt: .unique)] + + _ = sut.attachmentViewInjectorClassForMessage(at: .init(item: 0, section: 0)) + + XCTAssertEqual(AttachmentViewCatalog_Mock.attachmentViewInjectorClassForCallCount, 0) + } +} + +class AttachmentViewCatalog_Mock: AttachmentViewCatalog { + static var mockedInjector: AttachmentViewInjector.Type? + static var attachmentViewInjectorClassForCallCount = 0 + override class func attachmentViewInjectorClassFor(message: ChatMessage, components: Components) -> AttachmentViewInjector.Type? { + attachmentViewInjectorClassForCallCount += 1 + return mockedInjector + } } diff --git a/Tests/StreamChatUITests/SnapshotTests/MessageActionsPopup/ChatMessageActionsVC_Tests.swift b/Tests/StreamChatUITests/SnapshotTests/MessageActionsPopup/ChatMessageActionsVC_Tests.swift index 110cf397c47..632d07bf9a3 100644 --- a/Tests/StreamChatUITests/SnapshotTests/MessageActionsPopup/ChatMessageActionsVC_Tests.swift +++ b/Tests/StreamChatUITests/SnapshotTests/MessageActionsPopup/ChatMessageActionsVC_Tests.swift @@ -315,22 +315,6 @@ final class ChatMessageActionsVC_Tests: XCTestCase { XCTAssertTrue(vc.messageActions.contains(where: { $0 is FlagActionItem })) } - func test_messageActions_whenSendingOrSyncingOrDeleting_thenContainsEmptyActions() { - let states: [LocalMessageState] = [.sending, .syncing, .deleting] - - states.forEach { - chatMessageController.simulateInitial( - message: .mock(localState: $0, isSentByCurrentUser: false), - replies: [], - state: .remoteDataFetched - ) - - vc.channel = .mock(cid: .unique, ownCapabilities: []) - - XCTAssertTrue(vc.messageActions.isEmpty) - } - } - func test_messageActions_whenSendingFailed_thenContainsResendActionEditActionDeleteAction() { chatMessageController.simulateInitial( message: .mock(localState: .sendingFailed, isSentByCurrentUser: false), @@ -369,8 +353,16 @@ final class ChatMessageActionsVC_Tests: XCTestCase { XCTAssertFalse(vc.messageActions.contains(where: { $0 is CopyActionItem })) } - func test_messageActions_whenPendingOrSyncingFailedOrDeletingFailed_thenContainsEditActionDeleteAction() { - let states: [LocalMessageState] = [.pendingSend, .pendingSync, .syncingFailed, .deletingFailed] + func test_messageActions_whenLocalStateNotNilOrSendingFailed_thenContainsEditActionDeleteAction() { + let states: [LocalMessageState] = [ + .pendingSend, + .pendingSync, + .syncingFailed, + .deletingFailed, + .sending, + .syncing, + .deleting + ] states.forEach { chatMessageController.simulateInitial( diff --git a/docusaurus/docs/iOS/assets/custom-channel-list.png b/docusaurus/docs/iOS/assets/custom-channel-list.png new file mode 100644 index 00000000000..426c926bbaa Binary files /dev/null and b/docusaurus/docs/iOS/assets/custom-channel-list.png differ diff --git a/docusaurus/docs/iOS/client/custom-cdn.md b/docusaurus/docs/iOS/client/custom-cdn.md index 661a7bb8a59..d98510a8362 100644 --- a/docusaurus/docs/iOS/client/custom-cdn.md +++ b/docusaurus/docs/iOS/client/custom-cdn.md @@ -66,6 +66,10 @@ config.customCDNClient = CustomCDNClient(uploadFileApi: YourUploadFileApi()) In case your API does not support progress updates, you can simple ignore the argument. ::: +### Custom Firebase CDN + +You can also use `FirebaseStorage` as your custom CDN. There is a Swift Package called `StreamFirebaseCDN`, built by the community to facilitate this, which can be found [here](https://github.com/pzmudzinski/StreamFirebaseCDN). + ## Custom AttachmentUploader implementation If you require changing more details of the attachment, in case your upload file API supports more features, you can provide a custom `AttachmentUploader`. diff --git a/docusaurus/docs/iOS/swiftui/swiftui-cookbook/custom-channel-list.md b/docusaurus/docs/iOS/swiftui/swiftui-cookbook/custom-channel-list.md new file mode 100644 index 00000000000..2946282b168 --- /dev/null +++ b/docusaurus/docs/iOS/swiftui/swiftui-cookbook/custom-channel-list.md @@ -0,0 +1,236 @@ +--- +title: Custom Channel List +description: How to build your own UI for previewing channels +--- + +There are different variations on how to present a list of channels. Our `ChatChannelListView` component provides a lot of customization options to support those variations. + +However, in some cases, you would need to build a completely custom UI. In this cookbook, we will build a simple channel list view, without using our UI components, only the low-level client state. + +![Screenshot showing the custom channel list](../../assets/custom-channel-list.png) + +## Channels State + +We will expose the channels state with a view model, for example `CustomChannelListViewModel`. The view model will be an observable object, and it will implement the `ChatChannelListControllerDelegate`. + +We will keep the channels as a published variable in the view model. + +```swift +@Published public var channels = LazyCachedMapCollection() +``` + +The `ChatChannelListControllerDelegate` has a method called `controller:didChangeChannels`, that sends updates when the channels are updated. + +```swift +public func controller( + _ controller: ChatChannelListController, + didChangeChannels changes: [ListChange] +) { + channels = controller.channels +} +``` + +In order to support pagination, we will use the `ChatChannelListController`'s method `loadNextChannels`. We can call this method when the channel list items appear on the screen. + +```swift +public func checkForChannels(index: Int) { + if index < (controller?.channels.count ?? 0) - 15 { + return + } + + if !loadingNextChannels { + loadingNextChannels = true + controller?.loadNextChannels(limit: 30) { [weak self] _ in + guard let self = self else { return } + self.loadingNextChannels = false + } + } +} +``` + +Those are the most important bits for fetching and listening to the channels data. For reference, here's the full implementation of the view model. + +```swift +import StreamChat +import StreamChatSwiftUI +import SwiftUI + +class CustomChannelListViewModel: ObservableObject, ChatChannelListControllerDelegate { + + @Injected(\.chatClient) var chatClient + + @Published public var channels = LazyCachedMapCollection() + + public private(set) var loadingNextChannels: Bool = false + + private var controller: ChatChannelListController? + private var timer: Timer? + + init() { + setupChannelListController() + } + + public func controller( + _ controller: ChatChannelListController, + didChangeChannels changes: [ListChange] + ) { + channels = controller.channels + } + + public func checkForChannels(index: Int) { + if index < (controller?.channels.count ?? 0) - 15 { + return + } + + if !loadingNextChannels { + loadingNextChannels = true + controller?.loadNextChannels(limit: 30) { [weak self] _ in + guard let self = self else { return } + self.loadingNextChannels = false + } + } + } + + func imageURL(for channel: ChatChannel) -> URL? { + channel.lastActiveMembers.first(where: { member in + member.id != chatClient.currentUserId + })?.imageURL + } + + //MARK: - private + + private func updateChannels() { + channels = controller?.channels ?? LazyCachedMapCollection() + } + + private func setupChannelListController() { + guard let currentUserId = chatClient.currentUserId else { + observeClientIdChange() + return + } + controller = chatClient.channelListController( + query: .init(filter: .containMembers(userIds: [currentUserId])) + ) + + controller?.delegate = self + + updateChannels() + + controller?.synchronize { [weak self] error in + guard let self = self else { return } + if error != nil { + print("handle error here") + } else { + self.updateChannels() + } + } + } + + private func observeClientIdChange() { + timer?.invalidate() + timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { [weak self] _ in + guard let self = self else { return } + if self.chatClient.currentUserId != nil { + self.timer?.invalidate() + self.timer = nil + self.setupChannelListController() + } + }) + } +} +``` + +## Channel List View + +Next, let's implement the channel list view. We will call it `CustomChannelListView`. + +Since the list of channels is exposed via a view model, you can easily build any UI you prefer with SwiftUI. Our implementation is a pretty simple one. + +Here's the code. + +```swift +import StreamChat +import StreamChatSwiftUI +import SwiftUI + +struct CustomChannelListView: View { + + @Injected(\.chatClient) var chatClient + + @StateObject var viewModel = CustomChannelListViewModel() + + var body: some View { + NavigationStack { + ScrollView { + LazyVStack { + ForEach(viewModel.channels) { channel in + NavigationLink { + ChatChannelView( + channelController: chatClient.channelController(for: channel.cid) + ) + } label: { + HStack { + if let imageURL = viewModel.imageURL(for: channel) { + StreamLazyImage(url: imageURL) + } else { + Circle() + .fill(Color.gray) + .frame(width: 30) + } + VStack(alignment: .leading) { + Text(DefaultChatChannelNamer()(channel, chatClient.currentUserId) ?? "") + .lineLimit(1) + .bold() + Text("\(channel.previewMessage?.text ?? "No messages")") + .font(.caption) + } + Spacer() + } + .padding(.all, 12) + .foregroundColor(.primary) + .background(Color(uiColor: .secondarySystemBackground)) + .cornerRadius(16) + .padding(.horizontal) + } + .onAppear { + if let index = viewModel.channels.firstIndex(where: { chatChannel in + chatChannel.id == channel.id + }) { + viewModel.checkForChannels(index: index) + } + } + } + } + } + .navigationTitle("Chat") + .navigationBarTitleDisplayMode(.inline) + } + } +} +``` + +The most interesting part here is the navigation. On tap of a channel item, we want to show the selected channel. + +For this reason, we are using a `NavigationLink`. Its destination is a `ChatChannelView`, which is the default channel view component from the SDK. However, you can also implement your own implementation here. + +For loading new channels, we rely on the `onAppear` modifier on each channel list item. Whenever a new entry appears on screen, we find its index and check if we should load more channels by calling the view model's `checkForChannels` method. + +With that, you can use the custom channel list anywhere in your code. + +```swift + var body: some Scene { + WindowGroup { + CustomChannelListView() + } + } +``` + +Make sure that you have previously created and connected the `StreamChat` client, as described in the [getting started](../getting-started.md) section. + +## Summary + +In this cookbook, you learnt how to implement a completely custom channel list view. For customizations of the existing component, please check the following [page](../channel-list-components/helper-views.md). + +You can learn more about the channel list state exposed by the low-level client [here](../../client/controllers/channels.md). + +As a next step, you can also explore other parts of our cookbook, where we build many interesting customizations. Furthermore, for a complete social experience, we recommend looking into our [Video SDK](https://getstream.io/video/docs/ios/). diff --git a/docusaurus/docs/iOS/swiftui/swiftui-cookbook/overview.md b/docusaurus/docs/iOS/swiftui/swiftui-cookbook/overview.md index 66037111422..e48d6e0e427 100644 --- a/docusaurus/docs/iOS/swiftui/swiftui-cookbook/overview.md +++ b/docusaurus/docs/iOS/swiftui/swiftui-cookbook/overview.md @@ -31,6 +31,12 @@ This example will show you how to always align the messages to the left. ![Screenshot of the left aligned message list.](../../assets/swiftui-left-aligned.png) +### Custom Channel List + +We will build a channel list using the low-level client state in this section. + +![Screenshot of the custom channel list.](../../assets/custom-channel-list.png) + ### Custom Message List We will build a message list using the low-level client state in this section. diff --git a/docusaurus/docs/iOS/swiftui/voice-recording.md b/docusaurus/docs/iOS/swiftui/voice-recording.md new file mode 100644 index 00000000000..ee1b06ffe4e --- /dev/null +++ b/docusaurus/docs/iOS/swiftui/voice-recording.md @@ -0,0 +1,94 @@ +--- +title: Voice Recording +--- + +Stream Chat's SwiftUI SDK allows you to record and share async voice messages in your channels. The voice recordings have a built-in attachment type (as defined [here](https://getstream.io/chat/docs/sdk/ios/uikit/guides/working-with-attachments/)). + +:::note +Voice Recordings on SwiftUI are available since version [4.46.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.46.0). +::: + +Voice recording is disabled by default. In order to enable it, you should setup the `isVoiceRecordingEnabled` property to `true`, when setting up the `StreamChat` client: + +```swift +let utils = Utils( + composerConfig: ComposerConfig(isVoiceRecordingEnabled: true) +) +let streamChat = StreamChat(chatClient: chatClient, utils: utils) +``` + +## Recording UI Flows + +The voice recording feature supports several different UI flows. + +### Recording + +When the user long presses on the voice recording button longer than 1 second, the recording is started. In that case, while the button is still pressed, the recording view is shown. + +The recording view provides the following actions: +- add the recording to the composer input (invoked when releasing the long press button) +- slide to cancel (invoked when you drag to the slide to cancel indicator) +- lock the recording (invoked when drag towards the lock button) + +In order to replace this view with your own implementation, you can implement the following `ViewFactory` method. + +```swift +public func makeComposerRecordingView( + viewModel: MessageComposerViewModel, + gestureLocation: CGPoint +) -> some View { + CustomRecordingView(viewModel: MessageComposerViewModel, location: gestureLocation) +``` + +### Locked View + +When the user decides to lock the recording, `LockedView` is presented. It provides the user the following actions: +- discard the recording - the user is back to the initial state. +- stop the recording - the user goes into the recording preview state. +- confirm the recording - the recording is added to the composer input. + +If you want to replace this view with your own implementation, you can implement the following method. + +```swift +public func makeComposerRecordingLockedView( + viewModel: MessageComposerViewModel +) -> some View { + CustomLockedView(viewModel: viewModel) +} +``` + +#### Preview Recording + +If you stop the recording, the `LockedView` goes into a preview state. This means that there's no recording in progress, but you can still both discard or confirm the recording. Additionally, you can play the recording. + +### Recording Tip View + +If you press and release the voice recording button for less than 1 second, a tip view is presented. If you want to customize this view, you can implement the following method in the `ViewFactory`. + +```swift +public func makeComposerRecordingTipView() -> some View { + CustomRecordingTipView() +} +``` + +## Voice Recording Attachment + +When a message with a voice recording attachment is sent, it appears in the message list with a voice recording specific user interface. + +If you want to change the default UI, you should implement the `makeVoiceRecordingView` in the `ViewFactory`. + +```swift +public func makeVoiceRecordingView( + for message: ChatMessage, + isFirst: Bool, + availableWidth: CGFloat, + scrolledId: Binding +) -> some View { + CustomVoiceRecordingView( + message: message, + width: availableWidth, + isFirst: isFirst, + scrolledId: scrolledId + ) +} +``` \ No newline at end of file diff --git a/docusaurus/docs/iOS/uikit/guides/working-with-attachments.md b/docusaurus/docs/iOS/uikit/guides/working-with-attachments.md index 388d18ee2bb..50dd068a6c0 100644 --- a/docusaurus/docs/iOS/uikit/guides/working-with-attachments.md +++ b/docusaurus/docs/iOS/uikit/guides/working-with-attachments.md @@ -270,7 +270,30 @@ Components.default.attachmentViewCatalog = MyAttachmentViewCatalog.self The `AttachmentViewCatalog` class is used to pick the `AttachmentViewInjector` for a message, in our case we only need to check if the message contains a workout attachment. -If needed you can also send a message with a workout attachment directly from Swift just to test that everything works correctly: +In case you want your attachments to be rendered along with other types of attachments, you can do this by adhering your custom injector to the `MixedAttachmentViewInjector`. For this, first, you need to register your custom injector to the `MixedAttachmentViewInjector` registry like so: + +```swift +Components.default.mixedAttachmentInjector.register(.workout, with: WorkoutAttachmentViewInjector.self) +``` + +Then, you need to change your Catalog implementation to return the Mixed injector in case there are multiple types of attachments: + +```swift +class MyAttachmentViewCatalog: AttachmentViewCatalog { + override class func attachmentViewInjectorClassFor(message: ChatMessage, components: Components) -> AttachmentViewInjector.Type? { + let hasMultipleTypesOfAttachments = message.attachmentCounts.keys.count > 1 + if message.attachmentCounts.keys.contains(.workout) { + if hasMultipleTypesOfAttachments { + return MixedAttachmentViewInjector.self + } + return WorkoutAttachmentViewInjector.self + } + return super.attachmentViewInjectorClassFor(message: message, components: components) + } +} +``` + +If needed you can send a message with a workout attachment directly from Swift just to test that everything works correctly: ```swift let controller = client.channelController(for: ChannelId(type: .messaging, id: "my-test-channel")) diff --git a/docusaurus/sidebars-ios.json b/docusaurus/sidebars-ios.json index 7fcfb7ad627..83d968147b7 100644 --- a/docusaurus/sidebars-ios.json +++ b/docusaurus/sidebars-ios.json @@ -85,7 +85,8 @@ ] }, "swiftui/localization", - "swiftui/dependency-injection" + "swiftui/dependency-injection", + "swiftui/voice-recording" ] }, { @@ -95,6 +96,7 @@ "swiftui/swiftui-cookbook/custom-composer", "swiftui/swiftui-cookbook/reactions-reordering", "swiftui/swiftui-cookbook/list-alignment", + "swiftui/swiftui-cookbook/custom-channel-list", "swiftui/swiftui-cookbook/custom-message-list", "swiftui/swiftui-cookbook/creating-channels" ]