11import { useContext , useEffect , useRef , useState } from 'react' ;
2+ import type { Dispatch , RefObject , SetStateAction } from 'react' ;
23import {
34 FlatList ,
45 NativeSyntheticEvent ,
@@ -13,7 +14,7 @@ import {observer} from 'mobx-react-lite';
1314
1415import { ErrorBoundary } from 'react-error-boundary' ;
1516
16- import type { Channel , Message as RevoltMessage } from 'revolt.js' ;
17+ import { Channel , Message as RevoltMessage } from 'revolt.js' ;
1718
1819import { app } from './Generic' ;
1920import { client } from './lib/client' ;
@@ -130,72 +131,21 @@ function MessageViewErrorMessage({
130131export const NewMessageView = observer (
131132 ( {
132133 channel,
133- handledMessages,
134+ messages,
135+ atEndOfPage,
136+ fetchMoreMessages,
137+ scrollViewRef,
134138 } : {
135139 channel : Channel ;
136- handledMessages : string [ ] ;
140+ messages : RevoltMessage [ ] ;
141+ atEndOfPage : { current : boolean } ;
142+ fetchMoreMessages : ( before : string ) => void ;
143+ scrollViewRef : RefObject < FlatList > ;
137144 } ) => {
138145 console . log ( `[NEWMESSAGEVIEW] Creating message view for ${ channel . _id } ...` ) ;
139- const { currentTheme} = useContext ( ThemeContext ) ;
140146
141147 const { t} = useTranslation ( ) ;
142148
143- const [ messages , setMessages ] = useState ( [ ] as RevoltMessage [ ] ) ;
144- const [ loading , setLoading ] = useState ( true ) ;
145- const [ atEndOfPage , setAtEndOfPage ] = useState ( false ) ;
146- const [ error , setError ] = useState ( null as any ) ;
147-
148- const scrollViewRef = useRef < FlatList > ( null ) ;
149-
150- useEffect ( ( ) => {
151- console . log ( `[NEWMESSAGEVIEW] Fetching messages for ${ channel . _id } ...` ) ;
152- async function getMessages ( ) {
153- const msgs = await fetchMessages ( channel , { } , [ ] ) ;
154- console . log (
155- `[NEWMESSAGEVIEW] Pushing ${ msgs . length } initial message(s) for ${ channel . _id } ...` ,
156- ) ;
157- setMessages ( msgs ) ;
158- setLoading ( false ) ;
159- }
160- try {
161- getMessages ( ) ;
162- } catch ( err ) {
163- console . log (
164- `[NEWMESSAGEVIEW] Error fetching initial messages for ${ channel . _id } : ${ err } ` ,
165- ) ;
166- setError ( err ) ;
167- }
168- } , [ channel ] ) ;
169-
170- client . on ( 'message' , async msg => {
171- // set this before anything happens that might change it
172- const shouldScroll = atEndOfPage ;
173- if ( msg . channel === channel && ! handledMessages . includes ( msg . _id ) ) {
174- try {
175- console . log ( atEndOfPage ) ;
176- handledMessages . push ( msg . _id ) ;
177- console . log (
178- `[NEWMESSAGEVIEW] New message ${ msg . _id } is in current channel; pushing it to the message list... (debug: will scroll = ${ atEndOfPage } )` ,
179- ) ;
180- setMessages ( oldMessages => [ ...oldMessages , msg ] ) ;
181- if ( shouldScroll ) {
182- scrollViewRef . current ?. scrollToEnd ( ) ;
183- }
184- } catch ( err ) {
185- console . log (
186- `[NEWMESSAGEVIEW] Error pushing new message (${ msg . _id } ): ${ err } ` ,
187- ) ;
188- setError ( err ) ;
189- }
190- }
191- } ) ;
192-
193- client . on ( 'message/delete' , async ( id , msg ) => {
194- if ( msg ?. channel ?. _id === channel . _id ) {
195- setMessages ( messages . filter ( m => m . _id !== id ) ) ;
196- }
197- } ) ;
198-
199149 // set functions here so they don't get recreated
200150 const onPress = ( m : RevoltMessage ) => {
201151 handleTap ( m ) ;
@@ -210,32 +160,24 @@ export const NewMessageView = observer(
210160 } ;
211161
212162 const onScroll = ( e : NativeSyntheticEvent < NativeScrollEvent > ) => {
163+ console . log ( client . listenerCount ( 'message' ) ) ;
213164 const position = e . nativeEvent . contentOffset . y ;
214165 const viewHeight =
215166 e . nativeEvent . contentSize . height -
216167 e . nativeEvent . layoutMeasurement . height ;
217168 // account for decimal weirdness by assuming that if the position is this close to the height that the user is at the bottom
218169 if ( viewHeight - position <= 1 ) {
219170 console . log ( 'bonk!' ) ;
220- setAtEndOfPage ( true ) ;
171+ atEndOfPage . current = true ;
221172 channel . ack ( channel . last_message_id ?? '01ANOMESSAGES' , true ) ;
222173 } else {
223- if ( atEndOfPage ) {
224- setAtEndOfPage ( false ) ;
174+ if ( atEndOfPage . current ) {
175+ atEndOfPage . current = false ;
225176 }
226177 }
227178 if ( e . nativeEvent . contentOffset . y <= 0 ) {
228179 console . log ( 'bonk2!' ) ;
229- fetchMessages (
230- channel ,
231- {
232- type : 'before' ,
233- id : messages [ 0 ] . _id ,
234- } ,
235- messages ,
236- ) . then ( newMsgs => {
237- setMessages ( newMsgs ) ;
238- } ) ;
180+ fetchMoreMessages ( messages [ 0 ] . _id ) ;
239181 }
240182 // console.log(
241183 // e.nativeEvent.contentOffset.y,
@@ -246,39 +188,195 @@ export const NewMessageView = observer(
246188
247189 return (
248190 < ErrorBoundary fallbackRender = { MessageViewErrorMessage } >
249- { error ? (
250- < Text colour = { currentTheme . error } >
251- Error rendering messages: { error . message ?? error }
252- </ Text >
253- ) : loading ? (
254- < LoadingScreen />
255- ) : (
256- < View key = { 'messageview-outer-container' } style = { { flex : 1 } } >
257- < FlatList
258- key = { 'messageview-scrollview' }
259- keyExtractor = { keyExtractor }
260- data = { messages }
261- style = { styles . messagesView }
262- contentContainerStyle = { {
263- paddingBottom : Platform . OS === 'web' ? 0 : 20 ,
264- flexGrow : 1 ,
265- justifyContent : 'flex-end' ,
266- flexDirection : 'column' ,
267- } }
268- ref = { scrollViewRef }
269- renderItem = { renderItem }
270- onScroll = { onScroll }
271- />
272- { messages . length === 0 && (
273- < View style = { { padding : 16 } } >
274- < Text type = { 'h1' } > { t ( 'app.messaging.no_messages' ) } </ Text >
275- < Text > { t ( 'app.messaging.no_messages_body' ) } </ Text >
276- </ View >
277- ) }
278- </ View >
279- ) }
191+ < View key = { 'messageview-outer-container' } style = { { flex : 1 } } >
192+ < FlatList
193+ key = { 'messageview-scrollview' }
194+ keyExtractor = { keyExtractor }
195+ data = { messages }
196+ style = { styles . messagesView }
197+ contentContainerStyle = { {
198+ paddingBottom : Platform . OS === 'web' ? 0 : 20 ,
199+ flexGrow : 1 ,
200+ justifyContent : 'flex-end' ,
201+ flexDirection : 'column' ,
202+ } }
203+ ref = { scrollViewRef }
204+ renderItem = { renderItem }
205+ onScroll = { onScroll }
206+ />
207+ { messages . length === 0 && (
208+ < View style = { { padding : 16 } } >
209+ < Text type = { 'h1' } > { t ( 'app.messaging.no_messages' ) } </ Text >
210+ < Text > { t ( 'app.messaging.no_messages_body' ) } </ Text >
211+ </ View >
212+ ) }
213+ </ View >
280214 < MessageBox channel = { channel } />
281215 </ ErrorBoundary >
282216 ) ;
283217 } ,
284218) ;
219+
220+ function handleNewMessage (
221+ channel : Channel ,
222+ handledMessages : string [ ] ,
223+ atEndOfPage : boolean ,
224+ setMessages : Dispatch < SetStateAction < RevoltMessage [ ] > > ,
225+ scrollViewRef : RefObject < FlatList > ,
226+ setError : ( error : any ) => void ,
227+ msg : RevoltMessage ,
228+ ) {
229+ console . log ( `[NEWMESSAGEVIEW] Handling new message ${ msg . _id } ` , ) ;
230+
231+ if ( msg . channel !== channel || handledMessages . includes ( msg . _id ) ) {
232+ return ;
233+ }
234+
235+ // set this before anything happens that might change it
236+ const shouldScroll = atEndOfPage ;
237+ try {
238+ console . log ( atEndOfPage ) ;
239+ handledMessages . push ( msg . _id ) ;
240+ console . log (
241+ `[NEWMESSAGEVIEW] New message ${ msg . _id } is in current channel; pushing it to the message list... (debug: will scroll = ${ atEndOfPage } )` ,
242+ ) ;
243+ setMessages ( oldMessages => [ ...oldMessages , msg ] ) ;
244+ if ( shouldScroll ) {
245+ scrollViewRef . current ?. scrollToEnd ( ) ;
246+ }
247+ } catch ( err ) {
248+ console . log (
249+ `[NEWMESSAGEVIEW] Error pushing new message (${ msg . _id } ): ${ err } ` ,
250+ ) ;
251+ setError ( err ) ;
252+ }
253+ }
254+
255+ function handleMessageDeletion (
256+ channel : Channel ,
257+ setMessages : Dispatch < SetStateAction < RevoltMessage [ ] > > ,
258+ id : string ,
259+ msg ?: RevoltMessage ,
260+ ) {
261+ if ( msg ?. channel ?. _id === channel . _id ) {
262+ setMessages ( oldMessages => oldMessages . filter ( m => m . _id !== id ) ) ;
263+ }
264+ }
265+
266+ export const MessageView = observer ( ( { channel} : { channel : Channel } ) => {
267+ const { currentTheme} = useContext ( ThemeContext ) ;
268+
269+ const handledMessages = useRef < string [ ] > ( [ ] ) ;
270+
271+ const [ messages , setMessages ] = useState < RevoltMessage [ ] > ( [ ] ) ;
272+
273+ const [ loading , setLoading ] = useState ( true ) ;
274+ const atEndOfPage = useRef ( false ) ;
275+ const [ error , setError ] = useState ( null as any ) ;
276+
277+ const scrollViewRef = useRef < FlatList > ( null ) ;
278+
279+ function fetchMoreMessages ( messageID : string ) {
280+ fetchMessages (
281+ channel ,
282+ {
283+ type : 'before' ,
284+ id : messageID ,
285+ } ,
286+ messages ,
287+ ) . then ( newMsgs => {
288+ setMessages ( newMsgs ) ;
289+ } ) ;
290+ }
291+
292+ useEffect ( ( ) => {
293+ console . log ( `[NEWMESSAGEVIEW] Fetching messages for ${ channel . _id } ...` ) ;
294+ async function getMessages ( ) {
295+ const msgs = await fetchMessages ( channel , { } , [ ] ) ;
296+ console . log (
297+ `[NEWMESSAGEVIEW] Pushing ${ msgs . length } initial message(s) for ${ channel . _id } ...` ,
298+ ) ;
299+ setMessages ( msgs ) ;
300+ setLoading ( false ) ;
301+ }
302+
303+ function cleanupMessages ( ) {
304+ setLoading ( true ) ;
305+ setMessages ( [ ] ) ;
306+ }
307+
308+ try {
309+ getMessages ( ) ;
310+ } catch ( err ) {
311+ console . log (
312+ `[NEWMESSAGEVIEW] Error fetching initial messages for ${ channel . _id } : ${ err } ` ,
313+ ) ;
314+ setError ( err ) ;
315+ }
316+
317+ // called when switching channels
318+ return ( ) => cleanupMessages ( ) ;
319+ } , [ channel ] ) ;
320+
321+ useEffect ( ( ) => {
322+ console . log ( `[NEWMESSAGEVIEW] Setting up listeners for ${ channel . _id } ...` ) ;
323+
324+ function onNewMessage ( msg : RevoltMessage ) {
325+ handleNewMessage (
326+ channel ,
327+ handledMessages . current ,
328+ atEndOfPage . current ,
329+ setMessages ,
330+ scrollViewRef ,
331+ setError ,
332+ msg ,
333+ ) ;
334+ }
335+
336+ function onMessageDeletion ( id : string , msg ?: RevoltMessage ) {
337+ handleMessageDeletion ( channel , setMessages , id , msg ) ;
338+ }
339+
340+ function setUpListeners ( ) {
341+ client . addListener ( 'message' , msg => onNewMessage ( msg ) ) ;
342+ client . addListener ( 'message/delete' , ( id , msg ) => onMessageDeletion ( id , msg ) ) ;
343+ }
344+
345+ function cleanupListeners ( ) {
346+ client . removeListener ( 'message' ) ;
347+ client . removeListener ( 'message/delete' ) ;
348+ }
349+
350+ try {
351+ setUpListeners ( ) ;
352+ } catch ( err ) {
353+ console . log (
354+ `[NEWMESSAGEVIEW] Error seting up listeners for ${ channel . _id } : ${ err } ` ,
355+ ) ;
356+ setError ( err ) ;
357+ }
358+
359+ // called when switching channels
360+ return ( ) => cleanupListeners ( ) ;
361+ } , [ channel ] ) ;
362+
363+ return (
364+ < ErrorBoundary fallbackRender = { MessageViewErrorMessage } >
365+ { error ? (
366+ < Text colour = { currentTheme . error } >
367+ Error rendering messages: { error . message ?? error }
368+ </ Text >
369+ ) : loading ? (
370+ < LoadingScreen />
371+ ) : (
372+ < NewMessageView
373+ channel = { channel }
374+ atEndOfPage = { atEndOfPage }
375+ messages = { messages }
376+ fetchMoreMessages = { fetchMoreMessages }
377+ scrollViewRef = { scrollViewRef }
378+ />
379+ ) }
380+ </ ErrorBoundary >
381+ ) ;
382+ } ) ;
0 commit comments