Skip to content

Commit fe44060

Browse files
committed
feat: implement channel manager poc
1 parent 4df41c4 commit fe44060

File tree

2 files changed

+124
-90
lines changed

2 files changed

+124
-90
lines changed

package/src/components/ChannelList/ChannelList.tsx

Lines changed: 77 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@ import React, { useEffect, useState } from 'react';
22

33
import type { FlatList } from 'react-native-gesture-handler';
44

5-
import type { Channel, ChannelFilters, ChannelOptions, ChannelSort, Event } from 'stream-chat';
5+
import {
6+
Channel,
7+
ChannelFilters,
8+
ChannelManager,
9+
ChannelOptions,
10+
ChannelSort,
11+
Event,
12+
} from 'stream-chat';
613

714
import { ChannelListFooterLoadingIndicator } from './ChannelListFooterLoadingIndicator';
815
import { ChannelListHeaderErrorIndicator } from './ChannelListHeaderErrorIndicator';
@@ -265,7 +272,21 @@ export const ChannelList = <
265272
} = props;
266273

267274
const [forceUpdate, setForceUpdate] = useState(0);
268-
const { enableOfflineSupport } = useChatContext<StreamChatGenerics>();
275+
const [channelManager, setChannelManager] = useState<ChannelManager<StreamChatGenerics> | null>(
276+
null,
277+
);
278+
const { client, enableOfflineSupport } = useChatContext<StreamChatGenerics>();
279+
280+
useEffect(() => {
281+
const manager = new ChannelManager<StreamChatGenerics>({ client });
282+
manager.registerSubscriptions();
283+
setChannelManager(manager);
284+
285+
return () => {
286+
manager?.unregisterSubscriptions();
287+
};
288+
}, [client]);
289+
269290
const {
270291
channels,
271292
error,
@@ -276,74 +297,74 @@ export const ChannelList = <
276297
refreshing,
277298
refreshList,
278299
reloadList,
279-
setChannels,
280300
staticChannelsActive,
281301
} = usePaginatedChannels<StreamChatGenerics>({
282302
enableOfflineSupport,
283303
filters,
284304
options,
285305
setForceUpdate,
286306
sort,
307+
channelManager,
287308
});
288309

289310
// Setup event listeners
290-
useAddedToChannelNotification({
291-
onAddedToChannel,
292-
setChannels,
293-
});
294-
295-
useChannelDeleted({
296-
onChannelDeleted,
297-
setChannels,
298-
});
299-
300-
useChannelHidden({
301-
onChannelHidden,
302-
setChannels,
303-
});
304-
305-
useChannelTruncated({
306-
onChannelTruncated,
307-
refreshList,
308-
setChannels,
309-
setForceUpdate,
310-
});
311-
312-
useChannelUpdated({
313-
onChannelUpdated,
314-
setChannels,
315-
});
316-
317-
useChannelVisible({
318-
onChannelVisible,
319-
setChannels,
320-
});
321-
322-
useNewMessage({
323-
lockChannelOrder,
324-
onNewMessage,
325-
setChannels,
326-
});
327-
328-
useNewMessageNotification({
329-
onNewMessageNotification,
330-
setChannels,
331-
});
332-
333-
useRemovedFromChannelNotification({
334-
onRemovedFromChannel,
335-
setChannels,
336-
});
337-
338-
useUserPresence({
339-
setChannels,
340-
setForceUpdate,
341-
});
311+
// useAddedToChannelNotification({
312+
// onAddedToChannel,
313+
// setChannels,
314+
// });
315+
//
316+
// useChannelDeleted({
317+
// onChannelDeleted,
318+
// setChannels,
319+
// });
320+
//
321+
// useChannelHidden({
322+
// onChannelHidden,
323+
// setChannels,
324+
// });
325+
//
326+
// useChannelTruncated({
327+
// onChannelTruncated,
328+
// refreshList,
329+
// setChannels,
330+
// setForceUpdate,
331+
// });
332+
//
333+
// useChannelUpdated({
334+
// onChannelUpdated,
335+
// setChannels,
336+
// });
337+
//
338+
// useChannelVisible({
339+
// onChannelVisible,
340+
// setChannels,
341+
// });
342+
//
343+
// useNewMessage({
344+
// lockChannelOrder,
345+
// onNewMessage,
346+
// setChannels,
347+
// });
348+
//
349+
// useNewMessageNotification({
350+
// onNewMessageNotification,
351+
// setChannels,
352+
// });
353+
//
354+
// useRemovedFromChannelNotification({
355+
// onRemovedFromChannel,
356+
// setChannels,
357+
// });
358+
//
359+
// useUserPresence({
360+
// setChannels,
361+
// setForceUpdate,
362+
// });
342363

343364
const channelIdsStr = channels?.reduce((acc, channel) => `${acc}${channel.cid}`, '');
344365

345366
useEffect(() => {
346-
if (channels === null || staticChannelsActive || !enableOfflineSupport) {
367+
if (channels == null || staticChannelsActive || !enableOfflineSupport) {
347368
return;
348369
}
349370

package/src/components/ChannelList/hooks/usePaginatedChannels.ts

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
import { useEffect, useMemo, useRef, useState } from 'react';
22

3-
import type { Channel, ChannelFilters, ChannelOptions, ChannelSort } from 'stream-chat';
3+
import {
4+
Channel,
5+
ChannelFilters,
6+
ChannelManager,
7+
ChannelManagerState,
8+
ChannelOptions,
9+
ChannelSort,
10+
} from 'stream-chat';
411

512
import { useActiveChannelsRefContext } from '../../../contexts/activeChannelsRefContext/ActiveChannelsRefContext';
613
import { useChatContext } from '../../../contexts/chatContext/ChatContext';
14+
import { useStateStore } from '../../../hooks';
715
import { useIsMountedRef } from '../../../hooks/useIsMountedRef';
816

917
import { getChannelsForFilterSort } from '../../../store/apis/getChannelsForFilterSort';
@@ -24,6 +32,7 @@ type Parameters<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultSt
2432
options: ChannelOptions;
2533
setForceUpdate: React.Dispatch<React.SetStateAction<number>>;
2634
sort: ChannelSort<StreamChatGenerics>;
35+
channelManager?: ChannelManager<StreamChatGenerics>;
2736
};
2837

2938
const DEFAULT_OPTIONS = {
@@ -37,23 +46,32 @@ type QueryType = 'queryLocalDB' | 'reload' | 'refresh' | 'loadChannels';
3746

3847
export type QueryChannels = (queryType?: QueryType, retryCount?: number) => Promise<void>;
3948

49+
const selector = (nextValue: ChannelManagerState) =>
50+
({
51+
channels: nextValue.channels,
52+
pagination: nextValue.pagination,
53+
} as const);
54+
4055
export const usePaginatedChannels = <
4156
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
4257
>({
58+
channelManager,
4359
enableOfflineSupport,
4460
filters = {},
4561
options = DEFAULT_OPTIONS,
4662
setForceUpdate,
4763
sort = {},
4864
}: Parameters<StreamChatGenerics>) => {
49-
const [channels, setChannels] = useState<Channel<StreamChatGenerics>[] | null>(null);
65+
// const [channels, setChannels] = useState<Channel<StreamChatGenerics>[] | null>(null);
5066
const [error, setError] = useState<Error | undefined>(undefined);
5167
const [staticChannelsActive, setStaticChannelsActive] = useState<boolean>(false);
5268
const [activeQueryType, setActiveQueryType] = useState<QueryType | null>('queryLocalDB');
53-
const [hasNextPage, setHasNextPage] = useState<boolean>(false);
69+
// const [hasNextPage, setHasNextPage] = useState<boolean>(false);
5470
const activeChannels = useActiveChannelsRefContext();
5571
const isMountedRef = useIsMountedRef();
5672
const { client } = useChatContext<StreamChatGenerics>();
73+
const { channels, pagination } = useStateStore(channelManager?.state, selector) ?? {};
74+
const hasNextPage = pagination?.hasNext;
5775

5876
const filtersRef = useRef<typeof filters | null>(null);
5977
const sortRef = useRef<typeof sort | null>(null);
@@ -95,8 +113,7 @@ export const usePaginatedChannels = <
95113

96114
const newOptions = {
97115
limit: options?.limit ?? MAX_QUERY_CHANNELS_LIMIT,
98-
offset:
99-
queryType === 'loadChannels' && !staticChannelsActive && channels ? channels.length : 0,
116+
offset: 0,
100117
...options,
101118
};
102119

@@ -106,28 +123,32 @@ export const usePaginatedChannels = <
106123
* when they all (may) update the channel state at the same time (when connection state recovers)
107124
* TODO: if we move the channel state to a single context and share it between ChannelList, Channel and Thread we can remove this
108125
*/
109-
const channelQueryResponse = await client.queryChannels(filters, sort, newOptions, {
110-
skipInitialization: enableOfflineSupport ? undefined : activeChannels.current,
111-
});
126+
if (queryType === 'loadChannels') {
127+
await channelManager?.loadNext();
128+
} else {
129+
await channelManager?.queryChannels(filters, sort, newOptions, {
130+
skipInitialization: enableOfflineSupport ? undefined : activeChannels.current,
131+
});
132+
}
112133
if (isQueryStale() || !isMountedRef.current) {
113134
return;
114135
}
115136

116-
const newChannels =
117-
queryType === 'loadChannels' && !staticChannelsActive && channels
118-
? [...channels, ...channelQueryResponse]
119-
: channelQueryResponse.map((c) => {
120-
const existingChannel = client.activeChannels[c.cid];
121-
if (existingChannel) {
122-
return existingChannel;
123-
}
124-
125-
return c;
126-
});
127-
128-
setChannels(newChannels);
137+
// const newChannels =
138+
// queryType === 'loadChannels' && !staticChannelsActive && channels
139+
// ? [...channels, ...channelQueryResponse]
140+
// : channelQueryResponse.map((c) => {
141+
// const existingChannel = client.activeChannels[c.cid];
142+
// if (existingChannel) {
143+
// return existingChannel;
144+
// }
145+
//
146+
// return c;
147+
// });
148+
149+
// setChannels(newChannels);
129150
setStaticChannelsActive(false);
130-
setHasNextPage(channelQueryResponse.length >= newOptions.limit);
151+
// setHasNextPage(channelQueryResponse.length >= newOptions.limit);
131152
isQueryingRef.current = false;
132153
} catch (err: unknown) {
133154
isQueryingRef.current = false;
@@ -207,7 +228,7 @@ export const usePaginatedChannels = <
207228
skipInitialization: [], // passing empty array will clear out the existing messages from channel state, this removes the possibility of duplicate messages
208229
});
209230

210-
setChannels(offlineChannels);
231+
channelManager?.setChannels(offlineChannels);
211232
setStaticChannelsActive(true);
212233
}
213234
} catch (e) {
@@ -257,26 +278,18 @@ export const usePaginatedChannels = <
257278

258279
return () => listener?.unsubscribe?.();
259280
// eslint-disable-next-line react-hooks/exhaustive-deps
260-
}, [filterStr, sortStr]);
281+
}, [filterStr, sortStr, channelManager]);
261282

262283
return {
263284
channels,
264285
error,
265286
hasNextPage,
266-
loadingChannels:
267-
activeQueryType === 'queryLocalDB'
268-
? true
269-
: (activeQueryType === 'reload' || activeQueryType === null) && channels === null,
287+
loadingChannels: activeQueryType === 'queryLocalDB' ? true : pagination.isLoading,
270288
loadingNextPage: activeQueryType === 'loadChannels',
271-
loadNextPage: queryChannels,
289+
loadNextPage: channelManager?.loadNext,
272290
refreshing: activeQueryType === 'refresh',
273291
refreshList,
274292
reloadList,
275-
// Although channels can be null, there is no practical case where channels will be null
276-
// when setChannels is used. setChannels is only recommended to be used for overriding
277-
// event handler. Thus instead of adding if check for channels === null, its better to
278-
// simply reassign types here.
279-
setChannels,
280293
staticChannelsActive,
281294
};
282295
};

0 commit comments

Comments
 (0)