-
Notifications
You must be signed in to change notification settings - Fork 281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: initial message load issues #2292
Changes from all commits
e77798e
408970a
a6fedbc
87f78ba
bf1db66
e82806d
ff1368a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ import React, { | |
} from 'react'; | ||
|
||
import debounce from 'lodash.debounce'; | ||
import defaultsDeep from 'lodash.defaultsdeep'; | ||
import throttle from 'lodash.throttle'; | ||
import { | ||
ChannelAPIResponse, | ||
|
@@ -346,7 +347,7 @@ const ChannelInner = < | |
acceptedFiles, | ||
activeUnreadHandler, | ||
channel, | ||
channelQueryOptions, | ||
channelQueryOptions: propChannelQueryOptions, | ||
children, | ||
doDeleteMessageRequest, | ||
doMarkReadRequest, | ||
|
@@ -366,6 +367,16 @@ const ChannelInner = < | |
skipMessageDataMemoization, | ||
} = props; | ||
|
||
const channelQueryOptions: ChannelQueryOptions<StreamChatGenerics> & { | ||
messages: { limit: number }; | ||
} = useMemo( | ||
() => | ||
defaultsDeep(propChannelQueryOptions, { | ||
messages: { limit: DEFAULT_INITIAL_CHANNEL_PAGE_SIZE }, | ||
}), | ||
[propChannelQueryOptions], | ||
); | ||
|
||
const { | ||
client, | ||
customClasses, | ||
|
@@ -546,6 +557,7 @@ const ChannelInner = < | |
useLayoutEffect(() => { | ||
let errored = false; | ||
let done = false; | ||
let channelInitializedExternally = true; | ||
|
||
(async () => { | ||
if (!channel.initialized && initializeOnMount) { | ||
|
@@ -571,6 +583,7 @@ 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; | ||
|
@@ -583,10 +596,12 @@ const ChannelInner = < | |
if (!errored) { | ||
dispatch({ | ||
channel, | ||
hasMore: hasMoreMessagesProbably( | ||
channel.state.messages.length, | ||
channelQueryOptions?.messages?.limit ?? DEFAULT_INITIAL_CHANNEL_PAGE_SIZE, | ||
), | ||
hasMore: | ||
channelInitializedExternally || | ||
hasMoreMessagesProbably( | ||
channel.state.messages.length, | ||
channelQueryOptions.messages.limit, | ||
), | ||
Comment on lines
-586
to
+604
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it wasn't the |
||
type: 'initStateFromChannel', | ||
}); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ import { useChatContext } from '../../../context/ChatContext'; | |
|
||
import type { DefaultStreamChatGenerics } from '../../../types/types'; | ||
import type { ChannelsQueryState } from '../../Chat/hooks/useChannelsQueryState'; | ||
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; | ||
|
@@ -79,6 +80,7 @@ export const usePaginatedChannels = < | |
|
||
const newOptions = { | ||
limit: options?.limit ?? MAX_QUERY_CHANNELS_LIMIT, | ||
message_limit: options?.message_limit ?? DEFAULT_INITIAL_CHANNEL_PAGE_SIZE, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
offset, | ||
...options, | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import React, { PropsWithChildren, useCallback, useEffect, useLayoutEffect, useRef } from 'react'; | ||
import React, { PropsWithChildren, useEffect, useLayoutEffect, useRef } from 'react'; | ||
import type { PaginatorProps } from '../../types/types'; | ||
import { deprecationAndReplacementWarning } from '../../utils/deprecationWarning'; | ||
|
||
|
@@ -73,7 +73,8 @@ export const InfiniteScroll = (props: PropsWithChildren<InfiniteScrollProps>) => | |
|
||
const scrollComponent = useRef<HTMLElement>(); | ||
|
||
const scrollListener = useCallback(() => { | ||
const scrollListenerRef = useRef<() => void>(); | ||
scrollListenerRef.current = () => { | ||
const element = scrollComponent.current; | ||
|
||
if (!element || element.offsetParent === null) { | ||
|
@@ -103,15 +104,7 @@ export const InfiniteScroll = (props: PropsWithChildren<InfiniteScrollProps>) => | |
if (offset < Number(threshold) && typeof loadNextPageFn === 'function' && hasNextPageFlag) { | ||
loadNextPageFn(); | ||
} | ||
}, [ | ||
hasPreviousPageFlag, | ||
hasNextPageFlag, | ||
isLoading, | ||
listenToScroll, | ||
loadPreviousPageFn, | ||
loadNextPageFn, | ||
threshold, | ||
]); | ||
}; | ||
|
||
useEffect(() => { | ||
deprecationAndReplacementWarning( | ||
|
@@ -130,14 +123,17 @@ export const InfiniteScroll = (props: PropsWithChildren<InfiniteScrollProps>) => | |
const scrollElement = scrollComponent.current?.parentNode; | ||
if (!scrollElement) return; | ||
|
||
const scrollListener = () => scrollListenerRef.current?.(); | ||
|
||
scrollElement.addEventListener('scroll', scrollListener, useCapture); | ||
scrollElement.addEventListener('resize', scrollListener, useCapture); | ||
scrollListener(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that we'll only load a single additional page of messages in case the first page is not enough to cause overflow of the message list. To keep loading pages until there is overflow, or there is no more pages to load, we can do something like this: useLayoutEffect(() => {
if (!isLoading) {
scrollListenerRef.current?.();
}
}, [isLoading]); However, I don't see a way to have the same behavior in the |
||
|
||
return () => { | ||
scrollElement.removeEventListener('scroll', scrollListener, useCapture); | ||
scrollElement.removeEventListener('resize', scrollListener, useCapture); | ||
}; | ||
}, [initialLoad, scrollListener, useCapture]); | ||
}, [initialLoad, useCapture]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prevent this effect from firing excessively. Note that |
||
|
||
useEffect(() => { | ||
const scrollElement = scrollComponent.current?.parentNode; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ import type { MessageProps } from '../Message/types'; | |
import type { StreamMessage } from '../../context/ChannelStateContext'; | ||
|
||
import type { DefaultStreamChatGenerics } from '../../types/types'; | ||
import { DEFAULT_NEXT_CHANNEL_PAGE_SIZE } from '../../constants/limits'; | ||
|
||
type MessageListWithContextProps< | ||
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics | ||
|
@@ -68,7 +69,7 @@ const MessageListWithContext = < | |
headerPosition, | ||
read, | ||
renderMessages = defaultRenderMessages, | ||
messageLimit = 100, | ||
messageLimit = DEFAULT_NEXT_CHANNEL_PAGE_SIZE, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove hard-coded limit for message page size. |
||
loadMore: loadMoreCallback, | ||
loadMoreNewer: loadMoreNewerCallback, | ||
hasMoreNewer = false, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,6 +53,7 @@ import { ComponentContextValue, useComponentContext } from '../../context/Compon | |
|
||
import type { Channel, ChannelState as StreamChannelState, UserResponse } from 'stream-chat'; | ||
import type { DefaultStreamChatGenerics, UnknownType } from '../../types/types'; | ||
import { DEFAULT_NEXT_CHANNEL_PAGE_SIZE } from '../../constants/limits'; | ||
|
||
type VirtualizedMessageListPropsForContext = | ||
| 'additionalMessageInputProps' | ||
|
@@ -184,7 +185,7 @@ const VirtualizedMessageListWithContext = < | |
loadMoreNewer, | ||
Message: MessageUIComponentFromProps, | ||
messageActions, | ||
messageLimit = 100, | ||
messageLimit = DEFAULT_NEXT_CHANNEL_PAGE_SIZE, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove hard-coded limit for message page size. |
||
messages, | ||
notifications, | ||
// TODO: refactor to scrollSeekPlaceHolderConfiguration and components.ScrollSeekPlaceholder, like the Virtuoso Component | ||
|
@@ -386,7 +387,9 @@ const VirtualizedMessageListWithContext = < | |
} | ||
}; | ||
const atTopStateChange = (isAtTop: boolean) => { | ||
if (isAtTop) loadMore?.(messageLimit); | ||
if (isAtTop) { | ||
loadMore?.(messageLimit); | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use
DEFAULT_INITIAL_CHANNEL_PAGE_SIZE
when loading the first page of messages.