stream-chat-react
Version:
React components to create chat conversations or livestream style chat
95 lines (94 loc) • 4.2 kB
JavaScript
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import uniqBy from 'lodash.uniqby';
import { MAX_QUERY_CHANNELS_LIMIT } from '../utils';
import { useChatContext } from '../../../context/ChatContext';
import { DEFAULT_INITIAL_CHANNEL_PAGE_SIZE } from '../../../constants/limits';
const RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS = 5000;
const MIN_RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS = 2000;
export const usePaginatedChannels = (client, filters, sort, options, activeChannelHandler, recoveryThrottleIntervalMs = RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS, customQueryChannels) => {
const { channelsQueryState: { error, setError, setQueryInProgress }, } = useChatContext('usePaginatedChannels');
const [channels, setChannels] = useState([]);
const [hasNextPage, setHasNextPage] = useState(true);
const lastRecoveryTimestamp = useRef(undefined);
const recoveryThrottleInterval = recoveryThrottleIntervalMs < MIN_RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS
? MIN_RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS
: (recoveryThrottleIntervalMs ?? RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS);
// memoize props
const filterString = useMemo(() => JSON.stringify(filters), [filters]);
const sortString = useMemo(() => JSON.stringify(sort), [sort]);
// eslint-disable-next-line react-hooks/exhaustive-deps
const queryChannels = async (queryType = 'load-more') => {
setError(null);
if (queryType === 'reload') {
setChannels([]);
}
setQueryInProgress(queryType);
try {
if (customQueryChannels) {
await customQueryChannels({
currentChannels: channels,
queryType: queryType,
setChannels,
setHasNextPage,
});
}
else {
const offset = queryType === 'reload' ? 0 : channels.length;
const newOptions = {
limit: options?.limit ?? MAX_QUERY_CHANNELS_LIMIT,
message_limit: options?.message_limit ?? DEFAULT_INITIAL_CHANNEL_PAGE_SIZE,
offset,
...options,
};
const channelQueryResponse = await client.queryChannels(filters, sort || {}, newOptions);
const newChannels = queryType === 'reload'
? channelQueryResponse
: uniqBy([...channels, ...channelQueryResponse], 'cid');
setChannels(newChannels);
setHasNextPage(channelQueryResponse.length >= newOptions.limit);
// Set active channel only on load of first page
if (!offset && activeChannelHandler) {
activeChannelHandler(newChannels, setChannels);
}
}
}
catch (error) {
console.warn(error);
setError(error);
}
setQueryInProgress(null);
};
const throttleRecover = useCallback(() => {
const now = Date.now();
const isFirstRecovery = !lastRecoveryTimestamp.current;
const timeElapsedSinceLastRecoveryMs = lastRecoveryTimestamp.current
? now - lastRecoveryTimestamp.current
: 0;
if (!isFirstRecovery &&
timeElapsedSinceLastRecoveryMs < recoveryThrottleInterval &&
!error) {
return;
}
lastRecoveryTimestamp.current = now;
queryChannels('reload');
}, [error, queryChannels, recoveryThrottleInterval]);
const loadNextPage = () => queryChannels();
useEffect(() => {
if (client.recoverStateOnReconnect)
return;
const { unsubscribe } = client.on('connection.recovered', throttleRecover);
return () => {
unsubscribe();
};
}, [client, throttleRecover]);
useEffect(() => {
queryChannels('reload');
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [filterString, sortString]);
return {
channels,
hasNextPage,
loadNextPage,
setChannels,
};
};