Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

# Upcoming

### 🔄 Changed
### ✅ Added
- Add factory methods for gallery and video player view [#808](https://github.com/GetStream/stream-chat-swiftui/pull/808)

# [4.77.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.77.0)
_April 10, 2025_
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public struct MediaAttachmentsView<Factory: ViewFactory>: View {
if !mediaItem.isVideo, let imageAttachment = mediaItem.imageAttachment {
let index = viewModel.allImageAttachments.firstIndex { $0.id == imageAttachment.id } ?? 0
ImageAttachmentContentView(
factory: factory,
mediaItem: mediaItem,
imageAttachment: imageAttachment,
allImageAttachments: viewModel.allImageAttachments,
Expand All @@ -66,6 +67,7 @@ public struct MediaAttachmentsView<Factory: ViewFactory>: View {
)
} else if let videoAttachment = mediaItem.videoAttachment {
VideoAttachmentContentView(
factory: factory,
attachment: videoAttachment,
author: mediaItem.author,
width: Self.itemWidth,
Expand Down Expand Up @@ -108,10 +110,11 @@ public struct MediaAttachmentsView<Factory: ViewFactory>: View {
}
}

struct ImageAttachmentContentView: View {
struct ImageAttachmentContentView<Factory: ViewFactory>: View {

@State private var galleryShown = false

let factory: Factory
let mediaItem: MediaItem
let imageAttachment: ChatMessageImageAttachment
let allImageAttachments: [ChatMessageImageAttachment]
Expand All @@ -134,8 +137,8 @@ struct ImageAttachmentContentView: View {
.clipped()
}
.fullScreenCover(isPresented: $galleryShown) {
GalleryView(
imageAttachments: allImageAttachments,
factory.makeGalleryView(
mediaAttachments: allImageAttachments.map { MediaAttachment(from: $0) },
author: mediaItem.author,
isShown: $galleryShown,
selected: index
Expand Down
16 changes: 2 additions & 14 deletions Sources/StreamChatSwiftUI/ChatChannel/Gallery/GalleryView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,7 @@ public struct GalleryView: View {
isShown: Binding<Bool>,
selected: Int
) {
let mediaAttachments = imageAttachments.map { attachment in
let url: URL
if let state = attachment.uploadingState {
url = state.localFileURL
} else {
url = attachment.imageURL
}
return MediaAttachment(
url: url,
type: .image,
uploadingState: attachment.uploadingState
)
}
let mediaAttachments = imageAttachments.map { MediaAttachment(from: $0) }
self.init(
mediaAttachments: mediaAttachments,
author: author,
Expand All @@ -49,7 +37,7 @@ public struct GalleryView: View {
)
}

init(
public init(
mediaAttachments: [MediaAttachment],
author: ChatUser,
isShown: Binding<Bool>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public struct ImageAttachmentContainer<Factory: ViewFactory>: View {
.fullScreenCover(isPresented: $galleryShown, onDismiss: {
self.selectedIndex = 0
}) {
GalleryView(
factory.makeGalleryView(
mediaAttachments: sources,
author: message.author,
isShown: $galleryShown,
Expand Down Expand Up @@ -431,7 +431,7 @@ extension ChatMessage {
}
}

struct MediaAttachment {
public struct MediaAttachment {
@Injected(\.utils) var utils

let url: URL
Expand Down Expand Up @@ -460,6 +460,22 @@ struct MediaAttachment {
}
}

extension MediaAttachment {
init(from attachment: ChatMessageImageAttachment) {
let url: URL
if let state = attachment.uploadingState {
url = state.localFileURL
} else {
url = attachment.imageURL
}
self.init(
url: url,
type: .image,
uploadingState: attachment.uploadingState
)
}
}

enum MediaAttachmentType {
case image
case video
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public struct VideoAttachmentsContainer<Factory: ViewFactory>: View {
)

VideoAttachmentsList(
factory: factory,
message: message,
width: width
)
Expand All @@ -38,6 +39,7 @@ public struct VideoAttachmentsContainer<Factory: ViewFactory>: View {
)
} else {
VideoAttachmentsList(
factory: factory,
message: message,
width: width
)
Expand All @@ -63,12 +65,18 @@ public struct VideoAttachmentsContainer<Factory: ViewFactory>: View {
}
}

public struct VideoAttachmentsList: View {
public struct VideoAttachmentsList<Factory: ViewFactory>: View {

let factory: Factory
let message: ChatMessage
let width: CGFloat

public init(message: ChatMessage, width: CGFloat) {
public init(
factory: Factory = DefaultViewFactory.shared,
message: ChatMessage,
width: CGFloat
) {
self.factory = factory
self.message = message
self.width = width
}
Expand All @@ -77,6 +85,7 @@ public struct VideoAttachmentsList: View {
VStack {
ForEach(message.videoAttachments, id: \.self) { attachment in
VideoAttachmentView(
factory: factory,
attachment: attachment,
message: message,
width: width
Expand All @@ -90,21 +99,24 @@ public struct VideoAttachmentsList: View {
}
}

public struct VideoAttachmentView: View {
public struct VideoAttachmentView<Factory: ViewFactory>: View {

let factory: Factory
let attachment: ChatMessageVideoAttachment
let message: ChatMessage
let width: CGFloat
var ratio: CGFloat = 0.75
var cornerRadius: CGFloat = 24

public init(
factory: Factory = DefaultViewFactory.shared,
attachment: ChatMessageVideoAttachment,
message: ChatMessage,
width: CGFloat,
ratio: CGFloat = 0.75,
cornerRadius: CGFloat = 24
) {
self.factory = factory
self.attachment = attachment
self.message = message
self.width = width
Expand All @@ -118,6 +130,7 @@ public struct VideoAttachmentView: View {

public var body: some View {
VideoAttachmentContentView(
factory: factory,
attachment: attachment,
author: message.author,
width: width,
Expand All @@ -128,7 +141,7 @@ public struct VideoAttachmentView: View {
}
}

struct VideoAttachmentContentView: View {
struct VideoAttachmentContentView<Factory: ViewFactory>: View {

@Injected(\.utils) private var utils
@Injected(\.images) private var images
Expand All @@ -137,6 +150,7 @@ struct VideoAttachmentContentView: View {
utils.videoPreviewLoader
}

let factory: Factory
let attachment: ChatMessageVideoAttachment
let author: ChatUser
let width: CGFloat
Expand Down Expand Up @@ -183,7 +197,7 @@ struct VideoAttachmentContentView: View {
.frame(width: width, height: width * ratio)
.cornerRadius(cornerRadius)
.fullScreenCover(isPresented: $fullScreenShown) {
VideoPlayerView(
factory.makeVideoPlayerView(
attachment: attachment,
author: author,
isShown: $fullScreenShown
Expand Down
26 changes: 26 additions & 0 deletions Sources/StreamChatSwiftUI/DefaultViewFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,32 @@ extension ViewFactory {
)
}

public func makeGalleryView(
mediaAttachments: [MediaAttachment],
author: ChatUser,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like mentioned in the previous comment maybe better to expose the whole message object?

isShown: Binding<Bool>,
selected: Int
) -> some View {
GalleryView(
mediaAttachments: mediaAttachments,
author: author,
isShown: isShown,
selected: selected
)
}

public func makeVideoPlayerView(
attachment: ChatMessageVideoAttachment,
author: ChatUser,
isShown: Binding<Bool>
) -> some View {
VideoPlayerView(
attachment: attachment,
author: author,
isShown: isShown
)
}

public func makeDeletedMessageView(
for message: ChatMessage,
isFirst: Bool,
Expand Down
28 changes: 28 additions & 0 deletions Sources/StreamChatSwiftUI/ViewFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,34 @@ public protocol ViewFactory: AnyObject {
availableWidth: CGFloat,
scrolledId: Binding<String?>
) -> VideoAttachmentViewType

associatedtype GalleryViewType: View
/// Creates the gallery view.
/// - Parameters:
/// - mediaAttachments: the media attachments that will be displayed.
/// - author: the author of the message.
/// - isShown: whether the gallery is shown.
/// - selected: the index of the selected attachment.
/// - Returns: view displayed in the gallery slot.
func makeGalleryView(
mediaAttachments: [MediaAttachment],
author: ChatUser,
isShown: Binding<Bool>,
selected: Int
) -> GalleryViewType

associatedtype VideoPlayerViewType: View
/// Creates the video player view.
/// - Parameters:
/// - attachment: the video attachment that will be displayed.
/// - author: the author of the message.
/// - isShown: whether the video player is shown.
/// - Returns: view displayed in the video player slot.
func makeVideoPlayerView(
attachment: ChatMessageVideoAttachment,
author: ChatUser,
isShown: Binding<Bool>
) -> VideoPlayerViewType

associatedtype DeletedMessageViewType: View
/// Creates the deleted message view.
Expand Down
31 changes: 31 additions & 0 deletions StreamChatSwiftUITests/Tests/Utils/ViewFactory_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,37 @@ class ViewFactory_Tests: StreamChatTestCase {
// Then
XCTAssert(view is ChannelAvatarView)
}

func test_viewFactory_makeGalleryView() {
// Given
let viewFactory = DefaultViewFactory.shared

// When
let view = viewFactory.makeGalleryView(
mediaAttachments: [],
author: .unique,
isShown: .constant(true),
selected: 0
)

// Then
XCTAssert(view is GalleryView)
}

func test_viewFactory_makeVideoPlayerView() {
// Given
let viewFactory = DefaultViewFactory.shared

// When
let view = viewFactory.makeVideoPlayerView(
attachment: .mock(id: .unique),
author: .unique,
isShown: .constant(true)
)

// Then
XCTAssert(view is VideoPlayerView)
}
}

extension ChannelAction: Equatable {
Expand Down
Loading