From 68e0cb8f4f59a98f93c7e20d9b0fd46799c9d9c1 Mon Sep 17 00:00:00 2001 From: Irene Ryu Date: Tue, 10 Dec 2024 20:20:28 +0900 Subject: [PATCH] [CLNP-6022] fix scroll position issue when switching GroupChannel (#1282) Fixes https://sendbird.atlassian.net/browse/CLNP-6022 This PR addresses an issue where the scroll position was not correctly set to the bottom when switching from a channel with few messages to one with many messages. The problem was resolved by adding a delay to ensure the scroll reference is updated before attempting to scroll to the bottom. This change ensures that users always see the latest messages when switching channels. --- .../context/hooks/useGroupChannel.ts | 7 ++-- .../context/__test__/useThread.spec.tsx | 1 - src/utils/__tests__/utils.spec.ts | 39 ++++++++++++++++++- src/utils/utils.ts | 5 +++ 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/modules/GroupChannel/context/hooks/useGroupChannel.ts b/src/modules/GroupChannel/context/hooks/useGroupChannel.ts index 59653fcec..9827d5989 100644 --- a/src/modules/GroupChannel/context/hooks/useGroupChannel.ts +++ b/src/modules/GroupChannel/context/hooks/useGroupChannel.ts @@ -19,6 +19,7 @@ import useSendbird from '../../../../lib/Sendbird/context/hooks/useSendbird'; import { GroupChannelContext } from '../GroupChannelProvider'; import type { GroupChannelState, MessageActions } from '../types'; import { useMessageActions } from './useMessageActions'; +import { delay } from '../../../../utils/utils'; export interface GroupChannelActions extends MessageActions { // Channel actions @@ -69,16 +70,16 @@ export const useGroupChannel = () => { const scrollToBottom = async (animated?: boolean) => { if (!state.scrollRef.current) return; + // wait a bit for scroll ref to be updated + await delay(); flagActions.setAnimatedMessageId(null); flagActions.setIsScrollBottomReached(true); if (config.isOnline && state.hasNext()) { await state.resetWithStartingPoint(Number.MAX_SAFE_INTEGER); - state.scrollPubSub.publish('scrollToBottom', { animated }); - } else { - state.scrollPubSub.publish('scrollToBottom', { animated }); } + state.scrollPubSub.publish('scrollToBottom', { animated }); if (state.currentChannel && !state.hasNext()) { state.resetNewMessages(); diff --git a/src/modules/Thread/context/__test__/useThread.spec.tsx b/src/modules/Thread/context/__test__/useThread.spec.tsx index 9fb264c53..b4329e0a3 100644 --- a/src/modules/Thread/context/__test__/useThread.spec.tsx +++ b/src/modules/Thread/context/__test__/useThread.spec.tsx @@ -753,7 +753,6 @@ describe('useThread', () => { }); await waitFor(() => { - console.log(result.current.state.localThreadMessages[0]); expect(result.current.state.localThreadMessages[0].messageParams.fileInfoList).toContain(newFileInfo); }); }); diff --git a/src/utils/__tests__/utils.spec.ts b/src/utils/__tests__/utils.spec.ts index 16b1224f1..ca9aac37a 100644 --- a/src/utils/__tests__/utils.spec.ts +++ b/src/utils/__tests__/utils.spec.ts @@ -6,7 +6,7 @@ import { isMultipleFilesMessage, } from '../index'; import { AdminMessage, FileMessage, MultipleFilesMessage, UserMessage } from '@sendbird/chat/message'; -import { deleteNullish } from '../utils'; +import { delay, deleteNullish } from '../utils'; import { isMobileIOS } from '../browser'; describe('Global-utils: verify message type util functions', () => { @@ -234,3 +234,40 @@ describe('deleteNullish', () => { expect(component({ a: null, b: '3', c: 4 })).toEqual({ a: 1, b: '3', c: 4 }); }); }); + +describe('delay', () => { + it('should resolve after the specified time', async () => { + const start = Date.now(); + const delayTime = 100; + + await delay(delayTime); + + const end = Date.now(); + const elapsed = end - start; + + // Check if the elapsed time is at least the delay time + expect(elapsed).toBeGreaterThanOrEqual(delayTime); + }); + + it('should resolve immediately for 0 milliseconds', async () => { + const start = Date.now(); + + await delay(0); + + const end = Date.now(); + const elapsed = end - start; + + // Check if the elapsed time is very small + expect(elapsed).toBeLessThan(10); + }); + it('should resolve immediately when no parameter is provided', async () => { + const start = Date.now(); + + await delay(); + + const end = Date.now(); + const elapsed = end - start; + + expect(elapsed).toBeLessThan(10); + }); +}); diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 844c66969..45137ae1d 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,5 +1,10 @@ import { SendableMessageType } from './index'; +/** + * @param ms - milliseconds to delay + * @returns Promise that resolves after the specified time + */ +export const delay = (ms?: number) => new Promise((resolve) => { setTimeout(resolve, ms); }); export const noop = () => { /** noop * */ };