Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
const { sdk, sbOptions, groupChannelFragmentOptions } = useSendbirdChat();
const { setMessageToEdit, setMessageToReply } = useContext(GroupChannelContexts.Fragment);
const groupChannelPubSub = useContext(GroupChannelContexts.PubSub);
const { flatListRef, lazyScrollToBottom, lazyScrollToIndex, onPressReplyMessageInThread } = useContext(
const { flatListRef, lazyScrollToBottom, lazyScrollToMessageId, onPressReplyMessageInThread } = useContext(
GroupChannelContexts.MessageList,
);

Expand All @@ -40,6 +40,7 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
const viewableMessages = useRef<SendbirdMessage[]>();
const hasUserMarkedAsUnreadRef = useRef(false);
const [unreadFirstMessage, setUnreadFirstMessage] = useState<SendbirdMessage | undefined>(undefined);
const pendingBottomReachedRef = useRef<{ timeout: number; timestamp: number } | null>(null);

const updateHasSeenNewLine = useCallback(
(hasSeenNewLine: boolean) => {
Expand All @@ -63,14 +64,16 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {

const scrollToMessageWithCreatedAt = useFreshCallback(
(createdAt: number, focusAnimated: boolean, timeout: number): boolean => {
const foundMessageIndex = props.messages.findIndex((it) => it.createdAt === createdAt);
const isIncludedInList = foundMessageIndex > -1;
const foundMessage = props.messages.find((it) => it.createdAt === createdAt);
const isIncludedInList = !!foundMessage;
pendingBottomReachedRef.current = null;

if (isIncludedInList) {
if (focusAnimated) {
setTimeout(() => props.onUpdateSearchItem({ startingPoint: createdAt }), MESSAGE_FOCUS_ANIMATION_DELAY);
}
lazyScrollToIndex({ index: foundMessageIndex, animated: true, timeout });
pendingBottomReachedRef.current = { timeout, timestamp: Date.now() };
lazyScrollToMessageId({ messageId: foundMessage.messageId, animated: true, timeout });
} else {
if (props.channel.messageOffsetTimestamp <= createdAt) {
if (focusAnimated) {
Expand Down Expand Up @@ -346,6 +349,23 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
},
);

const onBottomReached = useFreshCallback(() => {
if (props.hasNext()) {
if (pendingBottomReachedRef.current) {
const currentTime = Date.now();
const elapsedTime = currentTime - pendingBottomReachedRef.current.timestamp;

const timeoutThreshold = 500;
if (elapsedTime >= pendingBottomReachedRef.current.timeout + timeoutThreshold) {
props.onBottomReached?.();
pendingBottomReachedRef.current = null;
}
} else {
props.onBottomReached?.();
}
}
});

return (
<ChannelMessageList
{...props}
Expand All @@ -359,6 +379,7 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
onPressNewMessagesButton={scrollToBottom}
onPressScrollToBottomButton={scrollToBottom}
onPressMarkAsUnreadMessage={onPressMarkAsUnreadMessage}
onBottomReached={onBottomReached}
unreadFirstMessage={unreadFirstMessage}
unreadMessagesFloatingProps={unreadMessagesFloatingPropsRef.current}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export const GroupChannelContexts: GroupChannelContextsType = {
lazyScrollToIndex: () => {
// noop
},
lazyScrollToMessageId: () => {
// noop
},
} as MessageListContextValue),
};

Expand All @@ -68,10 +71,11 @@ export const GroupChannelContextsProvider: GroupChannelModule['Provider'] = ({
const [messageToEdit, setMessageToEdit] = useState<SendbirdUserMessage | SendbirdFileMessage>();
const [messageToReply, setMessageToReply] = useState<SendbirdUserMessage | SendbirdFileMessage>();

const { flatListRef, lazyScrollToIndex, lazyScrollToBottom, scrollToMessage } = useScrollActions({
messages,
onUpdateSearchItem,
});
const { flatListRef, lazyScrollToIndex, lazyScrollToBottom, scrollToMessage, lazyScrollToMessageId } =
useScrollActions({
messages,
onUpdateSearchItem,
});

const updateInputMode = (mode: 'send' | 'edit' | 'reply', message?: SendbirdUserMessage | SendbirdFileMessage) => {
if (mode === 'send' || !message) {
Expand Down Expand Up @@ -143,6 +147,7 @@ export const GroupChannelContextsProvider: GroupChannelModule['Provider'] = ({
scrollToMessage,
lazyScrollToIndex,
lazyScrollToBottom,
lazyScrollToMessageId,
onPressReplyMessageInThread,
}}
>
Expand All @@ -159,9 +164,11 @@ type MessageListContextValue = ContextValue<GroupChannelContextsType['MessageLis
const useScrollActions = (params: Pick<GroupChannelProps['Provider'], 'messages' | 'onUpdateSearchItem'>) => {
const { messages, onUpdateSearchItem } = params;
const flatListRef = useRef<FlatList<SendbirdMessage>>(null);
const messagesRef = useRef(messages);
messagesRef.current = messages;

// FIXME: Workaround, should run after data has been applied to UI.
const lazyScrollToBottom = useFreshCallback<MessageListContextValue['lazyScrollToIndex']>((params) => {
const lazyScrollToBottom = useFreshCallback<MessageListContextValue['lazyScrollToBottom']>((params) => {
if (!flatListRef.current) {
logFlatListRefWarning();
return;
Expand All @@ -188,6 +195,33 @@ const useScrollActions = (params: Pick<GroupChannelProps['Provider'], 'messages'
}, params?.timeout ?? 0);
});

// FIXME: Workaround, should run after data has been applied to UI.
const lazyScrollToMessageId = useFreshCallback<MessageListContextValue['lazyScrollToMessageId']>((params) => {
if (!flatListRef.current) {
logFlatListRefWarning();
return;
}

setTimeout(() => {
let messageIndex = 0;
if (params?.messageId) {
const foundMessageIndex = messagesRef.current.findIndex((it) => it.messageId === params.messageId);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 첫 데이터 로드에서 찾은 index 는 스크롤로 가는 도중에 load more 등으로 리스트가 업데이트 되면
index 가 바뀌니, ref 를 사용해서 해당 시점의 messages 스냅샷에서 정확한 index 를 찾아서 이동하는것 맞을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 현재 상태에서는 onBottomReached 가 loadNext를 호출하면서 문제가 발생하고있습니다. 어떤 경우라도 messages 가 변경되었다면 현재 messages 기준으로 인덱스를 다시 찾아야 스크롤 처리해주는게 기대 동작일것 같습니다

if (foundMessageIndex > -1) {
messageIndex = foundMessageIndex;
} else {
Logger.warn('Message with messageId not found:', params.messageId);
return;
}
}

flatListRef.current?.scrollToIndex({
index: messageIndex,
animated: params?.animated ?? false,
viewPosition: params?.viewPosition ?? 0.5,
});
}, params?.timeout ?? 0);
});

const scrollToMessage = useFreshCallback<MessageListContextValue['scrollToMessage']>((messageId, options) => {
if (!flatListRef.current) {
logFlatListRefWarning();
Expand Down Expand Up @@ -221,6 +255,7 @@ const useScrollActions = (params: Pick<GroupChannelProps['Provider'], 'messages'
lazyScrollToIndex,
lazyScrollToBottom,
scrollToMessage,
lazyScrollToMessageId,
};
};

Expand Down
10 changes: 10 additions & 0 deletions packages/uikit-react-native/src/domain/groupChannel/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,16 @@ export interface GroupChannelContextsType {
timeout?: number;
viewPosition?: number;
}) => void;
/**
* Call the FlatList function asynchronously to scroll to a message by messageId lazily.
* to avoid scrolling before data rendering has been committed.
* */
lazyScrollToMessageId: (params?: {
messageId?: number;
animated?: boolean;
timeout?: number;
viewPosition?: number;
}) => void;

onPressReplyMessageInThread?: (parentMessage: SendbirdSendableMessage, startingPoint?: number) => void;
}>;
Expand Down