Skip to content

Commit f55aac9

Browse files
authored
fix: Move scroll of message list without scrollIntoView (#460)
Issue * If customer had setup OpenChannel on a Page that has scroll, it used to scroll the whole page, unintentionally * The function `scrollIntoView` has affected the outside element. So it triggers weird scroll behavior that we didn't expect of the parent elements. Fix * Move message list scroll using element.scrollTop instead of the function scrollIntoView in the OpenChannelMessageList [UIKIT-3420](https://sendbird.atlassian.net/browse/UIKIT-3420)
1 parent ac15bf5 commit f55aac9

File tree

6 files changed

+62
-35
lines changed

6 files changed

+62
-35
lines changed

src/smart-components/Channel/components/MessageList/index.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { isAboutSame } from '../../context/utils';
1111
import { getMessagePartsInfo } from './getMessagePartsInfo';
1212
import UnreadCount from '../UnreadCount';
1313
import FrozenNotification from '../FrozenNotification';
14-
import { MESSAGE_SCROLL_BUFFER } from '../../context/const';
14+
import { SCROLL_BUFFER } from '../../../../utils/consts';
1515

1616
export interface MessageListProps {
1717
className?: string;
@@ -77,7 +77,7 @@ const MessageList: React.FC<MessageListProps> = ({
7777
});
7878
}
7979

80-
if (isAboutSame(clientHeight + scrollTop, scrollHeight, MESSAGE_SCROLL_BUFFER)) {
80+
if (isAboutSame(clientHeight + scrollTop, scrollHeight, SCROLL_BUFFER)) {
8181
onScrollDownCallback(([messages]) => {
8282
if (messages) {
8383
try {
@@ -96,7 +96,7 @@ const MessageList: React.FC<MessageListProps> = ({
9696
setScrollBottom(current.scrollHeight - current.scrollTop - current.offsetHeight)
9797
}
9898

99-
if (!disableMarkAsRead && isAboutSame(clientHeight + scrollTop, scrollHeight, MESSAGE_SCROLL_BUFFER)) {
99+
if (!disableMarkAsRead && isAboutSame(clientHeight + scrollTop, scrollHeight, SCROLL_BUFFER)) {
100100
// Mark as read if scroll is at end
101101
setTimeout(() => {
102102
messagesDispatcher({
@@ -125,7 +125,7 @@ const MessageList: React.FC<MessageListProps> = ({
125125
const current = scrollRef?.current;
126126
if (current) {
127127
const bottom = current.scrollHeight - current.scrollTop - current.offsetHeight;
128-
if (scrollBottom < bottom && scrollBottom <= MESSAGE_SCROLL_BUFFER) {
128+
if (scrollBottom < bottom && scrollBottom <= SCROLL_BUFFER) {
129129
current.scrollTop += bottom - scrollBottom;
130130
}
131131
}

src/smart-components/Channel/context/const.ts

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
export const MESSAGE_SCROLL_BUFFER = 10;
2-
31
export const PREV_RESULT_SIZE = 30;
42
export const NEXT_RESULT_SIZE = 15;
53

src/smart-components/OpenChannel/components/OpenChannelMessageList/index.tsx

+9-28
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import './openchannel-message-list.scss';
22

33
import React, { ReactElement, useRef, useState, useMemo } from 'react';
4+
import { FileMessage, UserMessage } from '@sendbird/chat/message';
45
import isSameDay from 'date-fns/isSameDay';
56

67
import Icon, { IconTypes, IconColors } from '../../../../ui/Icon';
@@ -10,7 +11,7 @@ import { compareMessagesForGrouping } from '../../context/utils';
1011
import { useOpenChannelContext } from '../../context/OpenChannelProvider';
1112
import OpenChannelMessage from '../OpenChannelMessage';
1213
import { RenderMessageProps } from '../../../../types';
13-
import { FileMessage, UserMessage } from '@sendbird/chat/message';
14+
import { useHandleOnScrollCallback } from './useHandleOnScrollCallback';
1415

1516
export type OpenchannelMessageListProps = {
1617
renderMessage?: (props: RenderMessageProps) => React.ElementType<RenderMessageProps>;
@@ -30,40 +31,20 @@ function OpenchannelMessageList(
3031
const scrollRef = ref || useRef(null);
3132
const [showScrollDownButton, setShowScrollDownButton] = useState(false);
3233

33-
const handleOnScroll = (e) => {
34-
const element = e.target;
35-
const {
36-
scrollTop,
37-
scrollHeight,
38-
clientHeight,
39-
} = element;
40-
if (scrollHeight > scrollTop + clientHeight + 1) {
41-
setShowScrollDownButton(true);
42-
} else {
43-
setShowScrollDownButton(false);
44-
}
45-
46-
if (!hasMore) {
47-
return;
48-
}
49-
if (scrollTop === 0) {
50-
const nodes = scrollRef.current.querySelectorAll('.sendbird-msg--scroll-ref');
51-
const first = nodes && nodes[0];
52-
onScroll(() => {
53-
try {
54-
first.scrollIntoView();
55-
} catch (error) { }
56-
});
57-
}
58-
};
59-
6034
const scrollToBottom = () => {
6135
if (scrollRef && scrollRef.current) {
6236
scrollRef.current.scrollTo(0, scrollRef.current.scrollHeight);
6337
setShowScrollDownButton(false);
6438
}
6539
};
6640

41+
const handleOnScroll = useHandleOnScrollCallback({
42+
setShowScrollDownButton,
43+
hasMore,
44+
onScroll,
45+
scrollRef,
46+
});
47+
6748
const memoizedMessageList = useMemo(() => {
6849
if (allMessages.length > 0) {
6950
return (
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React, { useCallback } from "react";
2+
import { SCROLL_BUFFER } from "../../../../utils/consts";
3+
4+
export interface UseHandleOnScrollCallbackProps {
5+
setShowScrollDownButton: React.Dispatch<React.SetStateAction<boolean>>;
6+
hasMore: boolean;
7+
onScroll(fn: () => void): void;
8+
scrollRef: React.RefObject<HTMLDivElement>;
9+
}
10+
11+
export function useHandleOnScrollCallback({
12+
setShowScrollDownButton,
13+
hasMore,
14+
onScroll,
15+
scrollRef,
16+
}: UseHandleOnScrollCallbackProps): (e: React.UIEvent<HTMLElement>) => void {
17+
return useCallback((e) => {
18+
const element = e.target as Element;
19+
const {
20+
scrollTop,
21+
scrollHeight,
22+
clientHeight,
23+
} = element;
24+
const scrollBottom = scrollHeight - scrollTop;
25+
if (scrollHeight > scrollTop + clientHeight + 1) {
26+
setShowScrollDownButton(true);
27+
} else {
28+
setShowScrollDownButton(false);
29+
}
30+
if (!hasMore) {
31+
return;
32+
}
33+
if (scrollTop < SCROLL_BUFFER) {
34+
onScroll(() => {
35+
// Fetch more messages
36+
scrollRef.current.scrollTop = scrollRef.current.scrollHeight - scrollBottom;
37+
});
38+
}
39+
}, [
40+
setShowScrollDownButton,
41+
hasMore,
42+
onScroll,
43+
scrollRef,
44+
]);
45+
}

src/smart-components/OpenChannelList/components/OpenChannelListUI/index.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { useOpenChannelListContext } from '../../context/OpenChannelListProvider
1313
import OpenChannelListActionTypes from '../../context/dux/actionTypes';
1414
import CreateOpenChannel from '../../../CreateOpenChannel';
1515
import { LocalizationContext } from '../../../../lib/LocalizationContext';
16+
import { SCROLL_BUFFER } from '../../../../utils/consts';
1617

1718
interface RenderOpenChannelPreviewProps {
1819
channel: OpenChannel;
@@ -57,7 +58,7 @@ function OpenChannelListUI({
5758
scrollHeight,
5859
} = element;
5960
const isAboutSame = (a, b, px) => (Math.abs(a - b) <= px);
60-
if (isAboutSame(clientHeight + scrollTop, scrollHeight, 10)) {
61+
if (isAboutSame(clientHeight + scrollTop, scrollHeight, SCROLL_BUFFER)) {
6162
fetchNextChannels((messages) => {
6263
if (messages) {
6364
try {

src/utils/consts.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export const SCROLL_BUFFER = 10;
2+
13
// voice message record
24
export const VOICE_RECORDER_CLICK_BUFFER_TIME = 250;
35
export const VOICE_RECORDER_DEFAULT_MIN = 1000;

0 commit comments

Comments
 (0)