1- import React , { createContext , useCallback , useState } from 'react' ;
1+ import React , { createContext , useCallback , useRef , useState } from 'react' ;
2+ import type { FlatList } from 'react-native' ;
23
34import { useChannelHandler } from '@sendbird/uikit-chat-hooks' ;
45import {
6+ ContextValue ,
7+ Logger ,
58 NOOP ,
69 SendbirdFileMessage ,
710 SendbirdGroupChannel ,
11+ SendbirdMessage ,
812 SendbirdUser ,
913 SendbirdUserMessage ,
1014 isDifferentChannel ,
15+ useFreshCallback ,
1116 useUniqHandlerId ,
1217} from '@sendbird/uikit-utils' ;
1318
1419import ProviderLayout from '../../../components/ProviderLayout' ;
20+ import { MESSAGE_FOCUS_ANIMATION_DELAY } from '../../../constants' ;
1521import { useLocalization , useSendbirdChat } from '../../../hooks/useContext' ;
1622import type { PubSub } from '../../../utils/pubsub' ;
1723import type { GroupChannelContextsType , GroupChannelModule , GroupChannelPubSubContextPayload } from '../types' ;
24+ import { GroupChannelProps } from '../types' ;
1825
1926export const GroupChannelContexts : GroupChannelContextsType = {
2027 Fragment : createContext ( {
@@ -30,6 +37,16 @@ export const GroupChannelContexts: GroupChannelContextsType = {
3037 publish : NOOP ,
3138 subscribe : ( ) => NOOP ,
3239 } as PubSub < GroupChannelPubSubContextPayload > ) ,
40+ MessageList : createContext ( {
41+ flatListRef : { current : null } ,
42+ scrollToMessage : ( ) => false ,
43+ lazyScrollToBottom : ( ) => {
44+ // noop
45+ } ,
46+ lazyScrollToIndex : ( ) => {
47+ // noop
48+ } ,
49+ } as MessageListContextValue ) ,
3350} ;
3451
3552export const GroupChannelContextsProvider : GroupChannelModule [ 'Provider' ] = ( {
@@ -38,6 +55,8 @@ export const GroupChannelContextsProvider: GroupChannelModule['Provider'] = ({
3855 enableTypingIndicator,
3956 keyboardAvoidOffset = 0 ,
4057 groupChannelPubSub,
58+ messages,
59+ onUpdateSearchItem,
4160} ) => {
4261 if ( ! channel ) throw new Error ( 'GroupChannel is not provided to GroupChannelModule' ) ;
4362
@@ -49,6 +68,11 @@ export const GroupChannelContextsProvider: GroupChannelModule['Provider'] = ({
4968 const [ messageToEdit , setMessageToEdit ] = useState < SendbirdUserMessage | SendbirdFileMessage > ( ) ;
5069 const [ messageToReply , setMessageToReply ] = useState < SendbirdUserMessage | SendbirdFileMessage > ( ) ;
5170
71+ const { flatListRef, lazyScrollToIndex, lazyScrollToBottom, scrollToMessage } = useScrollActions ( {
72+ messages,
73+ onUpdateSearchItem,
74+ } ) ;
75+
5276 const updateInputMode = ( mode : 'send' | 'edit' | 'reply' , message ?: SendbirdUserMessage | SendbirdFileMessage ) => {
5377 if ( mode === 'send' || ! message ) {
5478 setMessageToEdit ( undefined ) ;
@@ -101,12 +125,97 @@ export const GroupChannelContextsProvider: GroupChannelModule['Provider'] = ({
101125 setMessageToReply : useCallback ( ( message ) => updateInputMode ( 'reply' , message ) , [ ] ) ,
102126 } }
103127 >
104- < GroupChannelContexts . TypingIndicator . Provider value = { { typingUsers } } >
105- < GroupChannelContexts . PubSub . Provider value = { groupChannelPubSub } >
106- { children }
107- </ GroupChannelContexts . PubSub . Provider >
108- </ GroupChannelContexts . TypingIndicator . Provider >
128+ < GroupChannelContexts . PubSub . Provider value = { groupChannelPubSub } >
129+ < GroupChannelContexts . TypingIndicator . Provider value = { { typingUsers } } >
130+ < GroupChannelContexts . MessageList . Provider
131+ value = { {
132+ flatListRef,
133+ scrollToMessage,
134+ lazyScrollToIndex,
135+ lazyScrollToBottom,
136+ } }
137+ >
138+ { children }
139+ </ GroupChannelContexts . MessageList . Provider >
140+ </ GroupChannelContexts . TypingIndicator . Provider >
141+ </ GroupChannelContexts . PubSub . Provider >
109142 </ GroupChannelContexts . Fragment . Provider >
110143 </ ProviderLayout >
111144 ) ;
112145} ;
146+
147+ type MessageListContextValue = ContextValue < GroupChannelContextsType [ 'MessageList' ] > ;
148+ const useScrollActions = ( params : Pick < GroupChannelProps [ 'Provider' ] , 'messages' | 'onUpdateSearchItem' > ) => {
149+ const { messages, onUpdateSearchItem } = params ;
150+ const flatListRef = useRef < FlatList < SendbirdMessage > > ( null ) ;
151+
152+ // FIXME: Workaround, should run after data has been applied to UI.
153+ const lazyScrollToBottom = useFreshCallback < MessageListContextValue [ 'lazyScrollToIndex' ] > ( ( params ) => {
154+ if ( ! flatListRef . current ) {
155+ logFlatListRefWarning ( ) ;
156+ return ;
157+ }
158+
159+ setTimeout ( ( ) => {
160+ flatListRef . current ?. scrollToOffset ( { offset : 0 , animated : params ?. animated ?? false } ) ;
161+ } , params ?. timeout ?? 0 ) ;
162+ } ) ;
163+
164+ // FIXME: Workaround, should run after data has been applied to UI.
165+ const lazyScrollToIndex = useFreshCallback < MessageListContextValue [ 'lazyScrollToIndex' ] > ( ( params ) => {
166+ if ( ! flatListRef . current ) {
167+ logFlatListRefWarning ( ) ;
168+ return ;
169+ }
170+
171+ setTimeout ( ( ) => {
172+ flatListRef . current ?. scrollToIndex ( {
173+ index : params ?. index ?? 0 ,
174+ animated : params ?. animated ?? false ,
175+ viewPosition : params ?. viewPosition ?? 0.5 ,
176+ } ) ;
177+ } , params ?. timeout ?? 0 ) ;
178+ } ) ;
179+
180+ const scrollToMessage = useFreshCallback < MessageListContextValue [ 'scrollToMessage' ] > ( ( messageId , options ) => {
181+ if ( ! flatListRef . current ) {
182+ logFlatListRefWarning ( ) ;
183+ return false ;
184+ }
185+
186+ const foundMessageIndex = messages . findIndex ( ( it ) => it . messageId === messageId ) ;
187+ const isIncludedInList = foundMessageIndex > - 1 ;
188+
189+ if ( isIncludedInList ) {
190+ if ( options ?. focusAnimated ) {
191+ setTimeout (
192+ ( ) => onUpdateSearchItem ( { startingPoint : messages [ foundMessageIndex ] . createdAt } ) ,
193+ MESSAGE_FOCUS_ANIMATION_DELAY ,
194+ ) ;
195+ }
196+ lazyScrollToIndex ( {
197+ index : foundMessageIndex ,
198+ animated : true ,
199+ timeout : 0 ,
200+ viewPosition : options ?. viewPosition ,
201+ } ) ;
202+ return true ;
203+ } else {
204+ return false ;
205+ }
206+ } ) ;
207+
208+ return {
209+ flatListRef,
210+ lazyScrollToIndex,
211+ lazyScrollToBottom,
212+ scrollToMessage,
213+ } ;
214+ } ;
215+
216+ const logFlatListRefWarning = ( ) => {
217+ Logger . warn (
218+ 'Cannot find flatListRef.current, please render FlatList and pass the flatListRef' +
219+ 'or please try again after FlatList has been rendered.' ,
220+ ) ;
221+ } ;
0 commit comments