Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
280b47d
feat: use LLC message pagination and message list state optimistic up…
MartinCupela Jan 12, 2026
1003896
Merge branch 'master' into feat/message-paginator
MartinCupela Jan 13, 2026
81f9689
feat(LayoutController): implement Core Types and Controller Engine an…
MartinCupela Feb 26, 2026
64ff386
feat(LayoutController): implement ChatView Integration (Context and P…
MartinCupela Feb 26, 2026
1bf06f6
feat(LayoutController): implement Built-in Two-Step DX Layout API
MartinCupela Feb 27, 2026
4c72159
feat(LayoutController): implement Docs and Spec Alignment
MartinCupela Feb 27, 2026
28f224a
chore(LayoutController): add new requirements to layout control API
MartinCupela Feb 27, 2026
3c3d0cf
feat(LayoutController): implement Slot Parent Stack and Back Navigation
MartinCupela Feb 27, 2026
95ea03a
feat(LayoutController): implement Unify ChannelList into Slot Model
MartinCupela Feb 27, 2026
8a3d6ba
feat(LayoutController): implement Min Slots and Fallback Workspace St…
MartinCupela Feb 27, 2026
f0a9ba2
feat(LayoutController): implement Generic Slot Component with Mount-P…
MartinCupela Feb 27, 2026
3223e46
feat(LayoutController): implement Deep-Linking, Serialization, and op…
MartinCupela Feb 27, 2026
bad6681
feat(LayoutController): implement High-Level Navigation Hook and Cont…
MartinCupela Feb 27, 2026
862a0ed
feat(LayoutController): adjust Thread and ThreadContext.tsx to be ind…
MartinCupela Feb 27, 2026
4b28a74
feat(LayoutController): implement Thread Component Layout-Controller …
MartinCupela Feb 27, 2026
55a7e37
feat(LayoutController): add translations to "aria/Go back"
MartinCupela Feb 27, 2026
f5f328e
feat(LayoutController): implement Slot Self-Visibility from Slot Prop…
MartinCupela Feb 27, 2026
3a0efb6
refactor(channel): remove thread pagination from channel contexts
MartinCupela Feb 28, 2026
1e42b28
feat: remove non-pagination state from ChannelStateContext
MartinCupela Mar 3, 2026
8cacbb3
Merge branch 'master' into feat/message-paginator
MartinCupela Mar 3, 2026
9680b7a
Merge branch 'master' into feat/chat-view-layout
MartinCupela Mar 4, 2026
e36f496
feat(chatview): remove ChatContext active channel/nav routing; add us…
MartinCupela Mar 4, 2026
1e5d9ae
refactor: remove Hardcoded `channel.messagePaginator` Usage from Reac…
MartinCupela Mar 4, 2026
98231e5
feat: remove ChannelActionContext
MartinCupela Mar 4, 2026
5a81ea1
feat: keep suppressAutoscrol management inside message lists
MartinCupela Mar 4, 2026
deabbcb
feat: port highlighted message functionality and use client notificat…
MartinCupela Mar 5, 2026
2fd3327
feat: adapt bootstrap/loading/error guards, listener lifecycle, mark-…
MartinCupela Mar 5, 2026
e5d9d1b
fix: clear channelUnreadUiState upon clicking on UnreadMessagesSepara…
MartinCupela Mar 5, 2026
4b54c38
fix: prevent showing NewMessageNotification & ScrollToLatestMessageBu…
MartinCupela Mar 5, 2026
3e83138
fix: make sure to scroll to the bottom, when the first batch of messa…
MartinCupela Mar 5, 2026
7431d07
fix: update reply count in MessageRepliesCountButton
MartinCupela Mar 5, 2026
1959233
fix: reflect deleted message text in QuotedMessagePreview
MartinCupela Mar 5, 2026
ff1cb05
fix: show cursor pointer when hovering above QuotedMessage in message…
MartinCupela Mar 6, 2026
429500b
fix: make the UnreadMessagesSeparator driven by MessagePaginator unre…
MartinCupela Mar 6, 2026
fb48d09
feat: make slot layout for threads and channels
MartinCupela Mar 6, 2026
bcca6e7
Merge branch 'master' into feat/message-paginator
MartinCupela Mar 6, 2026
7bd80a1
Merge branch 'pr-2909' into feat/message-paginator-master-merge
MartinCupela Jul 8, 2026
ecc98c1
feat: introduce additive opening in a secondary slot
MartinCupela Jul 8, 2026
0faca8e
feat: flatten the Channel DOM structure and remove Window component
MartinCupela Jul 8, 2026
156b609
feat: replace ChannelListContext contents with paginator
MartinCupela Jul 9, 2026
174b6af
refactor: remove Channel component memoization
MartinCupela Jul 9, 2026
bbec104
chore(demo): prevent floating message list's date separator to be ren…
MartinCupela Jul 9, 2026
e01230b
fix: add audio player pool for Thread
MartinCupela Jul 9, 2026
f7b4996
feat: add slot geometry plugin
MartinCupela Jul 9, 2026
1db47b8
refactor: extract deeplink navigation between sent-in-channel and thr…
MartinCupela Jul 9, 2026
4fade8b
feat: add layers to the slot system
MartinCupela Jul 9, 2026
70f3175
feat: add reveal intent to the slot geometry
MartinCupela Jul 10, 2026
db2f518
refactor: drop unnecessary channel query when jumping to a reply in c…
MartinCupela Jul 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions examples/tutorial/src/2-core-component-setup/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
MessageList,
Thread,
useCreateChatClient,
Window,
} from 'stream-chat-react';

import 'stream-chat-react/dist/css/index.css';
Expand Down Expand Up @@ -47,11 +46,9 @@ const App = () => {
return (
<Chat client={client}>
<Channel channel={channel}>
<Window>
<ChannelHeader />
<MessageList />
<MessageComposer />
</Window>
<ChannelHeader />
<MessageList />
<MessageComposer />
<Thread />
</Channel>
</Chat>
Expand Down
68 changes: 52 additions & 16 deletions examples/tutorial/src/3-channel-list/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import type { ChannelFilters, ChannelOptions, ChannelSort, User } from 'stream-chat';
import { useMemo } from 'react';
import type { ChannelFilters, ChannelSort, User } from 'stream-chat';
import { ChannelPaginator, ChannelPaginatorsOrchestrator } from 'stream-chat';
import {
Channel,
ChannelHeader,
ChannelList,
ChannelNavigation,
Chat,
ChatView,
MessageComposer,
MessageList,
Thread,
useCreateChatClient,
Window,
useSlotChannels,
} from 'stream-chat-react';

import './layout.css';
Expand All @@ -25,8 +28,30 @@ const filters: ChannelFilters = {
type: 'messaging',
members: { $in: [userId] },
};
const options: ChannelOptions = {
limit: 10,

// One view ("channels") with a single channel slot. Module-scoped so the reference is
// stable (it feeds the ChatView layout controller).
const chatViewLayouts = [{ id: 'channels' as const, slots: ['main-channel'] }];

// Renders the channel navigation (list + search) and the channel(s) currently open in
// a layout slot. Selecting a channel in the list binds it into a slot via ChatView
// navigation; `useSlotChannels` reads back the open channel(s).
const ChannelsWorkspace = () => {
const channelSlots = useSlotChannels();

return (
<>
<ChannelNavigation />
{channelSlots.map(({ channel, slot }) => (
<Channel channel={channel} key={slot}>
<ChannelHeader />
<MessageList />
<MessageComposer />
<Thread />
</Channel>
))}
</>
);
};

const App = () => {
Expand All @@ -36,19 +61,30 @@ const App = () => {
userData: user,
});

if (!client) return <div>Setting up client & connection...</div>;
// Channel-list query config (filters/sort) now lives on a `ChannelPaginator`,
// coordinated by the `ChannelPaginatorsOrchestrator` passed to `<Chat>`.
const channelPaginatorsOrchestrator = useMemo(
() =>
client &&
new ChannelPaginatorsOrchestrator({
client,
paginators: [
new ChannelPaginator({ client, filters, id: 'channels:default', sort }),
],
}),
[client],
);

if (!client || !channelPaginatorsOrchestrator)
return <div>Setting up client & connection...</div>;

return (
<Chat client={client} theme='custom-theme'>
<ChannelList filters={filters} options={options} sort={sort} />
<Channel>
<Window>
<ChannelHeader />
<MessageList />
<MessageComposer />
</Window>
<Thread />
</Channel>
<Chat
channelPaginatorsOrchestrator={channelPaginatorsOrchestrator}
client={client}
theme='custom-theme'
>
<ChatView layouts={chatViewLayouts} views={{ channels: <ChannelsWorkspace /> }} />
</Chat>
);
};
Expand Down
64 changes: 32 additions & 32 deletions examples/tutorial/src/4-custom-ui-components/App.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import React, { useEffect, useState } from 'react';
import type { ChannelFilters, ChannelOptions, ChannelSort, User } from 'stream-chat';
import type { User } from 'stream-chat';
import {
Channel,
ChannelAvatar,
ChannelHeader,
ChannelList,
type ChannelListItemUIProps,
ChannelNavigation,
Chat,
ChatView,
MessageComposer,
MessageList,
Thread,
useChatViewNavigation,
useCreateChatClient,
useMessageContext,
Window,
useSlotChannels,
WithComponents,
} from 'stream-chat-react';

Expand All @@ -25,37 +27,22 @@ const user: User = {
image: `https://getstream.io/random_png/?name=${userName}`,
};

const sort: ChannelSort = { last_message_at: -1 };
const filters: ChannelFilters = {
type: 'messaging',
members: { $in: [userId] },
};
const options: ChannelOptions = {
limit: 10,
};

const CustomChannelListItem = ({
active,
channel,
displayImage,
displayTitle,
latestMessagePreview,
onSelect,
setActiveChannel,
}: ChannelListItemUIProps) => {
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
if (onSelect) {
onSelect(event);
return;
}

setActiveChannel?.(channel, undefined, event);
};
// Selection is one navigation model: open the channel into a layout slot.
const { open } = useChatViewNavigation();

return (
<button
aria-pressed={active}
onClick={handleClick}
onClick={() =>
open({ key: channel.cid ?? undefined, kind: 'channel', source: channel })
}
style={{
width: '100%',
padding: '12px',
Expand Down Expand Up @@ -114,6 +101,27 @@ const CustomMessage = () => {
);
};

// One view ("channels") with a single channel slot. Module-scoped for a stable reference.
const chatViewLayouts = [{ id: 'channels' as const, slots: ['main-channel'] }];

const ChannelsWorkspace = () => {
const channelSlots = useSlotChannels();

return (
<>
<ChannelNavigation />
{channelSlots.map(({ channel, slot }) => (
<Channel channel={channel} key={slot}>
<ChannelHeader />
<MessageList />
<MessageComposer />
<Thread />
</Channel>
))}
</>
);
};

const App = () => {
const [isReady, setIsReady] = useState(false);
const client = useCreateChatClient({
Expand Down Expand Up @@ -152,15 +160,7 @@ const App = () => {
}}
>
<Chat client={client} theme='custom-theme'>
<ChannelList filters={filters} options={options} sort={sort} />
<Channel>
<Window>
<ChannelHeader />
<MessageList />
<MessageComposer />
</Window>
<Thread />
</Channel>
<ChatView layouts={chatViewLayouts} views={{ channels: <ChannelsWorkspace /> }} />
</Chat>
</WithComponents>
);
Expand Down
9 changes: 3 additions & 6 deletions examples/tutorial/src/5-custom-attachment-type/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
MessageList,
Thread,
useCreateChatClient,
Window,
WithComponents,
} from 'stream-chat-react';

Expand Down Expand Up @@ -118,11 +117,9 @@ const App = () => {
<WithComponents overrides={{ Attachment: CustomAttachment }}>
<Chat client={client} theme='custom-theme'>
<Channel channel={channel}>
<Window>
<ChannelHeader />
<MessageList />
<MessageComposer />
</Window>
<ChannelHeader />
<MessageList />
<MessageComposer />
<Thread />
</Channel>
</Chat>
Expand Down
44 changes: 26 additions & 18 deletions examples/tutorial/src/6-emoji-picker/App.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { useEffect, useState } from 'react';
import type { ChannelFilters, ChannelSort, User } from 'stream-chat';
import type { User } from 'stream-chat';
import {
Channel,
ChannelHeader,
ChannelList,
ChannelNavigation,
Chat,
ChatView,
MessageComposer,
MessageList,
Thread,
useCreateChatClient,
Window,
useSlotChannels,
WithComponents,
} from 'stream-chat-react';
import { EmojiPicker } from 'stream-chat-react/emojis';
Expand All @@ -26,14 +27,29 @@ const user: User = {
image: `https://getstream.io/random_png/?name=${userName}`,
};

const sort: ChannelSort = { last_message_at: -1 };
const filters: ChannelFilters = {
type: 'messaging',
members: { $in: [userId] },
};

init({ data });

// One view ("channels") with a single channel slot. Module-scoped for a stable reference.
const chatViewLayouts = [{ id: 'channels' as const, slots: ['main-channel'] }];

const ChannelsWorkspace = () => {
const channelSlots = useSlotChannels();

return (
<>
<ChannelNavigation />
{channelSlots.map(({ channel, slot }) => (
<Channel channel={channel} key={slot}>
<ChannelHeader />
<MessageList />
<MessageComposer emojiSearchIndex={SearchIndex} />
<Thread />
</Channel>
))}
</>
);
};

const App = () => {
const [isReady, setIsReady] = useState(false);
const client = useCreateChatClient({
Expand Down Expand Up @@ -67,15 +83,7 @@ const App = () => {
return (
<Chat client={client}>
<WithComponents overrides={{ EmojiPicker }}>
<ChannelList filters={filters} sort={sort} />
<Channel>
<Window>
<ChannelHeader />
<MessageList />
<MessageComposer emojiSearchIndex={SearchIndex} />
</Window>
<Thread />
</Channel>
<ChatView layouts={chatViewLayouts} views={{ channels: <ChannelsWorkspace /> }} />
</WithComponents>
</Chat>
);
Expand Down
9 changes: 3 additions & 6 deletions examples/tutorial/src/7-livestream/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
MessageComposer,
useCreateChatClient,
VirtualizedMessageList,
Window,
} from 'stream-chat-react';

import './layout.css';
Expand Down Expand Up @@ -51,11 +50,9 @@ const App = () => {
return (
<Chat client={chatClient} theme='str-chat__theme-dark'>
<Channel channel={channel}>
<Window>
<ChannelHeader />
<VirtualizedMessageList />
<MessageComposer focus />
</Window>
<ChannelHeader />
<VirtualizedMessageList />
<MessageComposer focus />
</Channel>
</Chat>
);
Expand Down
Loading