diff --git a/package/src/components/ChannelList/__tests__/ChannelList.test.js b/package/src/components/ChannelList/__tests__/ChannelList.test.js index 2b1228ae99..d166544fa7 100644 --- a/package/src/components/ChannelList/__tests__/ChannelList.test.js +++ b/package/src/components/ChannelList/__tests__/ChannelList.test.js @@ -20,6 +20,7 @@ import dispatchChannelDeletedEvent from '../../../mock-builders/event/channelDel import dispatchChannelHiddenEvent from '../../../mock-builders/event/channelHidden'; import dispatchChannelTruncatedEvent from '../../../mock-builders/event/channelTruncated'; import dispatchChannelUpdatedEvent from '../../../mock-builders/event/channelUpdated'; +import dispatchConnectionChangedEvent from '../../../mock-builders/event/connectionChanged'; import dispatchConnectionRecoveredEvent from '../../../mock-builders/event/connectionRecovered'; import dispatchMessageNewEvent from '../../../mock-builders/event/messageNew'; import dispatchNotificationAddedToChannelEvent from '../../../mock-builders/event/notificationAddedToChannel'; @@ -75,6 +76,11 @@ const ChannelListSwipeActionsProbe = () => { return {`${swipeActionsEnabled}`}; }; +const ChannelListRefreshingProbe = () => { + const { refreshing } = useChannelsContext(); + return {`${refreshing}`}; +}; + const ChannelPreviewContent = ({ unread }) => {`${unread}`}; const ChannelListWithChannelPreview = () => { @@ -805,6 +811,43 @@ describe('ChannelList', () => { }); }); + describe('connection.changed', () => { + it('should keep background reconnection refreshes debounced and out of pull-to-refresh UI', async () => { + useMockedApis(chatClient, [queryChannelsApi([testChannel1])]); + const deferredPromise = new DeferredPromise(); + const dateNowSpy = jest.spyOn(Date, 'now'); + dateNowSpy.mockReturnValueOnce(0); + dateNowSpy.mockReturnValue(6000); + + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByTestId('refreshing').children[0]).toBe('false'); + }); + + chatClient.queryChannels = jest.fn(() => deferredPromise.promise); + + act(() => dispatchConnectionChangedEvent(chatClient, false)); + act(() => dispatchConnectionChangedEvent(chatClient, true)); + + await waitFor(() => { + expect(chatClient.queryChannels).toHaveBeenCalled(); + }); + + act(() => dispatchConnectionChangedEvent(chatClient, true)); + + expect(chatClient.queryChannels).toHaveBeenCalledTimes(1); + expect(screen.getByTestId('refreshing').children[0]).toBe('false'); + + deferredPromise.resolve([testChannel1]); + dateNowSpy.mockRestore(); + }); + }); + describe('channel.truncated', () => { it('should call the `onChannelTruncated` function prop, if provided', async () => { useMockedApis(chatClient, [queryChannelsApi([testChannel1])]); diff --git a/package/src/components/ChannelList/hooks/usePaginatedChannels.ts b/package/src/components/ChannelList/hooks/usePaginatedChannels.ts index 27af07468f..7965d278f0 100644 --- a/package/src/components/ChannelList/hooks/usePaginatedChannels.ts +++ b/package/src/components/ChannelList/hooks/usePaginatedChannels.ts @@ -24,7 +24,7 @@ type Parameters = { const RETRY_INTERVAL_IN_MS = 5000; -type QueryType = 'queryLocalDB' | 'reload' | 'refresh' | 'loadChannels'; +type QueryType = 'queryLocalDB' | 'reload' | 'refresh' | 'loadChannels' | 'backgroundRefresh'; export type QueryChannels = (queryType?: QueryType, retryCount?: number) => Promise; @@ -68,6 +68,7 @@ export const usePaginatedChannels = ({ const hasUpdatedData = queryType === 'loadChannels' || queryType === 'refresh' || + queryType === 'backgroundRefresh' || [ JSON.stringify(filtersRef.current) !== JSON.stringify(filters), JSON.stringify(sortRef.current) !== JSON.stringify(sort), @@ -129,7 +130,7 @@ export const usePaginatedChannels = ({ setActiveQueryType(null); }; - const refreshList = async () => { + const refreshList = async ({ isBackground = false }: { isBackground?: boolean } = {}) => { const now = Date.now(); // Only allow pull-to-refresh 5 seconds after last successful refresh. if (now - lastRefresh.current < RETRY_INTERVAL_IN_MS && error === undefined) { @@ -137,7 +138,7 @@ export const usePaginatedChannels = ({ } lastRefresh.current = Date.now(); - await queryChannels('refresh'); + await queryChannels(isBackground ? 'backgroundRefresh' : 'refresh'); }; const reloadList = async () => { @@ -167,7 +168,9 @@ export const usePaginatedChannels = ({ 'connection.changed', async (event) => { if (event.online) { - await refreshList(); + // Reconnection refreshes should stay silent, but still share the same debounce + // path as pull-to-refresh. + await refreshList({ isBackground: true }); } }, ); @@ -195,7 +198,7 @@ export const usePaginatedChannels = ({ loadingNextPage: pagination?.isLoadingNext, loadNextPage: channelManager.loadNext, refreshing: activeQueryType === 'refresh', - refreshList, + refreshList: () => refreshList(), reloadList, staticChannelsActive, }; diff --git a/package/src/components/MessageMenu/MessageUserReactions.tsx b/package/src/components/MessageMenu/MessageUserReactions.tsx index affcf9c444..a606374a1c 100644 --- a/package/src/components/MessageMenu/MessageUserReactions.tsx +++ b/package/src/components/MessageMenu/MessageUserReactions.tsx @@ -291,6 +291,7 @@ export const MessageUserReactions = (props: MessageUserReactionsProps) => { { const [showAddCommentDialog, setShowAddCommentDialog] = useState(false); const { onPress } = props; + const styles = useStyles(); + const onPressHandler = useCallback(() => { if (onPress) { onPress({ message, poll }); @@ -37,10 +40,10 @@ export const AnswerListAddCommentButton = (props: PollButtonProps) => { }, [message, onPress, poll]); return ( - <> +