diff --git a/CHANGELOG.md b/CHANGELOG.md index c3de85f7e..c953ffb6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fix message long press taking too much time to show actions [#648](https://github.com/GetStream/stream-chat-swiftui/pull/648) - Fix rendering link attachment preview with other attachment types [#659](https://github.com/GetStream/stream-chat-swiftui/pull/659) - Fix not using colors from the palette in some of the poll views [#661](https://github.com/GetStream/stream-chat-swiftui/pull/661) +- Fix a rare crash when handling list change in the `ChatChannelViewModel` [#663](https://github.com/GetStream/stream-chat-swiftui/pull/663) ### 🔄 Changed - Message composer now uses `.uploadFile` capability when showing attachment picker icon [#646](https://github.com/GetStream/stream-chat-swiftui/pull/646) - `ChannelInfoView` now uses `.updateChannelMembers` capability to show "Add Users" button [#651](https://github.com/GetStream/stream-chat-swiftui/pull/651) @@ -59,8 +60,8 @@ _September 12, 2024_ ### 🔄 Changed - Improved subtitle info in pinned messages view [#594](https://github.com/GetStream/stream-chat-swiftui/pull/594) -- The `image(for channel: ChatChannel)` in `ChannelHeaderLoader` is now open [#595](https://github.com/GetStream/stream-chat-swiftui/pull/595) -- FlagMessage Action is now only shown if the user has a permission to perform the action [#599](https://github.com/GetStream/stream-chat-swiftui/pull/599) +- The `image(for channel: ChatChannel)` in `ChannelHeaderLoader` is now open [#595](https://github.com/GetStream/stream-chat-swiftui/pull/595) +- FlagMessage Action is now only shown if the user has a permission to perform the action [#599](https://github.com/GetStream/stream-chat-swiftui/pull/599) ### 🐞 Fixed - Typing users did not update reliably in the message list [#591](https://github.com/GetStream/stream-chat-swiftui/pull/591) diff --git a/Sources/StreamChatSwiftUI/ChatChannel/ChatChannelViewModel.swift b/Sources/StreamChatSwiftUI/ChatChannel/ChatChannelViewModel.swift index 547bd21a8..5ee41f5b2 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/ChatChannelViewModel.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/ChatChannelViewModel.swift @@ -709,19 +709,18 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource { .remove(_, index: _): return true case let .update(message, index: index): - let animateReactions = message.reactionScoresId != messages[index.row].reactionScoresId + guard index.row >= messages.startIndex, index.row < messages.endIndex else { continue } + let existingDisplayedMessage = messages[index.row] + let animateReactions = message.reactionScoresId != existingDisplayedMessage.reactionScoresId && utils.messageListConfig.messageDisplayOptions.shouldAnimateReactions - if index.row < messages.count, - message.messageId != messages[index.row].messageId + if animateReactions, + message.messageId != existingDisplayedMessage.messageId || message.type == .ephemeral || !message.linkAttachments.isEmpty { - if index.row < messages.count - && animateReactions { - animateChanges = message.linkAttachments.isEmpty - } + animateChanges = message.linkAttachments.isEmpty } - default: - break + case .move(_, fromIndex: _, toIndex: _): + continue } } diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/ChatChannelViewModel_Tests.swift b/StreamChatSwiftUITests/Tests/ChatChannel/ChatChannelViewModel_Tests.swift index 8c98ce1ab..075c10d94 100644 --- a/StreamChatSwiftUITests/Tests/ChatChannel/ChatChannelViewModel_Tests.swift +++ b/StreamChatSwiftUITests/Tests/ChatChannel/ChatChannelViewModel_Tests.swift @@ -489,6 +489,30 @@ class ChatChannelViewModel_Tests: StreamChatTestCase { // Then XCTAssert(shouldJump == false) } + + func test_chatChannelVM_crashWhenIndexAccess() { + // Given + let message1 = ChatMessage.mock() + let message2 = ChatMessage.mock() + let message3 = ChatMessage.mock() + let channelController = makeChannelController(messages: [message1, message2]) + let viewModel = ChatChannelViewModel(channelController: channelController) + let newMessages = LazyCachedMapCollection(elements: [message1, message2, message3]) + + // When + viewModel.dataSource( + channelDataSource: ChatChannelDataSource(controller: channelController), + didUpdateMessages: newMessages, + changes: [ + .insert(message3, index: IndexPath(row: 2, section: 0)), + .update(message3, index: IndexPath(row: 2, section: 0)), + .update(message3, index: IndexPath(row: 3, section: 0)) // intentionally invalid path + ] + ) + + // Then + XCTAssertEqual(3, viewModel.messages.count) + } // MARK: - private