Show deleted messages in channel preview + missing channel item redesign#1338
Show deleted messages in channel preview + missing channel item redesign#1338nuno-vieira wants to merge 17 commits intodevelopfrom
Conversation
This reverts commit c2482f8.
|
Important Review skippedReview was skipped due to path filters β Files ignored due to path filters (6)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including βοΈ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
π WalkthroughWalkthroughChannel list UI removed the injected Changes
Estimated code review effortπ― 4 (Complex) | β±οΈ ~45 minutes Poem
π₯ Pre-merge checks | β 2 | β 1β Failed checks (1 warning)
β Passed checks (2 passed)
βοΈ Tip: You can configure your own custom pre-merge checks in the settings. β¨ Finishing Touchesπ§ͺ Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and canβt be posted inline due to platform limitations.
β οΈ Outside diff range comments (1)
DemoAppSwiftUI/PinChannelHelpers.swift (1)
95-126:β οΈ Potential issue | π‘ MinorKeep the pinning demo row in sync with the framework row.
When
isChannelPinningFeatureEnabledis on, this custom item is the one that gets rendered, but its subtitle/status logic still stops at draft/plain preview text. UnlikeChatChannelListItem, it never renders the deleted-message placeholder/icon or the failed-message badge, so the pinning demo path won't actually show the behavior this PR adds. Consider extracting the shared preview-state block instead of maintaining a second copy here.Also applies to: 176-183
π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@DemoAppSwiftUI/PinChannelHelpers.swift` around lines 95 - 126, The custom pinning row's subtitle/status (in subtitleText and subtitleView) only handles typing, draft and plain preview but misses deleted-message placeholder/icon and failed-message badge that ChatChannelListItem shows; extract the shared preview-state rendering logic used by ChatChannelListItem into a reusable helper (e.g., a subtitleContent/subtitleView provider) and replace the duplicated logic in PinChannelHelpers (subtitleText/subtitleView) so it: checks shouldShowTypingIndicator, shows draft UI when draftMessageText exists, formats previewMessage via utils.messagePreviewFormatter, and also renders deleted-message placeholder/icon and the failed-message badge consistent with ChatChannelListItem (including image/icon and SubtitleText usage).
π§Ή Nitpick comments (1)
Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swift (1)
544-550: Consider simplifying redundant conditions.Both branches now call
updateChannels(), making the conditional structure redundant. This could be simplified:β»οΈ Suggested simplification
private func handleChannelAppearance() { - if skippedChannelUpdates && selectedChannel == nil { - updateChannels() - } else if skippedChannelUpdates { + if skippedChannelUpdates { updateChannels() } }π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swift` around lines 544 - 550, The conditional in handleChannelAppearance is redundant because both branches call updateChannels(); replace the whole if/else structure with a single condition that checks skippedChannelUpdates and calls updateChannels() (retain use of skippedChannelUpdates and selectedChannel only if needed elsewhere); update the function handleChannelAppearance to simply call updateChannels() when skippedChannelUpdates is true to remove duplicate branches.
π€ Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListItem.swift`:
- Around line 253-268: lastMessageFailedToSend currently only checks
previewMessage?.localState == .sendingFailed, so .syncingFailed previews slip
through and cause shouldShowReadEvents to return true; update the logic in
lastMessageFailedToSend (used by shouldShowReadEvents) to treat .syncingFailed
the same as .sendingFailed (e.g., check for both states on
previewMessage?.localState) so MessageReadIndicatorView and subtitle rendering
hide failure affordances consistently when previewMessage.localState is
.sendingFailed or .syncingFailed.
---
Outside diff comments:
In `@DemoAppSwiftUI/PinChannelHelpers.swift`:
- Around line 95-126: The custom pinning row's subtitle/status (in subtitleText
and subtitleView) only handles typing, draft and plain preview but misses
deleted-message placeholder/icon and failed-message badge that
ChatChannelListItem shows; extract the shared preview-state rendering logic used
by ChatChannelListItem into a reusable helper (e.g., a
subtitleContent/subtitleView provider) and replace the duplicated logic in
PinChannelHelpers (subtitleText/subtitleView) so it: checks
shouldShowTypingIndicator, shows draft UI when draftMessageText exists, formats
previewMessage via utils.messagePreviewFormatter, and also renders
deleted-message placeholder/icon and the failed-message badge consistent with
ChatChannelListItem (including image/icon and SubtitleText usage).
---
Nitpick comments:
In `@Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swift`:
- Around line 544-550: The conditional in handleChannelAppearance is redundant
because both branches call updateChannels(); replace the whole if/else structure
with a single condition that checks skippedChannelUpdates and calls
updateChannels() (retain use of skippedChannelUpdates and selectedChannel only
if needed elsewhere); update the function handleChannelAppearance to simply call
updateChannels() when skippedChannelUpdates is true to remove duplicate
branches.
πͺ Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
βΉοΈ Review info
βοΈ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: bdc5d3e2-e888-4948-af48-88fed2245ba7
β Files ignored due to path filters (30)
Sources/StreamChatSwiftUI/Generated/L10n.swiftis excluded by!**/generated/**StreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_deletedMessage_dmChannel.default-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_deletedMessage_dmChannel.extraExtraExtraLarge-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_deletedMessage_dmChannel.rightToLeftLayout-default.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_deletedMessage_dmChannel.small-dark.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_deletedMessage_groupChannel.default-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_deletedMessage_groupChannel.extraExtraExtraLarge-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_deletedMessage_groupChannel.rightToLeftLayout-default.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_deletedMessage_groupChannel.small-dark.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_deletedMessage_sentByCurrentUser.default-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_deletedMessage_sentByCurrentUser.extraExtraExtraLarge-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_deletedMessage_sentByCurrentUser.rightToLeftLayout-default.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_deletedMessage_sentByCurrentUser.small-dark.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_emptyMessages.default-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_emptyMessages.extraExtraExtraLarge-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_emptyMessages.rightToLeftLayout-default.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_emptyMessages.small-dark.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_ephemeralMessageSkipped_showsPreviousMessage.default-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_ephemeralMessageSkipped_showsPreviousMessage.extraExtraExtraLarge-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_ephemeralMessageSkipped_showsPreviousMessage.rightToLeftLayout-default.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_ephemeralMessageSkipped_showsPreviousMessage.small-dark.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_giphyMessageLatestButPreviewIsAnotherMessage.1.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_messageFailedToSend.default-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_messageFailedToSend.extraExtraExtraLarge-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_messageFailedToSend.rightToLeftLayout-default.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_messageFailedToSend.small-dark.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_messagePending.default-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_messagePending.extraExtraExtraLarge-light.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_messagePending.rightToLeftLayout-default.pngis excluded by!**/*.pngStreamChatSwiftUITests/Tests/ChatChannelList/__Snapshots__/ChatChannelListItemView_Tests/test_channelListItem_messagePending.small-dark.pngis excluded by!**/*.png
π Files selected for processing (12)
DemoAppSwiftUI/PinChannelHelpers.swiftSources/StreamChatSwiftUI/ChatChannelList/ChatChannelListItem.swiftSources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swiftSources/StreamChatSwiftUI/ChatChannelList/ChatChannelNavigatableListItem.swiftSources/StreamChatSwiftUI/Resources/en.lproj/Localizable.stringsSources/StreamChatSwiftUI/Utils/MessagePreviewFormatter.swiftStreamChatSwiftUI.xcodeproj/project.pbxprojStreamChatSwiftUITests/Infrastructure/Shared/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contentsStreamChatSwiftUITests/Tests/ChatChannel/MessageComposerViewModel_Tests.swiftStreamChatSwiftUITests/Tests/ChatChannelList/ChatChannelListItemView_Tests.swiftStreamChatSwiftUITests/Tests/ChatChannelList/ChatChannelListViewModel_Tests.swiftStreamChatSwiftUITests/Tests/Utils/MessagePreviewFormatter_Tests.swift
π€ Files with no reviewable changes (1)
- StreamChatSwiftUITests/Tests/ChatChannel/MessageComposerViewModel_Tests.swift
Generated by π« Danger |
martinmitrevski
left a comment
There was a problem hiding this comment.
Looks good, will do some tests next
| @@ -545,11 +545,7 @@ import UIKit | |||
| if skippedChannelUpdates && selectedChannel == nil { | |||
There was a problem hiding this comment.
we can now merge these 2 if statements though, right?
There was a problem hiding this comment.
What do you mean? π€
There was a problem hiding this comment.
When you see it like this, you will know π
private func handleChannelAppearance() {
if skippedChannelUpdates && selectedChannel == nil {
updateChannels()
} else if skippedChannelUpdates {
updateChannels()
}
}There was a problem hiding this comment.
Ah shiit XD, will fix this
| } | ||
| } | ||
|
|
||
| private func updateSelectedChannelData() { |
There was a problem hiding this comment.
This was crazy. However, I believe we should test before iOS 16, when a navigation view is used. I think the screen was popped in some cases, which was why we had this stuff.
There was a problem hiding this comment.
OK, @testableapple reminder to test this, I don't have a sim with this version, but if you don't have it as well, I'll install
There was a problem hiding this comment.
actually, what you can do instead is temporarily comment out the navigation stack code in NavigationContainerView and test with navigation view. You should force channel update from a channel that's not selected (just sending a channel in another channel should be fine).
There was a problem hiding this comment.
I tested, and it all looks good π (But maybe still worth testing on iOS 16)
Update reference images to reflect channel list item changes (deleted message display, swipe actions redesign, empty state text).
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and canβt be posted inline due to platform limitations.
β οΈ Outside diff range comments (1)
Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swift (1)
502-510:β οΈ Potential issue | π‘ MinorFilter ephemeral messages for consistency with
ChatChannelListItem.The PR objectives specify that preview messages should skip ephemeral messages using
first(where: { $0.type != .ephemeral }).ChatChannelListItem.swiftimplements this filtering (line 113), and there's a test confirming ephemeral messages should be skipped in channel list previews. However, the search results here usechannel.latestMessages.firstwithout filtering, which could display ephemeral messages in search result previews. Use the same filtering approach to maintain consistency.π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swift` around lines 502 - 510, In ChatChannelListViewModel where searchResults is built (the assignment creating ChannelSelectionInfo), change the message selection to skip ephemeral messages the same way ChatChannelListItem does: instead of using channel.latestMessages.first, use channel.latestMessages.first(where: { $0.type != .ephemeral }) so ChannelSelectionInfo.message excludes ephemeral previews and remains consistent with ChatChannelListItem and existing tests.
π€ Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swift`:
- Around line 544-548: The skippedChannelUpdates flag is never cleared after
queued updates are applied, causing repeated redundant updateChannels() calls;
after the updateChannels() call that applies queued changes (in the
selectedChannel willSet observer where handleChannelListChanges() set
skippedChannelUpdates = true), set skippedChannelUpdates = false so the
queued-update state is reset (alternatively you can clear it at the end of
handleChannelAppearance(), but prefer clearing it immediately after
updateChannels() in the willSet observer).
---
Outside diff comments:
In `@Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swift`:
- Around line 502-510: In ChatChannelListViewModel where searchResults is built
(the assignment creating ChannelSelectionInfo), change the message selection to
skip ephemeral messages the same way ChatChannelListItem does: instead of using
channel.latestMessages.first, use channel.latestMessages.first(where: { $0.type
!= .ephemeral }) so ChannelSelectionInfo.message excludes ephemeral previews and
remains consistent with ChatChannelListItem and existing tests.
πͺ Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
βΉοΈ Review info
βοΈ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ca046797-c1d0-4506-b650-a671c0e4610d
π Files selected for processing (2)
CHANGELOG.mdSources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swift
| private func handleChannelAppearance() { | ||
| if skippedChannelUpdates && selectedChannel == nil { | ||
| if skippedChannelUpdates { | ||
| updateChannels() | ||
| } else if skippedChannelUpdates { | ||
| updateSelectedChannelData() | ||
| } else if !skippedChannelUpdates && selectedChannel != nil { | ||
| if selectedChannel?.injectedChannelInfo == nil { | ||
| selectedChannel?.injectedChannelInfo = InjectedChannelInfo(unreadCount: 0) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
π§© Analysis chain
π Script executed:
#!/bin/bash
# Search for where skippedChannelUpdates is assigned
rg -n "skippedChannelUpdates\s*=" --type swift -C 2
# Check if the removed updateSelectedChannelData had reset logic (in git history)
git log -p --all -S "skippedChannelUpdates = false" -- "*.swift" 2>/dev/null | head -50Repository: GetStream/stream-chat-swiftui
Length of output: 1310
π Script executed:
# Search for all occurrences of skippedChannelUpdates (assignments, reads, resets)
rg -n "skippedChannelUpdates" --type swift -B 2 -A 2
# Look at the selectedChannel property definition and its observers
rg -n "@Published|selectedChannel|didSet|willSet" Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swift -A 5Repository: GetStream/stream-chat-swiftui
Length of output: 8436
Reset skippedChannelUpdates flag after applying queued updates.
The flag is set to true in handleChannelListChanges() at line 320, then checked in the selectedChannel willSet observer (line 60) when the channel is deselected. However, the flag is never reset to false after updateChannels() is called, which means subsequent handleChannelAppearance() invocations will redundantly call updateChannels() again.
Add skippedChannelUpdates = false after the updateChannels() call in the willSet observer (after line 61), or alternatively after the call in handleChannelAppearance() (after line 546). The former location is semantically more appropriate since that's where the "pop happened" and queued changes are actually applied.
π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swift`
around lines 544 - 548, The skippedChannelUpdates flag is never cleared after
queued updates are applied, causing repeated redundant updateChannels() calls;
after the updateChannels() call that applies queued changes (in the
selectedChannel willSet observer where handleChannelListChanges() set
skippedChannelUpdates = true), set skippedChannelUpdates = false so the
queued-update state is reset (alternatively you can clear it at the end of
handleChannelAppearance(), but prefer clearing it immediately after
updateChannels() in the willSet observer).
Co-authored-by: Stream Bot <ci@getstream.io>
SDK Size
|
StreamChatSwiftUI XCSize
Show 24 more objects
|
Public Interface- public final class InjectedChannelInfo: Sendable
-
- public let subtitle: String?
- public let unreadCount: Int
- public let timestamp: String?
- public let lastMessageAt: Date?
- public let latestMessages: [ChatMessage]?
-
-
- public init(subtitle: String? = nil,unreadCount: Int,timestamp: String? = nil,lastMessageAt: Date? = nil,latestMessages: [ChatMessage]? = nil)
public struct ChatChannelListItem: View
- public init(factory: Factory = DefaultViewFactory.shared,channel: ChatChannel,channelName: String,injectedChannelInfo: InjectedChannelInfo? = nil,disabled: Bool = false,onItemTap: @escaping (ChatChannel) -> Void)
+ public init(factory: Factory = DefaultViewFactory.shared,channel: ChatChannel,channelName: String,isSelected: Bool = false,disabled: Bool = false,onItemTap: @escaping (ChatChannel) -> Void)
public final class ChannelSelectionInfo: Identifiable, @unchecked Sendable
- public var injectedChannelInfo: InjectedChannelInfo?
+ public let searchType: ChannelListSearchType
- public let searchType: ChannelListSearchType
+
-
+
-
+ public init(channel: ChatChannel,message: ChatMessage?,searchType: ChannelListSearchType = .messages)
- public init(channel: ChatChannel,message: ChatMessage?,searchType: ChannelListSearchType = .messages) |
|



π Issue Links
IOS-1506
Depends on: GetStream/stream-chat-swift#4024
π― Goal
Deleted messages should be visible in the channel list preview.
π Summary
channel.latestMessages, filtering only ephemeral messagesInjectedChannelInfoand simplifyChatChannelListItemwith inline private computed properties@MainActorextensions onChatChannelβ formatting logic now lives in the viewMessagePreviewFormatterhandling of deleted messagesπ Implementation
The
ChatChannel.previewMessageproperty was removed from the core SDK (see linked PR). The preview message is now computed in the SwiftUI view layer aschannel.latestMessages.first(where: { $0.type != .ephemeral }), which allows deleted messages to appear in the preview while still filtering out ephemeral (Giphy) messages.InjectedChannelInfowas removed as it was unnecessary indirection β its only real purpose was suppressing unread badges for the selected channel, which is now handled by a simpleisSelected: Boolproperty.π§ͺ Manual Testing Notes
βοΈ Contributor Checklist
docs-contentrepoSummary by CodeRabbit
New Features
Updates