Skip to content

Commit 64ff09e

Browse files
Fix delays when removing reactions in reaction details (#1417)
1 parent 8092a3c commit 64ff09e

2 files changed

Lines changed: 117 additions & 0 deletions

File tree

Sources/StreamChatSwiftUI/ChatMessageList/Reactions/ReactionsDetailViewModel.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class ReactionsDetailViewModel: ObservableObject, ChatReactionListControllerDele
109109
func messageController(_ controller: ChatMessageController, didChangeMessage change: EntityChange<ChatMessage>) {
110110
if let message = controller.message {
111111
self.message = message
112+
syncCurrentUserReactions(from: message)
112113
}
113114
}
114115

@@ -146,4 +147,26 @@ class ReactionsDetailViewModel: ObservableObject, ChatReactionListControllerDele
146147
private var userReactionIDs: Set<MessageReactionType> {
147148
Set(message.currentUserReactions.map(\.type))
148149
}
150+
151+
private func syncCurrentUserReactions(from message: ChatMessage) {
152+
guard let currentUserId = chatClient.currentUserId else { return }
153+
154+
let currentUserReactions = message.currentUserReactions
155+
.sorted { $0.updatedAt > $1.updatedAt }
156+
let hadCurrentUserReactions = reactions.contains { $0.author.id == currentUserId }
157+
158+
guard hadCurrentUserReactions || !currentUserReactions.isEmpty else { return }
159+
160+
let mergedReactions = reactions
161+
.filter { $0.author.id != currentUserId }
162+
+ currentUserReactions
163+
164+
let sortedReactions = mergedReactions.sorted { first, second in
165+
first.updatedAt > second.updatedAt
166+
}
167+
168+
if reactions != sortedReactions {
169+
reactions = sortedReactions
170+
}
171+
}
149172
}

StreamChatSwiftUITests/Tests/ChatChannel/ReactionsDetailViewModel_Tests.swift

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import XCTest
99

1010
@MainActor final class ReactionsDetailViewModel_Tests: StreamChatTestCase {
11+
private let currentUserId = StreamChatTestCase.currentUserId
12+
1113
// MARK: - Init
1214

1315
func test_init_thenSynchronizeCalled() {
@@ -612,6 +614,98 @@ import XCTest
612614
XCTAssertEqual(viewModel.message.text, "Updated text")
613615
}
614616

617+
func test_messageControllerDidChangeMessage_thenCurrentUserReactionIsInsertedImmediately() {
618+
// Given
619+
let cid = ChannelId.unique
620+
let like = MessageReactionType(rawValue: "like")
621+
let love = MessageReactionType(rawValue: "love")
622+
let message = ChatMessage.mock(cid: cid)
623+
let controller = makeReactionListController(messageId: message.id)
624+
let existingReaction = ChatMessageReaction.mock(
625+
id: "existing",
626+
type: love,
627+
updatedAt: Date(timeIntervalSince1970: 100),
628+
author: .mock(id: "other-user")
629+
)
630+
controller.reactions_simulated = [existingReaction]
631+
632+
let viewModel = ReactionsDetailViewModel(message: message, reactionListController: controller)
633+
viewModel.controller(controller, didChangeReactions: [])
634+
635+
let messageController = ChatMessageControllerSUI_Mock.mock(
636+
chatClient: chatClient,
637+
currentUserId: currentUserId,
638+
cid: cid,
639+
messageId: message.id
640+
)
641+
let currentUserReaction = ChatMessageReaction.mock(
642+
id: "current-user-reaction",
643+
type: like,
644+
updatedAt: Date(timeIntervalSince1970: 200),
645+
author: .mock(id: currentUserId, name: "You")
646+
)
647+
let updatedMessage = ChatMessage.mock(
648+
id: message.id,
649+
cid: cid,
650+
reactionScores: [like: 1, love: 1],
651+
reactionCounts: [like: 1, love: 1],
652+
currentUserReactions: [currentUserReaction]
653+
)
654+
messageController.message_mock = updatedMessage
655+
656+
// When
657+
viewModel.messageController(messageController, didChangeMessage: .update(updatedMessage))
658+
659+
// Then
660+
XCTAssertEqual(viewModel.reactions.map(\.id), ["current-user-reaction", "existing"])
661+
}
662+
663+
func test_messageControllerDidChangeMessage_thenCurrentUserReactionIsRemovedImmediately() {
664+
// Given
665+
let cid = ChannelId.unique
666+
let like = MessageReactionType(rawValue: "like")
667+
let love = MessageReactionType(rawValue: "love")
668+
let message = ChatMessage.mock(cid: cid)
669+
let controller = makeReactionListController(messageId: message.id)
670+
let currentUserReaction = ChatMessageReaction.mock(
671+
id: "current-user-reaction",
672+
type: like,
673+
updatedAt: Date(timeIntervalSince1970: 200),
674+
author: .mock(id: currentUserId, name: "You")
675+
)
676+
let existingReaction = ChatMessageReaction.mock(
677+
id: "existing",
678+
type: love,
679+
updatedAt: Date(timeIntervalSince1970: 100),
680+
author: .mock(id: "other-user")
681+
)
682+
controller.reactions_simulated = [currentUserReaction, existingReaction]
683+
684+
let viewModel = ReactionsDetailViewModel(message: message, reactionListController: controller)
685+
viewModel.controller(controller, didChangeReactions: [])
686+
687+
let messageController = ChatMessageControllerSUI_Mock.mock(
688+
chatClient: chatClient,
689+
currentUserId: currentUserId,
690+
cid: cid,
691+
messageId: message.id
692+
)
693+
let updatedMessage = ChatMessage.mock(
694+
id: message.id,
695+
cid: cid,
696+
reactionScores: [love: 1],
697+
reactionCounts: [love: 1],
698+
currentUserReactions: []
699+
)
700+
messageController.message_mock = updatedMessage
701+
702+
// When
703+
viewModel.messageController(messageController, didChangeMessage: .update(updatedMessage))
704+
705+
// Then
706+
XCTAssertEqual(viewModel.reactions.map(\.id), ["existing"])
707+
}
708+
615709
func test_messageControllerDidChangeReactions_thenReactionsUpdated() {
616710
// Given
617711
let cid = ChannelId.unique

0 commit comments

Comments
 (0)