fix: chat starts scrolled to bottom instead of visibly scrolling down
- Use onContentSizeChange to scroll after FlashList renders content - Scroll without animation on initial load via needsInitialScroll ref - Remove unreliable 100ms timeout scrollToEnd from message loading
This commit is contained in:
@@ -634,7 +634,7 @@ NODE_ENV=development # development = pretty logs, production = JSON
|
|||||||
- Tracks conversationId for message continuity across sessions
|
- Tracks conversationId for message continuity across sessions
|
||||||
- ChatStore with addMessages() for bulk loading, chatMessageToMessageData() helper
|
- ChatStore with addMessages() for bulk loading, chatMessageToMessageData() helper
|
||||||
- KeyboardAvoidingView for proper keyboard handling (iOS padding, Android height)
|
- KeyboardAvoidingView for proper keyboard handling (iOS padding, Android height)
|
||||||
- Auto-scroll to end on new messages and keyboard show
|
- Auto-scroll to end on new messages and keyboard show; initial load uses `onContentSizeChange` with `animated: false` to start at bottom without visible scrolling
|
||||||
- keyboardDismissMode="interactive" and keyboardShouldPersistTaps="handled"
|
- keyboardDismissMode="interactive" and keyboardShouldPersistTaps="handled"
|
||||||
- `EventService`: getAll(), getById(), getByDateRange(), create(), update(), delete(mode, occurrenceDate) - fully implemented with recurring delete modes
|
- `EventService`: getAll(), getById(), getByDateRange(), create(), update(), delete(mode, occurrenceDate) - fully implemented with recurring delete modes
|
||||||
- `ChatService`: sendMessage(), confirmEvent(deleteMode, occurrenceDate), rejectEvent(), getConversations(), getConversation(), updateProposalEvent() - fully implemented with cursor pagination, recurring delete support, and proposal editing
|
- `ChatService`: sendMessage(), confirmEvent(deleteMode, occurrenceDate), rejectEvent(), getConversations(), getConversation(), updateProposalEvent() - fully implemented with cursor pagination, recurring delete support, and proposal editing
|
||||||
|
|||||||
@@ -64,11 +64,11 @@ const Chat = () => {
|
|||||||
string | undefined
|
string | undefined
|
||||||
>();
|
>();
|
||||||
const [hasLoadedMessages, setHasLoadedMessages] = useState(false);
|
const [hasLoadedMessages, setHasLoadedMessages] = useState(false);
|
||||||
|
const needsInitialScroll = useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const keyboardDidShow = Keyboard.addListener(
|
const keyboardDidShow = Keyboard.addListener("keyboardDidShow", () =>
|
||||||
"keyboardDidShow",
|
scrollToEnd(),
|
||||||
scrollToEnd,
|
|
||||||
);
|
);
|
||||||
return () => keyboardDidShow.remove();
|
return () => keyboardDidShow.remove();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -90,7 +90,7 @@ const Chat = () => {
|
|||||||
await ChatService.getConversation(conversationId);
|
await ChatService.getConversation(conversationId);
|
||||||
const clientMessages = serverMessages.map(chatMessageToMessageData);
|
const clientMessages = serverMessages.map(chatMessageToMessageData);
|
||||||
addMessages(clientMessages);
|
addMessages(clientMessages);
|
||||||
scrollToEnd();
|
needsInitialScroll.current = true;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to load messages:", error);
|
console.error("Failed to load messages:", error);
|
||||||
@@ -102,9 +102,9 @@ const Chat = () => {
|
|||||||
}, [isAuthLoading, isAuthenticated, hasLoadedMessages]),
|
}, [isAuthLoading, isAuthenticated, hasLoadedMessages]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const scrollToEnd = () => {
|
const scrollToEnd = (animated = true) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
listRef.current?.scrollToEnd({ animated: true });
|
listRef.current?.scrollToEnd({ animated });
|
||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -277,6 +277,12 @@ const Chat = () => {
|
|||||||
keyExtractor={(item) => item.id}
|
keyExtractor={(item) => item.id}
|
||||||
keyboardDismissMode="interactive"
|
keyboardDismissMode="interactive"
|
||||||
keyboardShouldPersistTaps="handled"
|
keyboardShouldPersistTaps="handled"
|
||||||
|
onContentSizeChange={() => {
|
||||||
|
if (needsInitialScroll.current) {
|
||||||
|
needsInitialScroll.current = false;
|
||||||
|
listRef.current?.scrollToEnd({ animated: false });
|
||||||
|
}
|
||||||
|
}}
|
||||||
ListFooterComponent={
|
ListFooterComponent={
|
||||||
isWaitingForResponse ? <TypingIndicator /> : null
|
isWaitingForResponse ? <TypingIndicator /> : null
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user