Skip to content

Clear stale mention suggestions when the query ends - #1568

Merged
nuno-vieira merged 9 commits into
developfrom
fix/stale-mention-suggestions
Aug 13, 2026
Merged

Clear stale mention suggestions when the query ends#1568
nuno-vieira merged 9 commits into
developfrom
fix/stale-mention-suggestions

Conversation

@nuno-vieira

@nuno-vieira nuno-vieira commented Aug 12, 2026

Copy link
Copy Markdown
Member

🔗 Issue Links

🎯 Goal

Prevent stale mention suggestions from resurfacing while deleting an @… query, and clear LLC search state when mention mode ends.

📝 Summary

  • Discard superseded suggestion results in MessageComposerViewModel when the active mention query changes or ends
  • Clear non-instant composer commands when the text becomes empty (mentions have displayInfo == nil)
  • Add CommandHandler.clearSuggestions() and implement it in MentionsCommandHandler to cancel in-flight work and call MentionSuggestionsProvider.clearResults()
  • Pin StreamChat to fix/mention-suggestions-large-channels while the LLC PR is open

🛠 Implementation

Deleting @iOS character-by-character could race a slower search and leave suggestions visible after the query was cleared. The view model now cancels the active suggestions subscription, ignores results that no longer match the current command/query, and clears suggestions when composerCommand becomes nil.

MentionsCommandHandler tracks the suggestion Task, cancels it on clear, and forwards to the provider clearResults() API added in the LLC PR.

🧪 Manual Testing Notes

  • Type @ + a few characters in a channel, then delete back to empty
  • Confirm suggestions disappear and do not reappear after a delay
  • Select a mention and confirm the overlay dismisses cleanly
  • Repeat on a large channel once the LLC branch is available

☑️ Contributor Checklist

  • I have signed the Stream CLA (required)
  • This change should be manually QAed
  • Changelog is updated with client-facing changes
  • Changelog is updated with new localization keys
  • New code is covered by unit tests
  • Documentation has been updated in the docs-content repo

Summary by CodeRabbit

  • Bug Fixes

    • Improved mention suggestions in large channels by cancelling outdated requests and ignoring stale results.
    • Suggestions now clear correctly when text, commands, or mention queries are removed.
    • Clearing the composer reliably resets active command state.
    • Prevented delayed suggestion results from appearing after the composer’s context changes.
  • Tests

    • Added coverage for mention cleanup, request cancellation, stale results, and clearing provider results.

Cancel in-flight suggestion requests and ignore superseded results so
deleting an @mention query does not briefly re-show the suggestions list.
Forward clearSuggestions through command handlers so MentionsCommandHandler cancels in-flight work and clears provider results when the composer leaves mention mode.
Use the LLC fix/mention-suggestions-large-channels branch so SwiftUI picks up clearResults and MemberSearch while that PR is open.
@nuno-vieira
nuno-vieira requested a review from a team as a code owner August 12, 2026 15:54
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1f524ba9-30d9-4768-b07f-ca9cbbd52560

📥 Commits

Reviewing files that changed from the base of the PR and between 655a8c6 and f00f32f.

📒 Files selected for processing (1)
  • Sources/StreamChatSwiftUI/ChatComposer/MessageComposerViewModel.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/StreamChatSwiftUI/ChatComposer/MessageComposerViewModel.swift

📝 Walkthrough

Walkthrough

The composer now cancels obsolete suggestion requests, clears stale command results, and applies results only for the current command and query. Mention handlers expose cancellation and provider cleanup. Tests cover text deletion, command removal, and in-flight cancellation.

Changes

Suggestion cleanup flow

Layer / File(s) Summary
Command handler cleanup contract
Sources/StreamChatSwiftUI/ChatComposer/Suggestions/CommandsHandler.swift
CommandHandler and CommandsHandler now expose clearSuggestions(). The default implementation is a no-op.
Mention request cancellation
Sources/StreamChatSwiftUI/ChatComposer/Suggestions/Mentions/MentionsCommandHandler.swift, StreamChatSwiftUITests/Tests/ChatChannel/Suggestions/MentionsCommandHandler_Tests.swift
Mention requests are tracked and cancelled before replacement or cleanup. Provider results are cleared asynchronously. Tests cover cancellation and provider cleanup.
Composer state synchronization
Sources/StreamChatSwiftUI/ChatComposer/MessageComposerViewModel.swift, StreamChatSwiftUITests/Tests/ChatChannel/MessageComposerViewModel_Tests.swift
The view model clears commands and suggestions when text changes, cancels obsolete requests, and ignores results for outdated commands or queries. Tests cover mention cleanup during text deletion.
Dependency revision
Package.swift, StreamChatSwiftUI.xcodeproj/project.pbxproj
The stream-chat-swift dependency references the new fixed revision.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Mergeability Score: ⚪ Minimal · up to f00f3

The change clears stale mention suggestions when a query ends and cancels superseded searches; no actionable merge-blocking risk remains after normal checks and review.

Possibly related PRs

Suggested reviewers: laevandus, martinmitrevski

Sequence Diagram(s)

sequenceDiagram
  participant Composer as MessageComposerViewModel
  participant Commands as CommandsHandler
  participant Mentions as MentionsCommandHandler
  participant Provider as SuggestionsProvider
  Composer->>Commands: clearSuggestions()
  Commands->>Mentions: clearSuggestions()
  Mentions->>Provider: clearResults()
  Composer->>Mentions: request suggestions for command and query
  Mentions->>Provider: fetch suggestions
  Composer->>Composer: accept only matching command and query
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: preventing stale mention suggestions after the mention query ends.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/stale-mention-suggestions

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.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
Sources/StreamChatSwiftUI/ChatComposer/MessageComposerViewModel.swift (1)

215-216: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the private documentation comment.

suggestionsCancellable is private. Its name describes its purpose. Do not add /// documentation for private declarations.

As per coding guidelines: “Write concise /// documentation only for public SDK declarations; do not document internal, private, or test code.”

🤖 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 `@Sources/StreamChatSwiftUI/ChatComposer/MessageComposerViewModel.swift` around
lines 215 - 216, Remove the private documentation comment immediately above
suggestionsCancellable in MessageComposerViewModel, leaving the property
declaration unchanged.

Source: Coding guidelines

Package.swift (1)

19-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pin the temporary StreamChat dependency to revision 900c626dd3b4c0036962fd1ab3febc119b657239.

Use this revision in both Package.swift and StreamChatSwiftUI.xcodeproj/project.pbxproj. Update it after the upstream change merges.

🤖 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 `@Package.swift` at line 19, Pin the temporary StreamChat dependency to
revision 900c626dd3b4c0036962fd1ab3febc119b657239 instead of the branch
reference in Package.swift and StreamChatSwiftUI.xcodeproj/project.pbxproj
(1546-1547); update both dependency declarations consistently, and switch back
after the upstream change merges.
🤖 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.

Inline comments:
In `@Sources/StreamChatSwiftUI/ChatComposer/MessageComposerViewModel.swift`:
- Around line 39-47: Update the command-removal flow in MessageComposerViewModel
so clearComposerSuggestions() is invoked only once when composerCommand is set
to nil. Remove or conditionally skip the explicit call after the animated
assignment, while preserving suggestion clearing for paths where the command
observer is not triggered.

In `@Sources/StreamChatSwiftUI/ChatComposer/Suggestions/CommandsHandler.swift`:
- Around line 268-272: Update CommandsHandler.clearSuggestions() to also call
mentionsCommandHandler.clearSuggestions(), ensuring TwoStepMentionCommand clears
its suggestions task and provider results. Preserve the existing loop that
clears suggestions for registered commands.

In
`@Sources/StreamChatSwiftUI/ChatComposer/Suggestions/Mentions/MentionsCommandHandler.swift`:
- Around line 127-131: Update clearSuggestions() and the mention-request flow to
track or serialize the provider.clearResults() task, ensuring cleanup completes
in ordering with subsequent requests. Prevent an untracked cleanup from
cancelling or clearing newer searches while preserving cancellation of the
current suggestionsTask.

In
`@StreamChatSwiftUITests/Tests/ChatChannel/Suggestions/MentionsCommandHandler_Tests.swift`:
- Around line 311-313: Replace the fixed 50 ms sleep in MentionsCommandHandler
tests with an XCTestExpectation fulfilled when the provider’s clear operation
completes, then wait for that expectation before asserting
clearResultsCallCount. In
StreamChatSwiftUITests/Tests/ChatChannel/Suggestions/MentionsCommandHandler_Tests.swift
lines 311-313, update the provider-clear flow; in
StreamChatSwiftUITests/Tests/ChatChannel/MessageComposerViewModel_Tests.swift
lines 730-731, inject a controlled delayed suggestion request, fulfill an
XCTestExpectation on completion, and await it before asserting stale results are
rejected.
- Around line 352-383: Synchronize the mutable state in
MockMentionSuggestionsProvider by protecting receivedRequests and
clearResultsCallCount with an actor or lock. Update mentionSuggestions(for:) and
clearResults() to use that synchronization while preserving their existing
behavior and the provider’s Sendable conformance.

---

Nitpick comments:
In `@Package.swift`:
- Line 19: Pin the temporary StreamChat dependency to revision
900c626dd3b4c0036962fd1ab3febc119b657239 instead of the branch reference in
Package.swift and StreamChatSwiftUI.xcodeproj/project.pbxproj (1546-1547);
update both dependency declarations consistently, and switch back after the
upstream change merges.

In `@Sources/StreamChatSwiftUI/ChatComposer/MessageComposerViewModel.swift`:
- Around line 215-216: Remove the private documentation comment immediately
above suggestionsCancellable in MessageComposerViewModel, leaving the property
declaration unchanged.
🪄 Autofix

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 Plus

Run ID: 308f0c74-93a0-4cf2-9555-7ac0ec7d19da

📥 Commits

Reviewing files that changed from the base of the PR and between 1e10b90 and 63b1c8c.

📒 Files selected for processing (7)
  • Package.swift
  • Sources/StreamChatSwiftUI/ChatComposer/MessageComposerViewModel.swift
  • Sources/StreamChatSwiftUI/ChatComposer/Suggestions/CommandsHandler.swift
  • Sources/StreamChatSwiftUI/ChatComposer/Suggestions/Mentions/MentionsCommandHandler.swift
  • StreamChatSwiftUI.xcodeproj/project.pbxproj
  • StreamChatSwiftUITests/Tests/ChatChannel/MessageComposerViewModel_Tests.swift
  • StreamChatSwiftUITests/Tests/ChatChannel/Suggestions/MentionsCommandHandler_Tests.swift

Comment thread Sources/StreamChatSwiftUI/ChatComposer/MessageComposerViewModel.swift Outdated
Clear suggestions once when emptying text, and replace sleep-based stale-mention coverage with expectation-driven delayed provider requests.
@github-actions

Copy link
Copy Markdown
1 Message
📖 There seems to be app changes but CHANGELOG wasn't modified.
Please include an entry if the PR includes user-facing changes.
You can find it at CHANGELOG.md.

Generated by 🚫 Danger

Comment thread Sources/StreamChatSwiftUI/ChatComposer/MessageComposerViewModel.swift Outdated
Comment thread Sources/StreamChatSwiftUI/ChatComposer/MessageComposerViewModel.swift Outdated

@laevandus laevandus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ I did some testing and did not get any odd behaviour. Left 2 minor comments.

@martinmitrevski martinmitrevski left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Use develop revision 8263bddc2 now that #4213 is merged.
Drop the debug log from the suggestions sink and use removeAll when clearing suggestions.
@nuno-vieira
nuno-vieira enabled auto-merge (squash) August 13, 2026 17:36
The Future publisher's sink requires both receiveCompletion and
receiveValue closures; the previous commit dropped receiveCompletion,
causing a type mismatch.
@github-actions

Copy link
Copy Markdown

Public Interface

 extension CommandHandler  
+   public func clearSuggestions()

 public final class MentionsCommandHandler: CommandHandler  
+   public func clearSuggestions()

 public class CommandsHandler: CommandHandler  
-   public func handleCommand(for text: Binding<String>,selectedRangeLocation: Binding<Int>,command: Binding<ComposerCommand?>,extraData: [String: Any])
+   public func clearSuggestions()
-   public func executeOnMessageSent(composerCommand: ComposerCommand,completion: @escaping @MainActor (Error?) -> Void)
+   public func handleCommand(for text: Binding<String>,selectedRangeLocation: Binding<Int>,command: Binding<ComposerCommand?>,extraData: [String: Any])
-   public func canBeExecuted(composerCommand: ComposerCommand)-> Bool
+   public func executeOnMessageSent(composerCommand: ComposerCommand,completion: @escaping @MainActor (Error?) -> Void)
+   public func canBeExecuted(composerCommand: ComposerCommand)-> Bool

@Stream-SDK-Bot

Copy link
Copy Markdown
Collaborator

SDK Size

title develop branch diff status
StreamChatSwiftUI 5.79 MB 5.79 MB +1 KB 🟢

@Stream-SDK-Bot

Copy link
Copy Markdown
Collaborator

StreamChatSwiftUI XCSize

Object Diff (bytes)
MentionsCommandHandler.o +3696
AlertBannerViewModifier.o -2628
MessageComposerViewModel.o +1010
CommandsHandler.o +196
TwoStepMentionCommand.o +148
StreamAsyncImage.o -56

@sonarqubecloud

Copy link
Copy Markdown

@nuno-vieira
nuno-vieira merged commit d9ffcf9 into develop Aug 13, 2026
15 checks passed
@nuno-vieira
nuno-vieira deleted the fix/stale-mention-suggestions branch August 13, 2026 22:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants