Skip to content

Commit

Permalink
Added thread replies shown in channel indicator (#518)
Browse files Browse the repository at this point in the history
  • Loading branch information
martinmitrevski authored Jun 19, 2024
1 parent c00c74d commit 2f07f70
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 15 deletions.
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
24 changes: 24 additions & 0 deletions StreamChatSwiftUITests/Tests/ChatChannel/MessageView_Tests.swift
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

0 comments on commit 2f07f70

Please sign in to comment.