feat: advanced commands flow and composer effects#1723
Merged
isekovanic merged 24 commits intomasterfrom Apr 30, 2026
Merged
Conversation
Contributor
|
Size Change: +5.63 kB (+1.5%) Total Size: 381 kB 📦 View Changed
|
6 tasks
Contributor
|
My proposal is not to introduce a notion of effects as they seem an overkill at the moment. Signaling that a command was activated can be seen from the Command activation
Snapshot API
By each manager implementing specific API, we follow already established pattern of the methods of the same name across the managers. And so |
Contributor
Author
|
As some general info, @MartinCupela and I decided offline how to proceed with this. Will update the description accordingly. |
MartinCupela
approved these changes
Apr 30, 2026
github-actions Bot
pushed a commit
that referenced
this pull request
Apr 30, 2026
## [9.43.0](v9.42.3...v9.43.0) (2026-04-30) ### Features * advanced commands flow and composer effects ([#1723](#1723)) ([5bcd1e6](5bcd1e6))
|
🎉 This PR is included in version 9.43.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
isekovanic
added a commit
to GetStream/stream-chat-react-native
that referenced
this pull request
Apr 30, 2026
## 🎯 Goal This PR is an extension of our commands flow to better reflect what's allowed and what's not on our backend. LLC PR with better explanation: GetStream/stream-chat-js#1723 General points: - Commands are disabled in certain scenarios: - When a quoted message is available, only `giphy` commands are allowed - When we're in an editing state, no commands are allowed (this is ignored server-side) - Adding a command when we have `composer` state already (specifically `text` and `attachments`), removes these but keeps a snapshot of the state so that if we remove the command we go back to the previous state - Adding `editing`/`quoted_message` state on already existing commands for which this is not allowed will remove them Trying to attach commands in a non-allowed state will also fire a notification so that the UI can react accordingly. ## 🛠 Implementation details <!-- Provide a description of the implementation --> ## 🎨 UI Changes <!-- Add relevant screenshots --> <details> <summary>iOS</summary> <table> <thead> <tr> <td>Before</td> <td>After</td> </tr> </thead> <tbody> <tr> <td> <!--<img src="" /> --> </td> <td> <!--<img src="" /> --> </td> </tr> </tbody> </table> </details> <details> <summary>Android</summary> <table> <thead> <tr> <td>Before</td> <td>After</td> </tr> </thead> <tbody> <tr> <td> <!--<img src="" /> --> </td> <td> <!--<img src="" /> --> </td> </tr> </tbody> </table> </details> ## 🧪 Testing <!-- Explain how this change can be tested (or why it can't be tested) --> ## ☑️ Checklist - [ ] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [ ] PR targets the `develop` branch - [ ] Documentation is updated - [ ] New code is tested in main example apps, including all possible scenarios - [ ] SampleApp iOS and Android - [ ] Expo iOS and Android
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CLA
Description of the changes, What, Why and How?
This PR refactors command activation and disabled command handling so command availability is evaluated from live
MessageComposerstate instead of being stored on command search results.Command disabled state depends on mutable composer state:
editingstateInitially, I had implemented this with a
disabledanddisabledReasonstate extension for the search source related to commands. However, this makes it super awkward for the UI SDKs as search results are not reactive (and we can't rely onsuggestionsfor things like the commands picker content). Because of that,disabled/disabledReasonhave been moved away fromCommandSearchSourceresults (keeping it purely CRUD). Since those results can be reused by different UI surfaces and they may become stale if composer state changes after the query runs, we rely purely on runtime logic for this.Instead, we expose 2 new APIs:
messageComposer.getCommandDisabledReason(command)messageComposer.isCommandDisabled(command)We check whether a command is disabled as early in the response as possible so that we prevent stale command objects from activating after composer state changes.
Additionally,
setQuotedMessage(...)andsetEditedMessage(...)clear the currently active command if the new composer state makes it invalid. This prevents the composer from remaining in a disallowed active command state after reply/edit state changes.Effects
For the purpose of achieving all of the above, we introduce
effectsas a concept to middleware execution. Effects are essentially deferred side-effects that need to happen in multiple different stores but triggered from one. For example, setting a command (i.etextComposer) needs to take care of clearingtext,mentionsandattachments- parts of the state which belong totextComposer.stateandattachmentManager.state. The easy solution here would be to forcetextComposerto also mutateattachmentManager(which we can do), however that's inherently wrong as even though we have access tocomposerwe should not introduce such tight coupling between the two.Command effects remain scoped to activation side effects only:
They do not control command availability. Removing command activation effects still opts out of snapshot/clear behavior, but disabled-command guards are evaluated separately at runtime.
As a note, if in the future we want to introduce
effectsthat are potentially concurrently unsafe - the proper way to solve this would be to introduce aneffectregistry that would then make sure that these are executed sequentially (or at the very least topologically, if it makes sense for theeffects). However for this iteration it seemed like a bit of an overkill to do that and so I decided to keep it simple.The definition of
effectshappens strictly withinmiddlewares. Their execution however, is tied tomessageComposerexplicitly. Customizingeffectshas many approaches. As an example:would remove all effects before they even reach execution.
The following:
overrides the default handling for
command.activateand adds acustom.effectthat does something arbitrary.Changelog