Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added thread replies shown in channel indicator #518

Merged
merged 2 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
- Thread replies shown in channel indicator [#518](https://github.com/GetStream/stream-chat-swiftui/pull/518)

# [4.57.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.57.0)
_June 07, 2024_
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class ChannelControllerFactory {

@Injected(\.chatClient) var chatClient

private var currentChannelController: ChatChannelController?
var currentChannelController: ChatChannelController?
private var messageControllers = [String: ChatMessageController]()

/// Creates a channel controller with the provided channel id.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,28 @@ public struct MessageContainerView<Factory: ViewFactory>: View {
.accessibilityElement(children: .contain)
.accessibilityIdentifier("MessageView")

if message.replyCount > 0 && !isInThread {
factory.makeMessageRepliesView(
channel: channel,
message: message,
replyCount: message.replyCount
)
.accessibilityElement(children: .contain)
.accessibility(identifier: "MessageRepliesView")
if !isInThread {
if message.replyCount > 0 {
factory.makeMessageRepliesView(
channel: channel,
message: message,
replyCount: message.replyCount
)
.accessibilityElement(children: .contain)
.accessibility(identifier: "MessageRepliesView")
} else if message.showReplyInChannel,
let parentId = message.parentMessageId,
let controller = utils.channelControllerFactory.currentChannelController,
let parentMessage = controller.dataStore.message(id: parentId) {
factory.makeMessageRepliesShownInChannelView(
channel: channel,
message: message,
parentMessage: parentMessage,
replyCount: parentMessage.replyCount
)
.accessibilityElement(children: .contain)
.accessibility(identifier: "MessageRepliesView")
}
}

if bottomReactionsShown {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,23 @@ public struct MessageRepliesView<Factory: ViewFactory>: View {
var channel: ChatChannel
var message: ChatMessage
var replyCount: Int
var isRightAligned: Bool
var showReplyCount: Bool

public init(factory: Factory, channel: ChatChannel, message: ChatMessage, replyCount: Int) {
public init(
factory: Factory,
channel: ChatChannel,
message: ChatMessage,
replyCount: Int,
showReplyCount: Bool = true,
isRightAligned: Bool? = nil
) {
self.factory = factory
self.channel = channel
self.message = message
self.replyCount = replyCount
self.isRightAligned = isRightAligned ?? message.isRightAligned
self.showReplyCount = showReplyCount
}

public var body: some View {
Expand All @@ -41,15 +52,15 @@ public struct MessageRepliesView<Factory: ViewFactory>: View {
)
} label: {
HStack {
if !message.isRightAligned {
if !isRightAligned {
MessageAvatarView(
avatarURL: message.threadParticipants.first?.imageURL,
size: .init(width: 16, height: 16)
)
}
Text("\(replyCount) \(repliesText)")
Text(title)
.font(fonts.footnoteBold)
if message.isRightAligned {
if isRightAligned {
MessageAvatarView(
avatarURL: message.threadParticipants.first?.imageURL,
size: .init(width: 16, height: 16)
Expand Down Expand Up @@ -81,13 +92,21 @@ public struct MessageRepliesView<Factory: ViewFactory>: View {
)
.offset(y: -24)
.rotation3DEffect(
.degrees(message.isRightAligned ? 180 : 0),
.degrees(isRightAligned ? 180 : 0),
axis: (x: 0, y: 1, z: 0)
)
)
.foregroundColor(colors.tintColor)
}
}

var title: String {
if showReplyCount {
return "\(replyCount) \(repliesText)"
} else {
return L10n.Message.Threads.reply
}
}

var repliesText: String {
if message.replyCount == 1 {
Expand Down
16 changes: 16 additions & 0 deletions Sources/StreamChatSwiftUI/DefaultViewFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,22 @@ extension ViewFactory {
)
}

public func makeMessageRepliesShownInChannelView(
channel: ChatChannel,
message: ChatMessage,
parentMessage: ChatMessage,
replyCount: Int
) -> some View {
MessageRepliesView(
factory: self,
channel: channel,
message: parentMessage,
replyCount: replyCount,
showReplyCount: false,
isRightAligned: message.isRightAligned
)
}

public func makeMessageComposerViewType(
with channelController: ChatChannelController,
messageController: ChatMessageController?,
Expand Down
15 changes: 15 additions & 0 deletions Sources/StreamChatSwiftUI/ViewFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,21 @@ public protocol ViewFactory: AnyObject {
message: ChatMessage,
replyCount: Int
) -> MessageRepliesViewType

associatedtype MessageRepliesShownInChannelViewType: View
/// Creates the message replies view for a reply that is also shown in a channel.
/// - Parameters:
/// - channel: the channel where the message is sent.
/// - message: the message that's being replied to.
/// - parentMessage: the parent message.
/// - replyCount: the current number of replies.
/// - Returns: view displayed in the message replies view slot.
func makeMessageRepliesShownInChannelView(
channel: ChatChannel,
message: ChatMessage,
parentMessage: ChatMessage,
replyCount: Int
) -> MessageRepliesShownInChannelViewType

associatedtype MessageComposerViewType: View
/// Creates the message composer view.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,30 @@ class MessageView_Tests: StreamChatTestCase {
// Then
assertSnapshot(matching: view, as: .image(perceptualPrecision: precision))
}

func test_messageRepliesViewShownInChannel_snapshot() {
// Given
let channel = ChatChannel.mockDMChannel()
let message = ChatMessage.mock(
id: .unique,
cid: channel.cid,
text: "Message with replies",
author: .mock(id: .unique)
)

// When
let view = MessageRepliesView(
factory: DefaultViewFactory.shared,
channel: channel,
message: message,
replyCount: 3,
showReplyCount: false
)
.frame(width: 300, height: 60)

// Then
assertSnapshot(matching: view, as: .image(perceptualPrecision: precision))
}

func test_topLeftView_snapshot() {
// Given
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions StreamChatSwiftUITests/Tests/Utils/ViewFactory_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,22 @@ class ViewFactory_Tests: StreamChatTestCase {
// Then
XCTAssert(view is MessageRepliesView<DefaultViewFactory>)
}

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

// When
let view = viewFactory.makeMessageRepliesShownInChannelView(
channel: ChatChannel.mockDMChannel(),
message: message,
parentMessage: message,
replyCount: 2
)

// Then
XCTAssert(view is MessageRepliesView<DefaultViewFactory>)
}

func test_viewFactory_makeTypingIndicatorBottomView() {
// Given
Expand Down
Loading