Skip to content

Commit 1e07576

Browse files
committed
--wip-- [skip ci]
1 parent d0a0721 commit 1e07576

2 files changed

Lines changed: 53 additions & 8 deletions

File tree

src/components/Chat/Chat.tsx

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
TextInput,
1616
useWindowDimensions,
1717
View,
18+
NativeSyntheticEvent,
19+
NativeScrollEvent,
1820
} from 'react-native';
1921
import { createStyleSheet, useStyles } from 'react-native-unistyles';
2022
import { Button } from '../Button';
@@ -90,6 +92,13 @@ export const Chat = memo(({ channelName, channelId }: ChatProps) => {
9092
const [connectionError, setConnectionError] = useState<string | null>(null);
9193
const [isConnecting, setIsConnecting] = useState<boolean>(false);
9294
const [showEmotePicker, setShowEmotePicker] = useState<boolean>(false);
95+
const [paused, setPaused] = useState(false);
96+
const pausedRef = useRef(paused);
97+
98+
// Keep pausedRef in sync with paused
99+
useEffect(() => {
100+
pausedRef.current = paused;
101+
}, [paused]);
93102

94103
const connectToChat = async () => {
95104
if (isConnecting) return;
@@ -155,18 +164,15 @@ export const Chat = memo(({ channelName, channelId }: ChatProps) => {
155164
sender: userstate.username || '',
156165
};
157166

158-
// Append the new message to the existing messages
159-
messagesRef.current = [...messagesRef.current, newMessage];
160-
setMessages([...messagesRef.current]);
161-
162-
// Scroll to the end of the chat
163-
flashListRef.current?.scrollToEnd({ animated: false });
167+
addMessageAndMaybeScroll(newMessage);
164168
});
165169

166170
client.on('clearchat', () => {
167171
messagesRef.current = [];
168172
setMessages([]);
169-
flashListRef.current?.scrollToEnd({ animated: false });
173+
setTimeout(() => {
174+
flashListRef.current?.scrollToEnd({ animated: false });
175+
}, 0);
170176
});
171177

172178
client.on('disconnected', reason => {
@@ -187,6 +193,35 @@ export const Chat = memo(({ channelName, channelId }: ChatProps) => {
187193
// eslint-disable-next-line react-hooks/exhaustive-deps
188194
}, [status]);
189195

196+
// Helper to check if user is at the bottom
197+
const handleScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
198+
const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent;
199+
const paddingToBottom = 20;
200+
const isAtBottom =
201+
layoutMeasurement.height + contentOffset.y >=
202+
contentSize.height - paddingToBottom;
203+
setPaused(!isAtBottom);
204+
};
205+
206+
// Resume chat when user taps overlay
207+
const handleResume = () => {
208+
setPaused(false);
209+
setTimeout(() => {
210+
flashListRef.current?.scrollToEnd({ animated: true });
211+
}, 50);
212+
};
213+
214+
// Only auto-scroll if not paused
215+
const addMessageAndMaybeScroll = (newMessage: ChatMessageProps) => {
216+
messagesRef.current = [...messagesRef.current, newMessage];
217+
setMessages([...messagesRef.current]);
218+
if (!pausedRef.current) {
219+
setTimeout(() => {
220+
flashListRef.current?.scrollToEnd({ animated: false });
221+
}, 0);
222+
}
223+
};
224+
190225
if (status === 'loading') {
191226
return (
192227
<View>
@@ -238,7 +273,17 @@ export const Chat = memo(({ channelName, channelId }: ChatProps) => {
238273
offset: 40 * index,
239274
index,
240275
})}
276+
onScroll={handleScroll}
277+
scrollEventThrottle={16}
241278
/>
279+
{paused && (
280+
<View style={styles.pausedOverlay}>
281+
<Typography style={styles.pausedText}>Chat Paused</Typography>
282+
<Button style={styles.resumeButton} onPress={handleResume}>
283+
<Typography style={styles.resumeButtonText}>Resume</Typography>
284+
</Button>
285+
</View>
286+
)}
242287
</View>
243288
<View style={styles.inputContainer}>
244289
<Button

src/components/Chat/ChatMessage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ import { View, ViewStyle } from 'react-native';
1616
import { createStyleSheet, useStyles } from 'react-native-unistyles';
1717
import { toast } from 'sonner-native';
1818
import { ChatUserstate } from 'tmi.js';
19+
import { BrandIcon } from '../BrandIcon';
1920
import { Button } from '../Button';
2021
import { Icon } from '../Icon';
2122
import { Image } from '../Image';
2223
import { ModalHandle } from '../ModalHandle';
2324
import { Typography } from '../Typography';
24-
import { BrandIcon } from '../BrandIcon';
2525

2626
export interface ChatMessageProps {
2727
userstate: ChatUserstate;

0 commit comments

Comments
 (0)