Skip to content

Commit 1fbb552

Browse files
committed
fix: stop duplicating message listeners + refactor message view
1 parent 658f527 commit 1fbb552

File tree

2 files changed

+204
-111
lines changed

2 files changed

+204
-111
lines changed

src/MessageView.tsx

Lines changed: 202 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {useContext, useEffect, useRef, useState} from 'react';
2+
import type {Dispatch, RefObject, SetStateAction} from 'react';
23
import {
34
FlatList,
45
NativeSyntheticEvent,
@@ -13,7 +14,7 @@ import {observer} from 'mobx-react-lite';
1314

1415
import {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

1819
import {app} from './Generic';
1920
import {client} from './lib/client';
@@ -130,72 +131,21 @@ function MessageViewErrorMessage({
130131
export 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+
});

src/components/views/ChannelView.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import type {Channel} from 'revolt.js';
1010

1111
import {app} from '@clerotri/Generic';
1212
import {Messages} from '@clerotri/LegacyMessageView';
13-
import {NewMessageView} from '@clerotri/MessageView';
13+
import {MessageView} from '@clerotri/MessageView';
1414
import {MessageBox} from '@clerotri/components/MessageBox';
1515
import {styles} from '@clerotri/Theme';
1616
import {Button, Text} from '@clerotri/components/common/atoms';
@@ -77,8 +77,6 @@ const RegularChannelView = observer(({channel}: {channel: Channel}) => {
7777

7878
const [renderCount, rerender] = useState(0);
7979

80-
const handledMessages = [] as string[];
81-
8280
return (
8381
<View style={styles.flex}>
8482
<ChannelHeader
@@ -140,10 +138,7 @@ const RegularChannelView = observer(({channel}: {channel: Channel}) => {
140138
) : !channel?.nsfw || app.settings.get('ui.messaging.showNSFWContent') ? (
141139
<ErrorBoundary fallbackRender={MessageViewErrorMessage}>
142140
{app.settings.get('ui.messaging.useNewMessageView') ? (
143-
<NewMessageView
144-
channel={channel}
145-
handledMessages={handledMessages}
146-
/>
141+
<MessageView channel={channel} />
147142
) : (
148143
<>
149144
<Messages

0 commit comments

Comments
 (0)