Skip to content

Commit

Permalink
bug-fix: Fix scrollBottom issue in IOS 16. (#770)
Browse files Browse the repository at this point in the history
  • Loading branch information
liamcho authored Oct 11, 2023
1 parent 96f2fff commit ec78d44
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 115 deletions.
1 change: 1 addition & 0 deletions src/hooks/useHandleOnScrollCallback/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface UseHandleOnScrollCallbackProps {
onScroll(callback: () => void): void;
scrollRef: React.RefObject<HTMLDivElement>;
setShowScrollDownButton?: React.Dispatch<React.SetStateAction<boolean>>;
setIsScrolled?: React.Dispatch<React.SetStateAction<boolean>>;
}

export function calcScrollBottom(scrollHeight: number, scrollTop: number): number {
Expand Down
2 changes: 2 additions & 0 deletions src/modules/Channel/components/Message/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const Message = ({
onMessageAnimated,
onMessageHighlighted,
onScrollCallback,
setIsScrolled,
} = useChannelContext();
const [showEdit, setShowEdit] = useState(false);
const [showRemove, setShowRemove] = useState(false);
Expand Down Expand Up @@ -117,6 +118,7 @@ const Message = ({
hasMore: false,
onScroll: onScrollCallback,
scrollRef: messageScrollRef,
setIsScrolled,
});

const mentionNodes = useDirtyGetMentions({ ref: editMessageInputRef }, { logger });
Expand Down
233 changes: 123 additions & 110 deletions src/modules/Channel/components/MessageList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,11 @@ const MessageList: React.FC<MessageListProps> = ({
filterMessageList,
replyType,
loading,
isScrolled,
setIsScrolled,
unreadSince,
} = useChannelContext();

const store = useSendbirdStateContext();
const allMessagesFiltered = (typeof filterMessageList === 'function')
? allMessages.filter((filterMessageList as (message: EveryMessage) => boolean))
Expand Down Expand Up @@ -160,121 +163,131 @@ const MessageList: React.FC<MessageListProps> = ({
}
return <PlaceHolder className="sendbird-conversation__no-messages" type={PlaceHolderTypes.NO_MESSAGES} />;
}

return (
<div className={`sendbird-conversation__messages ${className}`}>
<div className="sendbird-conversation__scroll-container">
<div className="sendbird-conversation__padding" />
<div
className="sendbird-conversation__messages-padding"
ref={scrollRef}
onScroll={(e) => {
handleOnScroll();
scrollToBottomHandler(e);
}}
>
{allMessagesFiltered.map((m, idx) => {
const {
chainTop,
chainBottom,
hasSeparator,
} = getMessagePartsInfo({
allMessages: allMessagesFiltered,
replyType,
isMessageGroupingEnabled,
currentIndex: idx,
currentMessage: m,
currentChannel: currentGroupChannel,
});
const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId;
return (
<MessageProvider message={m} key={m?.messageId} isByMe={isByMe}>
<Message
handleScroll={moveScroll}
renderMessage={renderMessage}
message={m}
hasSeparator={hasSeparator}
chainTop={chainTop}
chainBottom={chainBottom}
renderCustomSeparator={renderCustomSeparator}
/>
</MessageProvider>
);
})}
{localMessages.map((m, idx) => {
const {
chainTop,
chainBottom,
} = getMessagePartsInfo({
allMessages: allMessagesFiltered,
replyType,
isMessageGroupingEnabled,
currentIndex: idx,
currentMessage: m,
currentChannel: currentGroupChannel,
});
const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId;
return (
<MessageProvider message={m} key={m?.messageId} isByMe={isByMe}>
<Message
handleScroll={moveScroll}
renderMessage={renderMessage}
message={m}
chainTop={chainTop}
chainBottom={chainBottom}
renderCustomSeparator={renderCustomSeparator}
/>
</MessageProvider>
);
})}
{/* show frozen notifications */}
{/* show new message notifications */}
</div>
</div>
{currentGroupChannel?.isFrozen && (
<FrozenNotification className="sendbird-conversation__messages__notification" />
)}
{unreadSince && (
<UnreadCount
className="sendbird-conversation__messages__notification"
count={currentGroupChannel?.unreadMessageCount}
time={unreadSince}
onClick={() => {
if (scrollRef?.current?.scrollTop) {
scrollRef.current.scrollTop = (scrollRef?.current?.scrollHeight ?? 0) - (scrollRef?.current?.offsetHeight ?? 0);
}
if (!disableMarkAsRead && !!currentGroupChannel) {
markAsReadScheduler.push(currentGroupChannel);
messagesDispatcher({
type: messageActionTypes.MARK_AS_READ,
payload: { channel: currentGroupChannel },
});
}
setInitialTimeStamp(null);
setAnimatedMessageId(null);
setHighLightedMessageId(null);
}}
/>
)}
<>
{
// This flag is an unmatched variable
scrollBottom > SCROLL_BOTTOM_PADDING && (
!isScrolled && <PlaceHolder type={PlaceHolderTypes.LOADING} />
}
<div className={`sendbird-conversation__messages ${className}`}>
<div className="sendbird-conversation__scroll-container">
<div className="sendbird-conversation__padding" />
<div
className="sendbird-conversation__scroll-bottom-button"
onClick={onClickScrollBot}
onKeyDown={onClickScrollBot}
tabIndex={0}
role="button"
className="sendbird-conversation__messages-padding"
ref={scrollRef}
onScroll={(e) => {
handleOnScroll();
scrollToBottomHandler(e);
}}
>
<Icon
width="24px"
height="24px"
type={IconTypes.CHEVRON_DOWN}
fillColor={IconColors.PRIMARY}
/>
{
allMessagesFiltered.map((m, idx) => {
const {
chainTop,
chainBottom,
hasSeparator,
} = getMessagePartsInfo({
allMessages: allMessagesFiltered,
replyType,
isMessageGroupingEnabled,
currentIndex: idx,
currentMessage: m,
currentChannel: currentGroupChannel,
});
const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId;
return (
<MessageProvider message={m} key={m?.messageId} isByMe={isByMe}>
<Message
handleScroll={moveScroll}
renderMessage={renderMessage}
message={m}
hasSeparator={hasSeparator}
chainTop={chainTop}
chainBottom={chainBottom}
renderCustomSeparator={renderCustomSeparator}
/>
</MessageProvider>
);
})
}
{
localMessages.map((m, idx) => {
const {
chainTop,
chainBottom,
} = getMessagePartsInfo({
allMessages: allMessagesFiltered,
replyType,
isMessageGroupingEnabled,
currentIndex: idx,
currentMessage: m,
currentChannel: currentGroupChannel,
});
const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId;
return (
<MessageProvider message={m} key={m?.messageId} isByMe={isByMe}>
<Message
handleScroll={moveScroll}
renderMessage={renderMessage}
message={m}
chainTop={chainTop}
chainBottom={chainBottom}
renderCustomSeparator={renderCustomSeparator}
/>
</MessageProvider>
);
})
}
{/* show frozen notifications, */}
{/* show new message notifications, */}
</div>
)
}
</div>
</div>
{currentGroupChannel?.isFrozen && (
<FrozenNotification className="sendbird-conversation__messages__notification" />
)}
{unreadSince && (
<UnreadCount
className="sendbird-conversation__messages__notification"
count={currentGroupChannel?.unreadMessageCount}
time={unreadSince}
onClick={() => {
if (scrollRef?.current?.scrollTop) {
scrollRef.current.scrollTop = (scrollRef?.current?.scrollHeight ?? 0) - (scrollRef?.current?.offsetHeight ?? 0);
}
if (!disableMarkAsRead && !!currentGroupChannel) {
markAsReadScheduler.push(currentGroupChannel);
messagesDispatcher({
type: messageActionTypes.MARK_AS_READ,
payload: { channel: currentGroupChannel },
});
}
setInitialTimeStamp(null);
setAnimatedMessageId(null);
setHighLightedMessageId(null);
}}
/>
)}
{
// This flag is an unmatched variable
scrollBottom > SCROLL_BOTTOM_PADDING && (
<div
className="sendbird-conversation__scroll-bottom-button"
onClick={onClickScrollBot}
onKeyDown={onClickScrollBot}
tabIndex={0}
role="button"
>
<Icon
width="24px"
height="24px"
type={IconTypes.CHEVRON_DOWN}
fillColor={IconColors.PRIMARY}
/>
</div>
)
}
</div>
</>
);
};

Expand Down
6 changes: 6 additions & 0 deletions src/modules/Channel/context/ChannelProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ interface UpdateMessageProps {

interface ChannelProviderInterface extends ChannelContextProps, MessageStoreInterface {
scrollToMessage?(createdAt: number, messageId: number): void;
isScrolled?: boolean;
setIsScrolled?: React.Dispatch<React.SetStateAction<boolean>>;
messageActionTypes: Record<string, string>;
messagesDispatcher: CustomUseReducerDispatcher;
quoteMessage: SendableMessageType;
Expand Down Expand Up @@ -229,6 +231,7 @@ const ChannelProvider: React.FC<ChannelContextProps> = (props: ChannelContextPro
}, [highlightedMessage]);
const userFilledMessageListQuery = queries?.messageListParams;
const [quoteMessage, setQuoteMessage] = useState<SendableMessageType>(null);
const [isScrolled, setIsScrolled] = useState(false);

const [messagesStore, messagesDispatcher] = useReducer(
messagesReducer,
Expand Down Expand Up @@ -358,6 +361,7 @@ const ChannelProvider: React.FC<ChannelContextProps> = (props: ChannelContextPro
latestMessageTimeStamp,
replyType,
isVoiceMessageEnabled,
setIsScrolled,
}, {
logger,
scrollRef,
Expand Down Expand Up @@ -516,6 +520,8 @@ const ChannelProvider: React.FC<ChannelContextProps> = (props: ChannelContextPro
scrollRef,
scrollBehavior,
toggleReaction,
isScrolled,
setIsScrolled,
}}>
<UserProfileProvider
disableUserProfile={props?.disableUserProfile ?? config?.disableUserProfile}
Expand Down
1 change: 1 addition & 0 deletions src/modules/Channel/context/dux/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const RESET_MESSAGES = 'RESET_MESSAGES';

export const FETCH_INITIAL_MESSAGES_START = 'FETCH_INITIAL_MESSAGES_START';
export const FETCH_INITIAL_MESSAGES_SUCCESS = 'FETCH_INITIAL_MESSAGES_SUCCESS';

export const FETCH_INITIAL_MESSAGES_FAILURE = 'FETCH_INITIAL_MESSAGES_FAILURE';
export const FETCH_PREV_MESSAGES_SUCCESS = 'FETCH_PREV_MESSAGES_SUCCESS';
export const FETCH_PREV_MESSAGES_FAILURE = 'FETCH_PREV_MESSAGES_FAILURE';
Expand Down
7 changes: 5 additions & 2 deletions src/modules/Channel/context/hooks/useInitialMessagesFetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ function useInitialMessagesFetch({
userFilledMessageListQuery,
initialTimeStamp,
replyType,
setIsScrolled,
}, {
logger,
scrollRef,
messagesDispatcher,
}) {
const channelUrl = currentGroupChannel?.url;

useEffect(() => {
logger.info('Channel useInitialMessagesFetch: Setup started', currentGroupChannel);
setIsScrolled(false);
messagesDispatcher({
type: messageActionTypes.RESET_MESSAGES,
payload: null,
Expand Down Expand Up @@ -78,10 +81,10 @@ function useInitialMessagesFetch({
})
.finally(() => {
if (!initialTimeStamp) {
setTimeout(() => utils.scrollIntoLast(0, scrollRef));
setTimeout(() => utils.scrollIntoLast(0, scrollRef, setIsScrolled));
} else {
setTimeout(() => {
utils.scrollToRenderedMessage(scrollRef, initialTimeStamp);
utils.scrollToRenderedMessage(scrollRef, initialTimeStamp, setIsScrolled);
}, 500);
}
});
Expand Down
10 changes: 7 additions & 3 deletions src/modules/Channel/context/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { OutgoingMessageStates } from '../../../utils/exports/getOutgoingMessage
const UNDEFINED = 'undefined';
const { SUCCEEDED, FAILED, PENDING } = getSendingMessageStatus();

export const scrollToRenderedMessage = (scrollRef, initialTimeStamp) => {
export const scrollToRenderedMessage = (scrollRef, initialTimeStamp, setIsScrolled) => {
try {
const container = scrollRef.current;
// scroll into the message with initialTimeStamp
Expand All @@ -23,23 +23,27 @@ export const scrollToRenderedMessage = (scrollRef, initialTimeStamp) => {
}
} catch {
// do nothing
} finally {
setIsScrolled?.(true);
}
};

/* eslint-disable default-param-last */
export const scrollIntoLast = (initialTry = 0, scrollRef) => {
export const scrollIntoLast = (initialTry = 0, scrollRef, setIsScrolled) => {
const MAX_TRIES = 10;
const currentTry = initialTry;
if (currentTry > MAX_TRIES) {
setIsScrolled?.(true);
return;
}
try {
const scrollDOM = scrollRef?.current || document.querySelector('.sendbird-conversation__messages-padding');
// eslint-disable-next-line no-multi-assign
scrollDOM.scrollTop = scrollDOM.scrollHeight;
setIsScrolled?.(true);
} catch (error) {
setTimeout(() => {
scrollIntoLast(currentTry + 1, scrollRef);
scrollIntoLast(currentTry + 1, scrollRef, setIsScrolled);
}, 500 * currentTry);
}
};
Expand Down

0 comments on commit ec78d44

Please sign in to comment.