Skip to content

Commit 2932ba1

Browse files
authored
fix: revert "feat: expose channels state on chat level (#2161)" (v10) (#2194)
1 parent 02d233b commit 2932ba1

File tree

10 files changed

+79
-186
lines changed

10 files changed

+79
-186
lines changed

docusaurus/docs/React/components/contexts/chat-context.mdx

Lines changed: 0 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,6 @@ The currently active channel, which populates the [`Channel`](../core-components
3333
| ------- |
3434
| Channel |
3535

36-
### channels
37-
38-
State representing the array of loaded channels. Channels query is executed by default only within the [`ChannelList` component](../core-components/channel-list.mdx) in the SDK.
39-
40-
| Type |
41-
|-------------|
42-
| `Channel[]` |
43-
4436
### channelsQueryState
4537

4638
Exposes API that:
@@ -127,76 +119,6 @@ You can override the default behavior by pulling it from context and then utiliz
127119
| -------- |
128120
| function |
129121

130-
### setChannels
131-
132-
Sets the list of `Channel` objects to be rendered by `ChannelList` component. One have to be careful, when to call `setChannels` as the first channels query executed by the `ChannelList` overrides the whole [`channels` state](#channels). In that case it is better to subscribe to `client` event `channels.queried` and only then set the channels.
133-
In the following example, we have a component that sets the active channel based on the id in the URL. It waits until the first channels page is loaded, and then it sets the active channel. If the channel is not present on the first page, it performs additional API request with `getChannel()`:
134-
135-
```tsx
136-
import {useEffect} from 'react';
137-
import {useNavigate, useParams} from 'react-router-dom';
138-
import {ChannelList, getChannel, useChatContext} from 'stream-chat-react';
139-
import {ChannelFilters, ChannelOptions, ChannelSort, Event} from 'stream-chat';
140-
141-
const DEFAULT_CHANNEL = 'general';
142-
const CHANNEL_TYPE = 'messaging';
143-
144-
export const ChannelListWrapper = () => {
145-
const { channelId } = useParams();
146-
const navigate = useNavigate();
147-
const { client, channel, setActiveChannel, setChannels } = useChatContext();
148-
149-
const filters: ChannelFilters = { type: CHANNEL_TYPE, members: { $in: [client.user?.id || ''] } };
150-
const options: ChannelOptions = { state: true, presence: true, limit: 10 };
151-
const sort: ChannelSort = { last_message_at: -1, updated_at: -1 };
152-
153-
// set active channel only if URL param changed
154-
useEffect(() => {
155-
if (!channelId) return navigate(`/${DEFAULT_CHANNEL}`);
156-
157-
if (channel?.id === channelId || !client) return;
158-
159-
let subscription: { unsubscribe: () => void } | undefined;
160-
if(!channel?.id || channel?.id !== channelId) {
161-
subscription = client.on('channels.queried', (event: Event) => {
162-
// check, whether the channel has already been loaded with the first page
163-
const loadedChannelData = event.queriedChannels?.channels.find((response) => response.channel.id === channelId);
164-
165-
if (loadedChannelData) {
166-
setActiveChannel(client.channel( CHANNEL_TYPE, channelId));
167-
subscription?.unsubscribe();
168-
return;
169-
}
170-
171-
return getChannel({client, id: channelId, type: CHANNEL_TYPE}).then((newActiveChannel) => {
172-
setActiveChannel(newActiveChannel);
173-
setChannels((channels) => {
174-
return ([newActiveChannel, ...channels.filter((ch) => ch.data?.cid !== newActiveChannel.data?.cid)]);
175-
});
176-
});
177-
});
178-
}
179-
180-
return () => {
181-
subscription?.unsubscribe();
182-
};
183-
}, [channel?.id, channelId, setChannels, client, navigate, setActiveChannel]);
184-
185-
return (
186-
<ChannelList
187-
setActiveChannelOnMount={false}
188-
filters={filters}
189-
sort={sort}
190-
options={options}
191-
/>
192-
);
193-
};
194-
```
195-
196-
| Type |
197-
|---------------------------------------|
198-
| `Dispatch<SetStateAction<Channel[]>>` |
199-
200122
### theme
201123

202124
Deprecated and to be removed in a future major release. Use the `customStyles` prop to adjust CSS variables and [customize the theme](../../guides/theming/css-and-theming.mdx#css-variables) of your app.

src/components/ChannelList/__tests__/ChannelList.test.js

Lines changed: 71 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,6 @@ const channelsQueryStateMock = {
4848
setQueryInProgress: jest.fn(),
4949
};
5050

51-
const ChatContextOverrider = ({ chatContext, children }) => {
52-
const existingContext = useChatContext();
53-
return (
54-
<ChatContext.Provider
55-
value={{
56-
...existingContext,
57-
channelsQueryState: channelsQueryStateMock,
58-
...chatContext,
59-
}}
60-
>
61-
{children}
62-
</ChatContext.Provider>
63-
);
64-
};
65-
6651
/**
6752
* We use the following custom UI components for preview and list.
6853
* If we use ChannelPreviewMessenger or ChannelPreviewLastMessage here, then changes
@@ -131,7 +116,6 @@ describe('ChannelList', () => {
131116
client: chatClient,
132117
closeMobileNav,
133118
navOpen: true,
134-
setChannels: jest.fn(),
135119
}}
136120
>
137121
<ChannelList {...props} />
@@ -163,7 +147,6 @@ describe('ChannelList', () => {
163147
client: chatClient,
164148
closeMobileNav,
165149
navOpen: false,
166-
setChannels: jest.fn(),
167150
}}
168151
>
169152
<ChannelList {...props} />
@@ -409,7 +392,6 @@ describe('ChannelList', () => {
409392

410393
describe('Default and custom active channel', () => {
411394
let setActiveChannel;
412-
let setChannels;
413395
const watchersConfig = { limit: 20, offset: 0 };
414396
const testSetActiveChannelCall = (channelInstance) =>
415397
waitFor(() => {
@@ -420,7 +402,6 @@ describe('ChannelList', () => {
420402

421403
beforeEach(() => {
422404
setActiveChannel = jest.fn();
423-
setChannels = jest.fn();
424405
useMockedApis(chatClient, [queryChannelsApi([testChannel1, testChannel2])]);
425406
});
426407

@@ -431,7 +412,6 @@ describe('ChannelList', () => {
431412
channelsQueryState: channelsQueryStateMock,
432413
client: chatClient,
433414
setActiveChannel,
434-
setChannels,
435415
}}
436416
>
437417
<ChannelList
@@ -463,7 +443,6 @@ describe('ChannelList', () => {
463443
channelsQueryState: channelsQueryStateMock,
464444
client: chatClient,
465445
setActiveChannel,
466-
setChannels,
467446
}}
468447
>
469448
<ChannelList
@@ -487,40 +466,41 @@ describe('ChannelList', () => {
487466
});
488467

489468
it('should render channel with id `customActiveChannel` at top of the list', async () => {
490-
useMockedApis(chatClient, [getOrCreateChannelApi(testChannel2)]);
491-
jest
492-
.spyOn(chatClient, 'queryChannels')
493-
.mockImplementationOnce(() =>
494-
chatClient.hydrateActiveChannels([testChannel1, testChannel2]),
495-
);
496-
await act(async () => {
497-
await render(
498-
<Chat client={chatClient}>
499-
<ChannelList
500-
customActiveChannel={testChannel2.channel.id}
501-
filters={{}}
502-
List={ChannelListComponent}
503-
options={{ presence: true, state: true, watch: true }}
504-
Preview={ChannelPreviewComponent}
505-
watchers={watchersConfig}
506-
/>
507-
</Chat>,
508-
);
509-
});
469+
const { container, getAllByRole, getByRole, getByTestId } = render(
470+
<ChatContext.Provider
471+
value={{
472+
channelsQueryState: channelsQueryStateMock,
473+
client: chatClient,
474+
setActiveChannel,
475+
}}
476+
>
477+
<ChannelList
478+
customActiveChannel={testChannel2.channel.id}
479+
filters={{}}
480+
List={ChannelListComponent}
481+
options={{ presence: true, state: true, watch: true }}
482+
Preview={ChannelPreviewComponent}
483+
setActiveChannel={setActiveChannel}
484+
setActiveChannelOnMount
485+
watchers={watchersConfig}
486+
/>
487+
</ChatContext.Provider>,
488+
);
510489

511490
// Wait for list of channels to load in DOM.
512-
await waitFor(() => {
513-
expect(screen.getByRole('list')).toBeInTheDocument();
514-
const items = screen.getAllByRole('listitem');
491+
await waitFor(async () => {
492+
expect(getByRole('list')).toBeInTheDocument();
493+
const items = getAllByRole('listitem');
515494

516495
// Get the closest listitem to the channel that received new message.
517-
const channelPreview = screen
518-
.getByTestId(testChannel2.channel.id)
519-
.closest(ROLE_LIST_ITEM_SELECTOR);
496+
const channelPreview = getByTestId(testChannel2.channel.id).closest(
497+
ROLE_LIST_ITEM_SELECTOR,
498+
);
520499

521500
expect(channelPreview.isEqualNode(items[0])).toBe(true);
501+
const results = await axe(container);
502+
expect(results).toHaveNoViolations();
522503
});
523-
jest.restoreAllMocks();
524504
});
525505

526506
describe('channel search', () => {
@@ -555,16 +535,20 @@ describe('ChannelList', () => {
555535

556536
const renderComponents = (chatContext = {}, channeListProps) =>
557537
render(
558-
<Chat client={chatContext.client}>
559-
<ChatContextOverrider chatContext={{ ...chatContext, setActiveChannel }}>
560-
<ChannelList
561-
filters={{}}
562-
options={{ presence: true, state: true }}
563-
showChannelSearch
564-
{...channeListProps}
565-
/>
566-
</ChatContextOverrider>
567-
</Chat>,
538+
<ChatContext.Provider
539+
value={{
540+
channelsQueryState: channelsQueryStateMock,
541+
setActiveChannel,
542+
...chatContext,
543+
}}
544+
>
545+
<ChannelList
546+
filters={{}}
547+
options={{ presence: true, state: true }}
548+
showChannelSearch
549+
{...channeListProps}
550+
/>
551+
</ChatContext.Provider>,
568552
);
569553

570554
it.each([['1'], ['2']])(
@@ -1209,20 +1193,19 @@ describe('ChannelList', () => {
12091193
it('should unset activeChannel if it was deleted', async () => {
12101194
const setActiveChannel = jest.fn();
12111195
const { container, getByRole } = render(
1212-
<Chat client={chatClient}>
1213-
<ChatContextOverrider
1214-
chatContext={{
1215-
channelsQueryState: channelsQueryStateMock,
1216-
setActiveChannel,
1217-
}}
1218-
>
1219-
<ChannelList
1220-
{...channelListProps}
1221-
channel={{ cid: testChannel1.channel.cid }}
1222-
setActiveChannel={setActiveChannel}
1223-
/>
1224-
</ChatContextOverrider>
1225-
</Chat>,
1196+
<ChatContext.Provider
1197+
value={{
1198+
channelsQueryState: channelsQueryStateMock,
1199+
client: chatClient,
1200+
setActiveChannel,
1201+
}}
1202+
>
1203+
<ChannelList
1204+
{...channelListProps}
1205+
channel={{ cid: testChannel1.channel.cid }}
1206+
setActiveChannel={setActiveChannel}
1207+
/>
1208+
</ChatContext.Provider>,
12261209
);
12271210

12281211
// Wait for list of channels to load in DOM.
@@ -1274,21 +1257,32 @@ describe('ChannelList', () => {
12741257
});
12751258

12761259
it('should unset activeChannel if it was hidden', async () => {
1260+
const setActiveChannel = jest.fn();
12771261
const { container, getByRole } = render(
1278-
<Chat client={chatClient}>
1279-
<ChannelList {...channelListProps} />
1280-
</Chat>,
1262+
<ChatContext.Provider
1263+
value={{
1264+
channelsQueryState: channelsQueryStateMock,
1265+
client: chatClient,
1266+
setActiveChannel,
1267+
}}
1268+
>
1269+
<ChannelList
1270+
{...channelListProps}
1271+
channel={{ cid: testChannel1.channel.cid }}
1272+
setActiveChannel={setActiveChannel}
1273+
/>
1274+
</ChatContext.Provider>,
12811275
);
12821276

1277+
// Wait for list of channels to load in DOM.
12831278
await waitFor(() => {
1284-
expect(screen.getByTestId(testChannel1.channel.id)).toBeInTheDocument();
12851279
expect(getByRole('list')).toBeInTheDocument();
12861280
});
12871281

12881282
act(() => dispatchChannelHiddenEvent(chatClient, testChannel1.channel));
12891283

12901284
await waitFor(() => {
1291-
expect(screen.queryByTestId(testChannel1.channel.id)).not.toBeInTheDocument();
1285+
expect(setActiveChannel).toHaveBeenCalledTimes(1);
12921286
});
12931287
const results = await axe(container);
12941288
expect(results).toHaveNoViolations();

src/components/ChannelList/hooks/usePaginatedChannels.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@ export const usePaginatedChannels = <
2626
recoveryThrottleIntervalMs: number = RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS,
2727
) => {
2828
const {
29-
channels,
3029
channelsQueryState: { error, setError, setQueryInProgress },
31-
setChannels,
32-
} = useChatContext<StreamChatGenerics>('usePaginatedChannels');
33-
30+
} = useChatContext('usePaginatedChannels');
31+
const [channels, setChannels] = useState<Array<Channel<StreamChatGenerics>>>([]);
3432
const [hasNextPage, setHasNextPage] = useState(true);
3533
const lastRecoveryTimestamp = useRef<number | undefined>();
3634

@@ -117,7 +115,6 @@ export const usePaginatedChannels = <
117115
queryChannels('reload');
118116
}, [filterString, sortString]);
119117

120-
// FIXME: state refactor (breaking change) is needed - do not forward `channels` and `setChannel`
121118
return {
122119
channels,
123120
hasNextPage,

src/components/Chat/Chat.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,13 @@ export const Chat = <
8484

8585
const {
8686
channel,
87-
channels,
8887
closeMobileNav,
8988
getAppSettings,
9089
latestMessageDatesByChannels,
9190
mutes,
9291
navOpen,
9392
openMobileNav,
9493
setActiveChannel,
95-
setChannels,
9694
translators,
9795
} = useChat({ client, defaultLanguage, i18nInstance, initialNavOpen });
9896

@@ -109,7 +107,6 @@ export const Chat = <
109107

110108
const chatContextValue = useCreateChatContext({
111109
channel,
112-
channels,
113110
channelsQueryState,
114111
client,
115112
closeMobileNav,
@@ -120,7 +117,6 @@ export const Chat = <
120117
navOpen,
121118
openMobileNav,
122119
setActiveChannel,
123-
setChannels,
124120
theme,
125121
themeVersion,
126122
useImageFlagEmojisOnWindows,

0 commit comments

Comments
 (0)