Skip to content

MM-69016 Keep uncached DMs in Find Channels#36769

Open
cursor[bot] wants to merge 5 commits into
masterfrom
cursor/jira-bug-fix-agent-workflow-9acb
Open

MM-69016 Keep uncached DMs in Find Channels#36769
cursor[bot] wants to merge 5 commits into
masterfrom
cursor/jira-bug-fix-agent-workflow-9acb

Conversation

@cursor
Copy link
Copy Markdown
Contributor

@cursor cursor Bot commented May 27, 2026

Summary

Fixed Find Channels direct-message suggestions so existing DM channels are not dropped when the teammate profile has not been loaded yet. The fallback preserves the DM channel with its channel display data until the profile cache is warmed, keeping the direct DM ahead of matching group DMs.

Ticket Link

Fixes: https://mattermost.atlassian.net/browse/MM-69016

Screenshots

Before:
before

After:
after

Release Note

Fixed an issue where direct message channels could be missing from Find Channels results before teammate profiles loaded.
Open in Web View Automation 

Change Impact: 🟡 Medium

Regression Risk: Changes update SwitchChannelProvider's formatting/wrapping logic and add async profile-loading for missing DM profiles; unit tests were added covering fallback paths, but the provider is widely used by the channel switcher and Find Channels UI so asynchronous profile resolution could alter suggestion ordering/visibility in edge cases. There's moderate risk of regressions in suggestion ordering, display names, or unread/badge rendering when profiles are slow to load.

QA Recommendation: Moderate manual QA recommended: exercise Find Channels and the channel switcher with teammate profiles initially uncached, validate DM vs group-DM ordering and display before/after profile cache warms, and run regression checks for suggestion uniqueness and unread/badge behavior.

Generated by CodeRabbitAI

Review Change Stack

cursoragent and others added 3 commits May 27, 2026 16:34
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
@mattermost-build
Copy link
Copy Markdown
Contributor

Hello @cursor[bot],

Thanks for your pull request! A Core Committer will review your pull request soon. For code contributions, you can learn more about the review process here.

@mattermost-code mattermost-code marked this pull request as ready for review May 27, 2026 16:49
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

📝 Walkthrough

Walkthrough

SwitchChannelProvider now handles missing DM user profile data gracefully by creating fallback wrapped channels in both formatGroup and wrapChannels functions, instead of skipping those channels. Test coverage validates both functions retain direct channels with the expected item structure when teammate profiles are not cached.

Changes

DM Channel Profile Fallback Handling

Layer / File(s) Summary
Import and helper for missing-profile loading
webapp/channels/src/components/suggestion/switch_channel_provider.tsx
Adds getMissingProfilesByIds import and implements helpers to collect missing DM user IDs and asynchronously dispatch profile load requests with error handling.
Integrate profile loading into search flows
webapp/channels/src/components/suggestion/switch_channel_provider.tsx
Calls loadMissingDirectChannelProfiles during local pretext search and combined local+remote search, then re-runs matching/formatting with updated state.
formatGroup DM fallback handling
webapp/channels/src/components/suggestion/switch_channel_provider.tsx
Derives DM userId via Utils.getUserIdFromChannelId(), and when profile is missing marks the userId done and constructs a fallback WrappedChannel (userId, name from `display_name
Recently-viewed / unread profile loading
webapp/channels/src/components/suggestion/switch_channel_provider.tsx
fetchAndFormatRecentlyViewedChannels now loads missing DM profiles for unread and recently-viewed channels before wrapping to ensure wrapped channels use refreshed profile data.
wrapChannels DM fallback handling
webapp/channels/src/components/suggestion/switch_channel_provider.tsx
Derives DM userId and, if the profile is missing, produces a fallback wrapped channel by augmenting channel with userId and name from `display_name
Tests: missing-profile scenarios
webapp/channels/src/components/suggestion/switch_channel_provider.test.tsx
Adds mockProfilesById, a mocked getMissingProfilesByIds, a beforeEach reset, and two tests asserting formatGroup and wrapChannels keep direct-channel suggestions with expected ordering and channel.userId/type fields when teammate profiles are not cached.

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

2: Dev Review, Changelog/Done, release-note

Suggested reviewers

  • davidkrauser
  • hmhealey
  • lindalumitchell
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly relates to the main change: preserving uncached DM channels in Find Channels results.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/jira-bug-fix-agent-workflow-9acb

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
webapp/channels/src/components/suggestion/switch_channel_provider.tsx (1)

985-999: ⚡ Quick win

Reuse existing state variable instead of calling this.store.getState() again.

Line 986 calls this.store.getState() but state is already assigned at line 964. Use the existing variable for consistency.

♻️ Proposed fix
             } else if (channel.type === Constants.DM_CHANNEL) {
                 const userId = Utils.getUserIdFromChannelId(channel.name, getCurrentUserId(state));
-                const user = getUser(this.store.getState(), userId);
+                const user = getUser(state, userId);
 
                 if (user) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/channels/src/components/suggestion/switch_channel_provider.tsx` around
lines 985 - 999, The code calls this.store.getState() to compute user via
getUser(...), but a local state variable already exists (assigned earlier);
replace this.store.getState() with the existing state variable to avoid
redundant store access and ensure consistency—update the getUser call in the
block that uses Utils.getUserIdFromChannelId/getCurrentUserId to pass state, and
keep the rest of the userWrappedChannel/channel merging logic unchanged
(references: Utils.getUserIdFromChannelId, getCurrentUserId, getUser,
userWrappedChannel).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@webapp/channels/src/components/suggestion/switch_channel_provider.tsx`:
- Around line 985-999: The code calls this.store.getState() to compute user via
getUser(...), but a local state variable already exists (assigned earlier);
replace this.store.getState() with the existing state variable to avoid
redundant store access and ensure consistency—update the getUser call in the
block that uses Utils.getUserIdFromChannelId/getCurrentUserId to pass state, and
keep the rest of the userWrappedChannel/channel merging logic unchanged
(references: Utils.getUserIdFromChannelId, getCurrentUserId, getUser,
userWrappedChannel).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f94da5c0-9c88-49b0-9b36-f6e5ea55dd52

📥 Commits

Reviewing files that changed from the base of the PR and between 159ed5a and 5e36c0c.

📒 Files selected for processing (2)
  • webapp/channels/src/components/suggestion/switch_channel_provider.test.tsx
  • webapp/channels/src/components/suggestion/switch_channel_provider.tsx

Co-authored-by: mattermost-code <matty-code@mattermost.com>
@cursor cursor Bot removed the AI/babysit label May 27, 2026
@mattermost-code mattermost-code added the Setup Cloud Test Server Setup an on-prem test server label May 27, 2026
@mm-cloud-bot
Copy link
Copy Markdown

Creating a new SpinWick test server using Mattermost Cloud.

Copy link
Copy Markdown
Contributor Author

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Babysit complete — ready for human review

PR: MM-69016 Keep uncached DMs in Find Channels

Status

Check Result
CodeRabbit Approved (coderabbitai, review on 5e36c0c; post-0dbdd47 summary: no actionable comments)
CI All checks green on 0dbdd47 (Web App CI, Enterprise CI/tests, e2e cypress/playwright enterprise, Snyk, CodeQL, etc.)
Merge conflicts None (mergeable: MERGEABLE)

Changes made during babysit

  • Addressed CodeRabbit nitpick: reuse existing state in wrapChannels instead of this.store.getState() for getUser (0dbdd47).

Handoff

  • Removed AI/babysit label; added Setup Cloud Test Server.
  • Requested human reviewer via automation workflow.
Open in Web View Automation 

Sent by Cursor Automation: Matty Code (Babysit Until Human Review)

@lieut-data lieut-data requested review from davidkrauser and removed request for lieut-data May 27, 2026 17:53
@mm-cloud-bot
Copy link
Copy Markdown

Mattermost Spinwick PR #36769 🎉

Test server created!

Access here: https://mattermost-pr-36769-eu3tf.test.mattermost.cloud

Installation ID: ghea5gcq63815boudp7uuh3hmc
Logs: Click here

Credentials: Posted securely in this Mattermost channel - Look for PR #36769

Copy link
Copy Markdown
Member

@hmhealey hmhealey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is likely to solve the issue because the DM channel's display_name won't be set (see below).

The root cause of this is that the user isn't loaded properly, so I think the only way to fix this will be to make the SwitchChannelProvider able to load the user itself. If it was just needed to render the SwitchChannelSuggestion, that'd be much easier because we could just use the useUser hook I added, but the SwitchChannelProvider needs that information for sorting as well, so that's not an option here. Instead, I think we would need the SwitchChannelProvider to return dispatch getMissingProfilesByIds to load those, but it might be tricky to do that at the right time because of how formatGroup and fetchAndFormatRecentlyViewedChannels need to truncate the data, sort it, and fetch the users at different times. For recently viewed channels, I think the order is sort then truncate then fetch users (since the names don't affect sorting), and for unread channels or search results, we would fetch users then sort then truncate.

Separate from that, to test this properly, you'd need to ensure that the user isn't loaded in some other way before opening the channel switcher. You can check the Redux store using the dev tools when testing manually to ensure that the user wasn't loaded first, but for an automated test, I did that recently for a similar issue in #36548, so the same technique would work here

id: 'direct_joram_user',
type: 'D',
name: 'current_user_id__joram_user',
display_name: 'Joram User',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think these tests are valid because the display_name for DMs is empty in the Redux state while this assumes that it's already set to "Joram User". Some of the selectors will populate that based when the other user is loaded, but I think the problem here is that the user likely hasn't been loaded.

The same also applies to the group channel below, although they have a default display_name made up by concatenated usernames which you see in Ian's screenshot when he reported this. The key tell that it's the default display name is that it only shows usernames and it includes the current user in it

@mattermost-code
Copy link
Copy Markdown
Contributor

Looking!

@mattermost-code mattermost-code requested a review from hmhealey May 28, 2026 14:14
@mattermost-code
Copy link
Copy Markdown
Contributor

PR Feedback Resolution Summary

Addressed (2 items):

  • FB-001 — Updated the cited tests so uncached DM/GM fixtures no longer assume profile-populated display names.
  • FB-002 — Updated SwitchChannelProvider to load missing DM profiles before formatting/sorting search and unread results, and after recency truncation for recent channels.

Declined (0 items):

Needs Human Review (0 items):

Verification:

  • Targeted tests: ✅ npm test -- switch_channel_provider.test.tsx --runInBand
  • /deslop: ⚠️ no /deslop executable or Cursor CLI hook was available in this shell; performed manual diff review and git diff --check
  • /precommit: ✅ npm run check, npm run check-types, npm run i18n-extract:check
  • Visual proof captured for 0 items

@mm-cloud-bot
Copy link
Copy Markdown

New commit detected. SpinWick will upgrade if the updated docker image is available.

@mm-cloud-bot
Copy link
Copy Markdown

Test server creation failed. Review the error details here.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
webapp/channels/src/components/suggestion/switch_channel_provider.tsx (1)

936-969: 💤 Low value

Consider adding cancellation checks after async profile loading.

Other async methods like fetchAndFormatLocalSearchResults and fetchUsersAndChannels check shouldCancelDispatch after awaiting async operations to avoid processing stale results. This method has two await points (lines 948 and 968) but no cancellation checks. If the user types a search term while profiles are loading, this method may still call resultsCallback with recently-viewed channels after the search results have already been dispatched.

Proposed fix
         await this.loadMissingDirectChannelProfiles(unreadChannelsExclMuted);
+        if (this.shouldCancelDispatch('')) {
+            return;
+        }
         state = this.store.getState();
         ...
         await this.loadMissingDirectChannelProfiles(sortedRecentRawChannels);
+        if (this.shouldCancelDispatch('')) {
+            return;
+        }
         sortedRecentChannels = this.wrapChannels(sortedRecentRawChannels, Constants.MENTION_RECENT_CHANNELS);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/channels/src/components/suggestion/switch_channel_provider.tsx` around
lines 936 - 969, After each await of this.loadMissingDirectChannelProfiles
inside fetchAndFormatRecentlyViewedChannels (the two async points where profiles
are loaded for unreadChannelsExclMuted and sortedRecentRawChannels), call
this.shouldCancelDispatch() and return early if it returns true so you don't
continue processing stale results; i.e., immediately after the first await
(before re-reading state and continuing) and immediately after the second await
(before wrapping channels and calling resultsCallback/fetchChannels), check
shouldCancelDispatch and abort if canceled.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@webapp/channels/src/components/suggestion/switch_channel_provider.tsx`:
- Around line 936-969: After each await of this.loadMissingDirectChannelProfiles
inside fetchAndFormatRecentlyViewedChannels (the two async points where profiles
are loaded for unreadChannelsExclMuted and sortedRecentRawChannels), call
this.shouldCancelDispatch() and return early if it returns true so you don't
continue processing stale results; i.e., immediately after the first await
(before re-reading state and continuing) and immediately after the second await
(before wrapping channels and calling resultsCallback/fetchChannels), check
shouldCancelDispatch and abort if canceled.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8d27e5da-9326-4ffe-adde-7b5961a31e99

📥 Commits

Reviewing files that changed from the base of the PR and between 0dbdd47 and 7787fe1.

📒 Files selected for processing (2)
  • webapp/channels/src/components/suggestion/switch_channel_provider.test.tsx
  • webapp/channels/src/components/suggestion/switch_channel_provider.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • webapp/channels/src/components/suggestion/switch_channel_provider.test.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Contributor Setup Cloud Test Server Setup an on-prem test server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants