Skip to content

feat: use configuration service API as a single source of truth - #3273

Open
MartinCupela wants to merge 5 commits into
release-v15from
feat/llc-instance-configuration
Open

feat: use configuration service API as a single source of truth#3273
MartinCupela wants to merge 5 commits into
release-v15from
feat/llc-instance-configuration

Conversation

@MartinCupela

Copy link
Copy Markdown
Contributor

What

The React side of the LLC configuration work (companion to stream-chat-js#…). Depends on GetStream/stream-chat-js#1831.

Two things: components now read the resolved configuration instead of the channel type's raw server
flags, and the per-component request-handler props are gone in favour of registering handlers once on
client.config.

Reading resolved configuration

The LLC now combines each server flag with whatever is registered through client.config, and that
combined value is what it enforces. Components were reading only the server's half, so a menu could offer
an action the composer had already disabled.

useChannelConfig now returns the channel's resolved configuration rather than the raw server config, so
field names change with it:

channelConfig?.typing_events            channelConfig?.typingEvents.enabled
channelConfig?.read_events              channelConfig?.readEvents.enabled
channelConfig?.replies                  channelConfig?.replies.enabled
channelConfig?.user_message_reminders   channelConfig?.userMessageReminders.enabled
channelConfig?.commands                 channelConfig?.availableCommands

useAttachmentManagerState gained the composer-resolved gates — attachmentsEnabled, locationEnabled,
pollsEnabled, customCdn — which let AttachmentSelector drop a duplicate selector of its own.

Both hooks subscribe to the configuration. These values are getters on the LLC instances, so nothing
re-rendered when they changed; a client.config.set() did not reach the screen.

useMarkRead moved from channel.getConfig()?.read_events to the resolved readEvents.enabled, so it
now honours a client-side disable and re-runs when it changes — a plain method call could not, being
outside React's dependency graph.

After this, no component reads channel.serverConfig.

Removing the request-handler props

doSendMessageRequest, doUpdateMessageRequest, doDeleteMessageRequest and doMarkReadRequest are
removed from Channel and Thread, along with the two hooks and the ownership coordinator behind them.

The props and client.config wrote to the same slot, so the SDK carried a coordinator that tracked which
mounted component owned each handler, restored the previous owner on unmount, and re-applied everything
whenever the LLC re-derived. All of that existed to reconcile two ways of doing one thing. With one owner
there is nothing to arbitrate — 827 deletions against 17 insertions.

useChannelEditMessageHandler went with them: it existed to apply doUpdateMessageRequest to the edit
path and wrapped nothing once that prop was gone.

Also removed, in its own commit: adaptMessageSendErrorToErrorFromResponse. It normalised a network error
into a StreamAPIError when Channel.tsx did the sending. The send path moved into the LLC, which
produces that shape itself, and the call site went with it — leaving the function with no callers, no
tests, and no reachable import path.

⚠️ Breaking changes

Details in ai-docs/ai-migration-v14-v15.md.

Request handlers move to client.config:

// v14
<Channel channel={channel} doSendMessageRequest={mySend}>

// v15
client.config.set({
  channel: {
    requestHandlers: {
      sendMessageRequest: async ({ localMessage, message, options }) => ({
        message: await mySend(message, options),
      }),
    },
  },
});

Three differences: handlers take a single params object and return { message }; thread flows register
under the thread key; and registration is per client, not per mounted subtree — the one thing the
props could do that this cannot. Different behaviour per channel now needs a branch inside one handler on
the cid it receives.

Also: useChannelConfig's field names (above); useAttachmentManagerState replaces
hasCustomDoUploadRequest with customCdn, which answers the question that flag was only ever a proxy
for; useChannelEditMessageHandler removed.

Note for tests: channel.getConfig() is now the channel.serverConfig getter, so
vi.spyOn(channel, 'getConfig') has no equivalent — set the channel type's config on
client.channelConfigsByTypeStore instead, which also drives the derivation so channel.config is
correct.

Example app

examples/vite gains a Configuration tab (+1803 lines): the resolved tree per scope, inline editors,
a reference of every configurable path, and a view of what is actually registered versus what resolved.
It was the main tool for exercising this end to end. Also a fullscreen toggle for the settings modal and a
contrast fix on its buttons.

Not ready to merge yet

stream-chat is pinned to 10.0.0-rc.2 in package.json (peer and dev) and examples/vite/package.json.
That tag predates this API, so CI will fail to typecheck — it installs from npm, whereas this passes
locally through a symlink to the LLC worktree. Needs the LLC PR merged, a new rc published, and a
chore(deps) bump.

# Conflicts:
#	examples/tutorial/src/3-channel-list/App.tsx
#	examples/tutorial/src/7-livestream/App.tsx
#	examples/vite/src/App.tsx
#	examples/vite/src/ChatLayout/SwitchableChannelNavigation.tsx
#	examples/vite/src/SingleChannel/SingleChannelApp.tsx
#	src/components/ChannelList/ChannelList.tsx
#	src/components/Chat/Chat.tsx
#	src/context/ChatContext.tsx
# Conflicts:
#	ai-docs/ai-migration-v14-v15.md
#	examples/vite/src/AppSettings/tabs/General/GeneralTab.tsx
#	src/components/Channel/hooks/useChannelConfig.ts
#	src/components/Thread/ThreadHeader.tsx
@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e3ad6c68-1667-4267-bc6b-d8aec9d5721c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

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.

1 participant