Skip to content

chore: upgrade the TypeScript demo apps to stream-chat-react 15.0.0-rc.3 - #138

Open
oliverlaz wants to merge 2 commits into
masterfrom
chore/stream-chat-react-v15-rc3
Open

oliverlaz wants to merge 2 commits into
masterfrom
chore/stream-chat-react-v15-rc3

Conversation

@oliverlaz

@oliverlaz oliverlaz commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

Upgrades the four TypeScript demo apps from stream-chat-react 14 to the v15 release candidate. The aim is to try the v15 / stream-chat v10 migration guides on real apps.

Changes

  • Dependencies: stream-chat-react@15.0.0-rc.3 and stream-chat@10.0.0-rc.26 in all four apps.
  • Node 22: stream-chat 10 requires >=22.18, so social-messenger-ts/.nvmrc and deploy.yml move to Node 22.
  • social-messenger-ts:
    • ChatView from stream-chat-react/slot-layout, with views, ChannelSlot / ThreadSlot and a thread slot renderer.
    • The channel list is a ChannelPaginator, created once above ChatView so switching views doesn't re-query it. The first channel opens on load only when nothing is open.
    • Components moved to workspace navigation, useChannel / useStateStore, and custom.name. The thread header uses useCloseThread.
    • CSS moved from str-chat__main-panel to the slot selectors.
  • team-ts:
    • No ChatView. The active channel and thread now live in the existing WorkspaceController, which provides WorkspaceNavigationProvider.
    • Each sidebar section has its own paginator. ChannelListUI is replaced by a wrapper around <ChannelList>.
    • The thread and pins panels render beside <Channel>, and .channel__container is now a flex row.
    • Also updated for stream-chat v10: getInstance config, queryChannelsAndHydrate, queryUsers({ payload }) and addMembers({ members }).
  • gaming-livestream:
    • The app now holds its single channel and the open thread in its own state, and provides WorkspaceNavigationProvider.
    • The thread still replaces the channel. That used to be a .str-chat__main-panel DOM toggle; it is now an app-owned wrapper, hidden while a thread is open.
    • The /ban, /mute, … notifications moved from overrideSubmitHandler to a sendMessageRequest handler.
    • Custom user fields (color, userRole) moved under custom; timestamps go through convertTimestampToDate.
  • virtual-event:
    • A new ChannelWithThread gives the event chat and the DM view each its own open thread. The thread replaces the channel in place, and switching channels drops it.
    • The DM list is a ChannelPaginator with the list slots and useChannelPaginatorState for errors.
    • Message UI: useMessagePaginator, workspace navigation for "Show in thread" (the parent now comes from client.getMessage), and Q&A upvotes via updateMessagePartial.
    • Also updated for stream-chat v10: custom data under custom, client.moderation.*, queryUsers({ payload }) and members as { user_id }.
  • v15-migration-log.md: a log of where the migration guides fell short, in two rounds (one per pair of apps).

Testing

  • tsc and CI=true yarn build pass for all four apps.
  • Clicked through each app against the staging app; social-messenger-ts, team-ts and virtual-event were compared with a v14 build from master:
    • social-messenger-ts: channels, threads, the Threads view and create-DM.
    • team-ts: channel switching, threads, pins, the admin edit panel and search.
    • gaming-livestream: sending, the timestamp toggle, threads and command notifications. Tested on a temporary messaging channel: gaming:gaming-demo doesn't exist on staging and the test user can't create it.
    • virtual-event: global, Q&A and room chats; threads and "Show in thread"; upvotes; reactions; the DM list; and starting a DM from a participant profile.
  • Not tested: phone-width layouts, failed sends, typing indicators, giphy, and the team-ts admin "create channel" submit.

Notes for reviewers

  • Mute and flag fail in virtual-event on staging. The v10 replacements (client.moderation.mute / unmuteUser / flagUser) return 403 "this endpoint needs a feature flag". Production needs that flag enabled before this ships. The app's modal still reports success, as it did on v14 when a call failed.
  • On v15 the CRA dev server shows a ResizeObserver loop completed with undelivered notifications overlay on load in social-messenger-ts and team-ts; v14 doesn't. It's dev-only and an SDK issue. Details are in the log.

…5.0.0-rc.3

- stream-chat-react 15.0.0-rc.3, stream-chat 10.0.0-rc.26
- Node 22 in .nvmrc and the deploy workflow (stream-chat 10 requires >=22.18)
- social-messenger-ts: ChatView slots, ChannelPaginator, workspace navigation
- team-ts: app-owned WorkspaceNavigationProvider, paginator-driven channel lists
- v15-migration-log.md: gaps found in the v15 / v10 migration guides
oliverlaz added a commit to GetStream/stream-chat-react that referenced this pull request Oct 29, 2026
…3356)

## Goal

Ticket: https://linear.app/stream/issue/REACT-1162

We migrated two demo apps from v14 to `15.0.0-rc.3`
(`stream-chat@10.0.0-rc.26`) using only
`ai-docs/ai-migration-v14-v15.md` and the stream-chat-js
`v10-breaking-changes.md`, and logged every place the guides fell short.
The findings and the migrated code are in
GetStream/website-react-examples#138. Every compile error had an answer
in the guides; the gaps are UI states that compile but behave
differently, mostly because `ChatView` unmounts the inactive view, plus
a few type-level holes. This PR fixes them in the guide. Docs only, and
it describes the current rc API.

## Implementation details

Each claim was checked against the source in this repo, and every new or
changed snippet compiles against the installed
`stream-chat-react@15.0.0-rc.3` / `stream-chat@10.0.0-rc.26` (strict,
`noImplicitReturns`, `noUnusedLocals`).

- **Section 3:** says the inactive `ChatView` view unmounts. Only the
slot bindings survive; state, refs and effects inside a view start over
on every switch.
- **Section 6, `useOpenFirstChannel`:** the `useRef` guard reset on
remount, so returning from the threads view re-opened the first channel
over the one the user had open. The hook now guards on
`useWorkspaceNavigation().openChannels.length === 0`. It also notes
where to call it (the channels view, not an always-rendered `ChatView`
child, where `openChannels` is empty in the threads view) and that it
re-opens when nothing is open.
- **Section 6, paginator example:** the paginator is created and
registered once, above `ChatView` (`useChannelPaginator`), so it is not
re-created and re-queried on every view switch.
- **Section 4, "Without ChatView":** the override list is split into
always / threads / default headers. It adds `isThreadDismissable` (the
default `ThreadHeader` close button needs it, and its default is `() =>
false`), `isChannelDismissable` with `closeChannel`, and `canGoBack`
with `goBack`. The example now overrides `isThreadDismissable`.
- **Section 5:** a concrete stale-state example
(`useState(channel.data?.custom?.name)` keeps the previous channel name)
and its fix.
- **Section 9:** reading `event.user` from the `typing` slice behind an
`EventPayload<'typing.start'>` type guard (`Event` is a union, so the
unguarded read fails with `TS2339`), plus a note that the guard can go
if `stream-chat` narrows the slice type.
- **Section 19:** a ready-to-run grep for the removed classes over
`*.css` / `*.scss` / `*.sass` / `*.less`, which also matches them inside
combinators (e.g. `.x + .str-chat__main-panel`) and skips
`.str-chat__main-panel-inner` and `.str-chat__thread-container`, which
still exist.
- **Silent-break table:** new rows for the channel resetting after a
view switch, the missing thread close button without `ChatView`, and
state seeded from the channel going stale; the CSS row now points at the
grep.
- **Smaller fixes:**
- Sections 7 and 10: warn that TypeScript's "Did you mean `ThreadHead`?"
for a stale `ThreadHeader` key points at the parent-message slot.
- Section 7: `useCloseThread` takes a `WorkspaceNavigationTriggerEvent`,
narrower than v14's `BaseSyntheticEvent`.
- Section 10: opens with "if your custom message UI calls
`getMessageActions()`" so those apps know to switch to `useUserRole`.
- Section 13: new subsection on `setMessageComposerSetupFunction` being
deprecated in favour of
`client.config.setSetupFunction('messageComposer', ...)`.
- Section 18: the flow "string not in the key map, so app-owned, so
`asDynamicKey`".
- Section 6: a custom `ChannelListItemUI` receives the computed `active`
prop.
- Section 1: reads the `stream-chat` peer range from the installed
`package.json` instead of a hard-coded rc, and lists the `stream-chat`
type changes that are usually the first errors (`ChannelSort` to
`SortParamRequest[]`, `queryChannels` raw vs `queryChannelsAndHydrate`,
`queryUsers({ payload })`).
- **Links:** the three links to stream-chat-js
`master/ai-docs/v10-breaking-changes.md` returned 404, because the file
lives on `release-v10` (GetStream/stream-chat-js#1919 merged there).
They now point at `release-v10` and use that guide's explicit anchors.

## UI Changes

None, documentation only.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **Documentation**
* Updated the v14-to-v15 migration guide with guidance on channel state
during view changes and workspace navigation, thread-header dismissal,
and custom channel-list setup.
* Revised examples for pagination and channel opening, and clarified
compatibility guidance, typing-event handling, CSS search, and
references to earlier migration documentation.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…ct 15.0.0-rc.3

- stream-chat-react 15.0.0-rc.3, stream-chat 10.0.0-rc.26
- gaming-livestream: app-owned channel and thread state with WorkspaceNavigationProvider,
  thread shown in place of the channel, command notifications via a sendMessageRequest handler
- virtual-event: ChannelWithThread per channel surface, paginator-driven DM list,
  v10 custom data, moderation, partial update and timestamp APIs
- v15-migration-log.md: round 2 gaps found in the v15 / v10 migration guides
@oliverlaz oliverlaz changed the title chore: upgrade social-messenger-ts and team-ts to stream-chat-react 15.0.0-rc.3 chore: upgrade the TypeScript demo apps to stream-chat-react 15.0.0-rc.3 Oct 29, 2026
oliverlaz added a commit to GetStream/stream-chat-react that referenced this pull request Oct 29, 2026
…rame (#3357)

Ticket: https://linear.app/stream/issue/REACT-1469

### Goal

Every page load of a v15 app whose channel list grows with its content
reports `ResizeObserver loop completed with undelivered notifications`.
Under CRA / webpack-dev-server this becomes a full-screen "Uncaught
runtime errors" overlay that blocks clicks. It was found while migrating
the team-ts and social-messenger-ts demos in website-react-examples to
15.0.0-rc.3 (GetStream/website-react-examples#138), and does not happen
on v14.

### Root cause

`InfiniteScrollPaginator` observes its root with a ResizeObserver and
checks the distance to the edges on every resize. #3342 changed that
check from a trailing `debounce` (always async) to a leading `throttle`,
so the observer's first callback now calls `loadNextOnScrollToBottom`
synchronously. The page load updates a `StateStore`, and the
`useSyncExternalStore` subscription renders synchronously in the
callback's microtasks. When the root is sized by its content (team-ts's
channel lists are), the root grows within the same ResizeObserver
delivery. The browser cannot deliver that resize in the same frame and
reports the loop error.

### How it was isolated

I ran a copy of team-ts against the rc.3 build with
`window.ResizeObserver` wrapped from a module imported first. The
wrapper recorded every callback's targets per animation frame and which
observed elements changed size during the callback and its microtasks.
The error appeared on 2 of 2 loads. Only the paginator's callback ever
resized an observed element: the channel list root went from 240x111 to
240x132 within its own callback, and the next frame delivered 240x132.
The `MessageList` relayout observer,
`useFloatingDateSeparatorMessageList` and `useScrollLocationLogic` never
resized an observed element.

### Implementation details

- The observer callback now schedules the check with
`requestAnimationFrame`, at most once per frame, and the frame is
cancelled on unmount. The resize the loaded page causes is then observed
in the following frame instead of within the current delivery.
- Scroll-triggered checks are unchanged and still run immediately.
- New tests in `InfiniteScrollPaginator.test.tsx` cover: no check inside
the observer callback (the check runs in the next frame), one check for
several resizes in a frame, and a pending check dropped when the
observer is replaced. Each fails when the code it covers is removed (the
deferral, the per-frame guard, the cancel in the cleanup).

Verified: with a local build of this branch in the same team-ts copy,
there was no loop error on 5 of 5 loads, and no observer callback
resized an observed element. `yarn lint`, `tsc -p tsconfig.lib.json
--noEmit` and `yarn test` (3,300 tests) pass.

Related: REACT-1439 (a content-height `ChannelList` loads every page on
mount). This PR does not change that behavior; it only moves the load
out of the observer callback.

### UI Changes

None visible, apart from the dev-server error overlay no longer
appearing.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved infinite-scroll behavior when content is resized: edge checks
now run on the next frame, and repeated resize events within that frame
trigger only one check.
* Pending checks are discarded when the observer configuration changes
or the component is cleaned up.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

This branch has not been deployed

No deployments
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