Skip to content

feat: UI sdk state cleanup in favor of llc - #3848

Merged
isekovanic merged 9 commits into
V10from
feat/ui-sdk-state-cleanup-in-favor-of-llc
Oct 9, 2026
Merged

isekovanic merged 9 commits into
V10from
feat/ui-sdk-state-cleanup-in-favor-of-llc

Conversation

@isekovanic

@isekovanic isekovanic commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

🎯 Goal

Linear ticket: https://linear.app/stream/issue/REACT-1218/remove-ui-state-duplicated-from-the-stream-chat-in-scrn

stream-chat v10 publishes channel, message, thread and composer state through reactive stores (channel.state, the message and channel paginators, client.threads, messageComposer). The SDK still kept its own copies of a lot of that state from the v9 model: context fields mirroring paginator state, event listeners rebuilding what the client already publishes, memo keys built from stringified state, and hand-written send/edit logic duplicating the composer.

This PR removes those copies and reads the stream-chat state directly. The UI is unchanged apart from the bug fixes listed below. It is a breaking change for integrators who used the removed context fields, props and exports; every one is listed with its replacement in ai-docs/ai-migration-v9-to-v10.md.

It is also part 1/2 of a larger cleanup pass in SCRN, which will come in a separate PR.

🛠 Implementation details

The work is split into one commit per area.

1. Dead code (8d149acd5)

  • Removed code with no callers left since the v10 rewrite: useSelectedChannelState, reduceMessagesToString, findInMessagesById, findInMessagesByDate, getLastReceivedMessage, DebugContext, the unused ChannelList hook utils, and offline-store functions nothing called.
  • Removed context fields and props that were never read or never set (isChannelActive, scrollToFirstUnreadThreshold, MessagesContext.quotedMessage, <Channel isOnline> and others).
  • useIsOnline is renamed to useConnectionLifecycle, since it returns nothing in v10.
  • useMutedChannels() no longer takes an argument.
  • OfflineStoreApis uses userId instead of currentUserId.

2. Message lists (ce99291d3)

  • MessageList and MessageFlashList page the paginator they render (the channel's, or the thread's in a thread list) and track their own loading flags. The loadMore / loadMoreRecent / hasMore props are gone.
  • The logic shared by both lists moved into hooks under MessageList/hooks.
  • Thread lists used to drive the channel's paginator for viewing-live state and the scroll-to-bottom jump. They now use the thread's. This fixes the "1 new messages" separator that stuck after returning from a thread.
  • A failed page no longer leaves that edge of the list unable to paginate.
  • Removing a message no longer scrolls MessageList to the bottom.

3. ChatContext (a04468e10)

  • ChatContext holds only client, getAppSettings and isMessageAIGenerated. channel, setActiveChannel, appSettings, mutedUsers and enableOfflineSupport are removed.
  • The context no longer rebuilds on mute changes or on returning from the background, so its ~65 consumers no longer re-render then.
  • getAppSettings() follows stream-chat-react: it fetches once per user, falls back to the offline database, and does not cache a failure.
  • <Chat> follows the connected user reactively. It used to read client.userID during render, so the offline database could miss a user that connected after mount.

4. <Channel> (0a75ced25)

  • frozen is read reactively, so disabled updates on freeze and unfreeze.
  • Contexts are keyed on the channel instance instead of its cid.
  • <Channel> no longer re-renders on channel updates that leave its capabilities unchanged, and overrideOwnCapabilities merges correctly.
  • typing.stop is sent on background only if the user was typing.
  • <Channel> keeps rendering a deleted channel, as in stream-chat-react. The app decides what to show from the channel's pendingDisposal state. The SampleApp does this in useLeaveGoneChannel.

5. Composer (5a7abceaa)

  • Sending and editing go through messageComposer.send() / update(). The SDK's own send and edit wrappers are removed.
  • <Channel preSendMessageRequest> is replaced by the sendMessageRequest request handler.
  • Failures come from client.notifications, mapped to the same toast copy as before.
  • The composer is resolved once in MessageComposerProvider. Each useMessageComposer() call used to create its own subscriptions.
  • An edit composer is re-initialised from the message each time. This fixes text left over from a cancelled edit showing up in the next one.
  • SendMessageDisallowedIndicator, the commands button and the commands sheet follow capabilities and config that arrive after mount.
  • Attachment picker cells re-render only when their own selection changes, not on every upload progress tick.
  • The moderation command rewrite (patchMessageTextCommand) is removed, since the backend now resolves the target from the mentioned users.

6. Channel list (5cf121467)

  • ChannelsContext holds the list's configuration and its ChannelPaginator. The query state (channels, loadingChannels, refreshing, ...) goes to ChannelListView as props. The memo key built from every channel's name, unread count and member presence is gone.
  • The list's FlatList sets strictMode. Channel rows now re-render only when their own channel changes, instead of all of them on every list update.
  • Config props (onSelect, getChannelActionItems, maxUnreadCount, ...) take effect when changed at runtime.
  • A mounted list reloads after disconnectUser() once a user connects again. It used to stay on its skeleton.
  • Channel rows show drafts that only have attachments.

7. Thread list (71a373a53)

  • A thread row reads its latest reply from thread.messagePaginator.aggregateState, as stream-chat-react does. It used to take the last reply of the loaded page, which is an older reply once the thread has been scrolled back.

🎨 UI Changes

/

🧪 Testing

/

☑️ Checklist

  • I have signed the Stream CLA (required)
  • PR targets the V10 branch
  • Documentation is updated (ai-docs/ai-migration-v9-to-v10.md, Part P)
  • New code is tested in main example apps, including all possible scenarios
    • SampleApp iOS and Android
    • Expo iOS and Android

@Stream-SDK-Bot

Copy link
Copy Markdown
Contributor

SDK Size

title develop branch diff status
js_bundle_size 2033 KB 2013 KB -20418 B 🚀

@isekovanic
isekovanic merged commit d1a29fe into V10 Oct 9, 2026
4 checks passed
@isekovanic
isekovanic deleted the feat/ui-sdk-state-cleanup-in-favor-of-llc branch October 9, 2026 14:29
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.

3 participants