Skip to content

Commit

Permalink
fix: use the client pagination indicators for ChannelStateContext's h…
Browse files Browse the repository at this point in the history
…asMore and hasMoreNewer flags
  • Loading branch information
MartinCupela committed Aug 20, 2024
1 parent 3a9acea commit c891096
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 46 deletions.
65 changes: 25 additions & 40 deletions src/components/Channel/Channel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,11 @@ const ChannelInner = <
channelReducer,
// channel.initialized === false if client.channel().query() was not called, e.g. ChannelList is not used
// => Channel will call channel.watch() in useLayoutEffect => state.loading is used to signal the watch() call state
{ ...initialState, loading: !channel.initialized },
{
...initialState,
hasMore: channel.state.messagePagination.hasPrev,

Check failure on line 418 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 16

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.

Check failure on line 418 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 18

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.
loading: !channel.initialized,
},
);

const isMounted = useIsMounted();
Expand Down Expand Up @@ -571,7 +575,6 @@ const ChannelInner = <
useLayoutEffect(() => {
let errored = false;
let done = false;
let channelInitializedExternally = true;

(async () => {
if (!channel.initialized && initializeOnMount) {
Expand All @@ -597,7 +600,6 @@ const ChannelInner = <
await getChannel({ channel, client, members, options: channelQueryOptions });
const config = channel.getConfig();
setChannelConfig(config);
channelInitializedExternally = false;
} catch (e) {
dispatch({ error: e as Error, type: 'setError' });
errored = true;
Expand All @@ -610,12 +612,7 @@ const ChannelInner = <
if (!errored) {
dispatch({
channel,
hasMore:
channelInitializedExternally ||
hasMoreMessagesProbably(
channel.state.messages.length,
channelQueryOptions.messages.limit,
),
hasMore: channel.state.messagePagination.hasPrev,

Check failure on line 615 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 16

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.

Check failure on line 615 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 18

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.
type: 'initStateFromChannel',
});

Expand Down Expand Up @@ -690,7 +687,8 @@ const ChannelInner = <
);

const loadMore = async (limit = DEFAULT_NEXT_CHANNEL_PAGE_SIZE) => {
if (!online.current || !window.navigator.onLine || !state.hasMore) return 0;
if (!online.current || !window.navigator.onLine || !channel.state.messagePagination.hasPrev)

Check failure on line 690 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 16

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.

Check failure on line 690 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 18

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.
return 0;

// prevent duplicate loading events...
const oldestMessage = state?.messages?.[0];
Expand All @@ -716,14 +714,14 @@ const ChannelInner = <
return 0;
}

const hasMoreMessages = queryResponse.messages.length === perPage;
loadMoreFinished(hasMoreMessages, channel.state.messages);
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);

Check failure on line 717 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 16

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.

Check failure on line 717 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 18

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.

return queryResponse.messages.length;
};

const loadMoreNewer = async (limit = DEFAULT_NEXT_CHANNEL_PAGE_SIZE) => {
if (!online.current || !window.navigator.onLine || !state.hasMoreNewer) return 0;
if (!online.current || !window.navigator.onLine || !channel.state.messagePagination.hasNext)

Check failure on line 723 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 16

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.

Check failure on line 723 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 18

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.
return 0;

const newestMessage = state?.messages?.[state?.messages?.length - 1];
if (state.loadingMore || state.loadingMoreNewer) return 0;
Expand All @@ -745,10 +743,8 @@ const ChannelInner = <
return 0;
}

const hasMoreNewerMessages = channel.state.messages !== channel.state.latestMessages;

dispatch({
hasMoreNewer: hasMoreNewerMessages,
hasMoreNewer: channel.state.messagePagination.hasNext,

Check failure on line 747 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 16

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.

Check failure on line 747 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 18

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.
messages: channel.state.messages,
type: 'loadMoreNewerFinished',
});
Expand All @@ -766,18 +762,9 @@ const ChannelInner = <
dispatch({ loadingMore: true, type: 'setLoadingMore' });
await channel.state.loadMessageIntoState(messageId, undefined, messageLimit);

/**
* if the message we are jumping to has less than half of the page size older messages,
* we have jumped to the beginning of the channel.
*/
const indexOfMessage = channel.state.messages.findIndex(
(message) => message.id === messageId,
);
const hasMoreMessages = indexOfMessage >= Math.floor(messageLimit / 2);

loadMoreFinished(hasMoreMessages, channel.state.messages);
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);

Check failure on line 765 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 16

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.

Check failure on line 765 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 18

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.
dispatch({
hasMoreNewer: channel.state.messages !== channel.state.latestMessages,
hasMoreNewer: channel.state.messagePagination.hasNext,

Check failure on line 767 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 16

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.

Check failure on line 767 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 18

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.
highlightedMessageId: messageId,
type: 'jumpToMessageFinished',
});
Expand All @@ -796,9 +783,7 @@ const ChannelInner = <

const jumpToLatestMessage: ChannelActionContextValue<StreamChatGenerics>['jumpToLatestMessage'] = useCallback(async () => {
await channel.state.loadMessageIntoState('latest');
// FIXME: we cannot rely on constant value 25 as the page size can be customized by integrators
const hasMoreOlder = channel.state.messages.length >= 25;
loadMoreFinished(hasMoreOlder, channel.state.messages);
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);

Check failure on line 786 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 16

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.

Check failure on line 786 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 18

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.
dispatch({
type: 'jumpToLatestMessage',
});
Expand All @@ -813,7 +798,6 @@ const ChannelInner = <
let lastReadMessageId = channelUnreadUiState?.last_read_message_id;
let firstUnreadMessageId = channelUnreadUiState?.first_unread_message_id;
let isInCurrentMessageSet = false;
let hasMoreMessages = true;

if (firstUnreadMessageId) {
const result = findInMsgSetById(firstUnreadMessageId, channel.state.messages);
Expand Down Expand Up @@ -852,14 +836,14 @@ const ChannelInner = <
).messages;
} catch (e) {
addNotification(t('Failed to jump to the first unread message'), 'error');
loadMoreFinished(hasMoreMessages, channel.state.messages);
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);

Check failure on line 839 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 16

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.

Check failure on line 839 in src/components/Channel/Channel.tsx

View workflow job for this annotation

GitHub Actions / Test with Node 18

Property 'messagePagination' does not exist on type 'ChannelState<StreamChatGenerics>'.
return;
}

const firstMessageWithCreationDate = messages.find((msg) => msg.created_at);
if (!firstMessageWithCreationDate) {
addNotification(t('Failed to jump to the first unread message'), 'error');
loadMoreFinished(hasMoreMessages, channel.state.messages);
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
return;
}
const firstMessageTimestamp = new Date(
Expand All @@ -868,13 +852,11 @@ const ChannelInner = <
if (lastReadTimestamp < firstMessageTimestamp) {
// whole channel is unread
firstUnreadMessageId = firstMessageWithCreationDate.id;
hasMoreMessages = false;
} else {
const result = findInMsgSetByDate(channelUnreadUiState.last_read, messages);
lastReadMessageId = result.target?.id;
hasMoreMessages = result.index >= Math.floor(queryMessageLimit / 2);
}
loadMoreFinished(hasMoreMessages, channel.state.messages);
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
}
}

Expand All @@ -895,13 +877,12 @@ const ChannelInner = <
const indexOfTarget = channel.state.messages.findIndex(
(message) => message.id === targetId,
) as number;
hasMoreMessages = indexOfTarget >= Math.floor(queryMessageLimit / 2);
loadMoreFinished(hasMoreMessages, channel.state.messages);
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
firstUnreadMessageId =
firstUnreadMessageId ?? channel.state.messages[indexOfTarget + 1]?.id;
} catch (e) {
addNotification(t('Failed to jump to the first unread message'), 'error');
loadMoreFinished(hasMoreMessages, channel.state.messages);
loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
return;
}
}
Expand All @@ -918,7 +899,7 @@ const ChannelInner = <
});

dispatch({
hasMoreNewer: channel.state.messages !== channel.state.latestMessages,
hasMoreNewer: channel.state.messagePagination.hasNext,
highlightedMessageId: firstUnreadMessageId,
type: 'jumpToMessageFinished',
});
Expand Down Expand Up @@ -1346,6 +1327,10 @@ const ChannelInner = <
);
}

// @ts-ignore
window['channel'] = channel;
// @ts-ignore
window['client'] = client;
return (
<div className={clsx(className, windowsEmojiClass)}>
<ChannelStateProvider value={channelStateContextValue}>
Expand Down
36 changes: 30 additions & 6 deletions src/components/Channel/__tests__/Channel.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ const ActiveChannelSetter = ({ activeChannel }) => {
const user = generateUser({ custom: 'custom-value', id: 'id', name: 'name' });

// create a full message state so that we can properly test `loadMore`
const messages = Array.from({ length: 25 }, () => generateMessage({ user }));
const messages = Array.from({ length: 25 }, (_, i) =>
generateMessage({ created_at: new Date((i + 1) * 1000000), user }),
);

const pinnedMessages = [generateMessage({ pinned: true, user })];

Expand Down Expand Up @@ -701,6 +703,20 @@ describe('Channel', () => {

describe('loading more messages', () => {
const limit = 10;
it("should initiate the hasMore flag with the current message set's pagination hasPrev value", async () => {
const { channel, chatClient } = await initClient();
let hasMore;
await renderComponent({ channel, chatClient }, ({ hasMore: hasMoreCtx }) => {
hasMore = hasMoreCtx;
});
expect(hasMore).toBe(true);

channel.state.messageSets[0].pagination.hasPrev = false;
await renderComponent({ channel, chatClient }, ({ hasMore: hasMoreCtx }) => {
hasMore = hasMoreCtx;
});
expect(hasMore).toBe(false);
});
it('should be able to load more messages', async () => {
const { channel, chatClient } = await initClient();
const channelQuerySpy = jest.spyOn(channel, 'query');
Expand Down Expand Up @@ -740,7 +756,7 @@ describe('Channel', () => {
it('should set hasMore to false if querying channel returns less messages than the limit', async () => {
const { channel, chatClient } = await initClient();
let channelHasMore = false;
const newMessages = [generateMessage()];
const newMessages = [generateMessage({ created_at: new Date(1000) })];
await renderComponent(
{ channel, chatClient },
({ hasMore, loadMore, messages: contextMessages }) => {
Expand Down Expand Up @@ -822,8 +838,12 @@ describe('Channel', () => {

it('should load the second page, if the previous query has returned message count equal default messages limit', async () => {
const { channel, chatClient } = await initClient();
const firstPageMessages = Array.from({ length: 25 }, generateMessage);
const secondPageMessages = Array.from({ length: 15 }, generateMessage);
const firstPageMessages = Array.from({ length: 25 }, (_, i) =>
generateMessage({ created_at: new Date((i + 16) * 100000) }),
);
const secondPageMessages = Array.from({ length: 15 }, (_, i) =>
generateMessage({ created_at: new Date((i + 1) * 100000) }),
);
useMockedApis(chatClient, [queryChannelWithNewMessages(firstPageMessages, channel)]);
let queryNextPageSpy;
let contextMessageCount;
Expand Down Expand Up @@ -896,8 +916,12 @@ describe('Channel', () => {
const channelQueryOptions = {
messages: { limit: equalCount },
};
const firstPageMessages = Array.from({ length: equalCount }, generateMessage);
const secondPageMessages = Array.from({ length: equalCount - 1 }, generateMessage);
const firstPageMessages = Array.from({ length: equalCount }, (_, i) =>
generateMessage({ created_at: new Date((i + 1 + equalCount) * 100000) }),
);
const secondPageMessages = Array.from({ length: equalCount - 1 }, (_, i) =>
generateMessage({ created_at: new Date((i + 1) * 100000) }),
);
useMockedApis(chatClient, [queryChannelWithNewMessages(firstPageMessages, channel)]);
let queryNextPageSpy;
let contextMessageCount;
Expand Down

0 comments on commit c891096

Please sign in to comment.