mirror of
https://github.com/Qortal/chrome-extension.git
synced 2025-02-12 02:05:49 +00:00
fix chat scroll
This commit is contained in:
parent
d83bbd40a0
commit
600215fb15
26
package-lock.json
generated
26
package-lock.json
generated
@ -17,6 +17,7 @@
|
|||||||
"@mui/lab": "^5.0.0-alpha.173",
|
"@mui/lab": "^5.0.0-alpha.173",
|
||||||
"@mui/material": "^5.16.7",
|
"@mui/material": "^5.16.7",
|
||||||
"@reduxjs/toolkit": "^2.2.7",
|
"@reduxjs/toolkit": "^2.2.7",
|
||||||
|
"@tanstack/react-virtual": "^3.10.8",
|
||||||
"@testing-library/jest-dom": "^6.4.6",
|
"@testing-library/jest-dom": "^6.4.6",
|
||||||
"@testing-library/user-event": "^14.5.2",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
"@tiptap/extension-color": "^2.5.9",
|
"@tiptap/extension-color": "^2.5.9",
|
||||||
@ -1899,6 +1900,31 @@
|
|||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/react-virtual": {
|
||||||
|
"version": "3.10.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.8.tgz",
|
||||||
|
"integrity": "sha512-VbzbVGSsZlQktyLrP5nxE+vE1ZR+U0NFAWPbJLoG2+DKPwd2D7dVICTVIIaYlJqX1ZCEnYDbaOpmMwbsyhBoIA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/virtual-core": "3.10.8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/virtual-core": {
|
||||||
|
"version": "3.10.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.8.tgz",
|
||||||
|
"integrity": "sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA==",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@testing-library/dom": {
|
"node_modules/@testing-library/dom": {
|
||||||
"version": "10.3.0",
|
"version": "10.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.3.0.tgz",
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"@mui/lab": "^5.0.0-alpha.173",
|
"@mui/lab": "^5.0.0-alpha.173",
|
||||||
"@mui/material": "^5.16.7",
|
"@mui/material": "^5.16.7",
|
||||||
"@reduxjs/toolkit": "^2.2.7",
|
"@reduxjs/toolkit": "^2.2.7",
|
||||||
|
"@tanstack/react-virtual": "^3.10.8",
|
||||||
"@testing-library/jest-dom": "^6.4.6",
|
"@testing-library/jest-dom": "^6.4.6",
|
||||||
"@testing-library/user-event": "^14.5.2",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
"@tiptap/extension-color": "^2.5.9",
|
"@tiptap/extension-color": "^2.5.9",
|
||||||
|
@ -149,7 +149,7 @@ const defaultValues: MyContextInterface = {
|
|||||||
message: "",
|
message: "",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
export let isMobile = true;
|
export let isMobile = false;
|
||||||
|
|
||||||
const isMobileDevice = () => {
|
const isMobileDevice = () => {
|
||||||
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
|
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
|
||||||
|
@ -1,16 +1,25 @@
|
|||||||
import React, { useCallback, useState, useEffect, useRef } from 'react';
|
import React, { useCallback, useState, useEffect, useRef, useMemo } from 'react';
|
||||||
import { Virtuoso } from 'react-virtuoso';
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||||
import { MessageItem } from './MessageItem';
|
import { MessageItem } from './MessageItem';
|
||||||
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
||||||
|
import { useInView } from 'react-intersection-observer'
|
||||||
|
|
||||||
export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onReply, handleReaction, chatReferences, tempChatReferences }) => {
|
export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onReply, handleReaction, chatReferences, tempChatReferences }) => {
|
||||||
const virtuosoRef = useRef();
|
const parentRef = useRef();
|
||||||
const [messages, setMessages] = useState(initialMessages);
|
const [messages, setMessages] = useState(initialMessages);
|
||||||
const [showScrollButton, setShowScrollButton] = useState(false);
|
const [showScrollButton, setShowScrollButton] = useState(false);
|
||||||
const hasLoadedInitialRef = useRef(false);
|
const hasLoadedInitialRef = useRef(false);
|
||||||
const isAtBottomRef = useRef(true); //
|
const isAtBottomRef = useRef(true);
|
||||||
// Update message list with unique signatures and tempMessages
|
// const [ref, inView] = useInView({
|
||||||
|
// threshold: 0.7
|
||||||
|
// })
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (inView) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }, [inView])
|
||||||
|
// Update message list with unique signatures and tempMessages
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let uniqueInitialMessagesMap = new Map();
|
let uniqueInitialMessagesMap = new Map();
|
||||||
|
|
||||||
@ -32,9 +41,12 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const hasUnreadMessages = totalMessages.some((msg) => msg.unread && !msg?.chatReference);
|
const hasUnreadMessages = totalMessages.some((msg) => msg.unread && !msg?.chatReference);
|
||||||
|
console.log('hasUnreadMessages', hasUnreadMessages)
|
||||||
if (virtuosoRef.current) {
|
if (parentRef.current) {
|
||||||
if (virtuosoRef.current && !isAtBottomRef.current && hasUnreadMessages) {
|
const { scrollTop, scrollHeight, clientHeight } = parentRef.current;
|
||||||
|
const atBottom = scrollTop + clientHeight >= scrollHeight - 10; // Adjust threshold as needed
|
||||||
|
console.log('atBottom', atBottom, {scrollTop, scrollHeight, clientHeight})
|
||||||
|
if (!atBottom && hasUnreadMessages) {
|
||||||
setShowScrollButton(hasUnreadMessages);
|
setShowScrollButton(hasUnreadMessages);
|
||||||
} else {
|
} else {
|
||||||
handleMessageSeen();
|
handleMessageSeen();
|
||||||
@ -47,45 +59,31 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
|
|||||||
}, 500);
|
}, 500);
|
||||||
}, [initialMessages, tempMessages]);
|
}, [initialMessages, tempMessages]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleMessageSeen = useCallback(() => {
|
const handleMessageSeen = useCallback(() => {
|
||||||
|
console.log('hello handle seen')
|
||||||
setMessages((prevMessages) =>
|
setMessages((prevMessages) =>
|
||||||
prevMessages.map((msg) => ({
|
prevMessages.map((msg) => ({
|
||||||
...msg,
|
...msg,
|
||||||
unread: false,
|
unread: false,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
setShowScrollButton(false)
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const scrollToItem = useCallback((index) => {
|
// const scrollToBottom = (initialMsgs) => {
|
||||||
if (virtuosoRef.current) {
|
// const index = initialMsgs ? initialMsgs.length - 1 : messages.length - 1;
|
||||||
virtuosoRef.current.scrollToIndex({ index, behavior: 'smooth' });
|
// if (parentRef.current) {
|
||||||
}
|
// parentRef.current.scrollToIndex(index);
|
||||||
}, []);
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
const scrollToBottom = (initialMsgs) => {
|
const scrollToBottom = (initialMsgs) => {
|
||||||
|
const index = initialMsgs ? initialMsgs.length - 1 : messages.length - 1;
|
||||||
const index = initialMsgs ? initialMsgs.length - 1 : messages.length - 1
|
if (rowVirtualizer) {
|
||||||
if (virtuosoRef.current) {
|
rowVirtualizer.scrollToIndex(index, { align: 'end' });
|
||||||
virtuosoRef.current.scrollToIndex({ index});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleScroll = (scrollState) => {
|
|
||||||
const { scrollTop, scrollHeight, clientHeight } = scrollState;
|
|
||||||
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 50;
|
|
||||||
const hasUnreadMessages = messages.some((msg) => msg.unread);
|
|
||||||
|
|
||||||
if (isAtBottom) {
|
|
||||||
handleMessageSeen();
|
|
||||||
}
|
|
||||||
|
|
||||||
setShowScrollButton(!isAtBottom && hasUnreadMessages);
|
|
||||||
};
|
|
||||||
|
|
||||||
const sentNewMessageGroupFunc = useCallback(() => {
|
const sentNewMessageGroupFunc = useCallback(() => {
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}, [messages]);
|
}, [messages]);
|
||||||
@ -97,41 +95,98 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
|
|||||||
};
|
};
|
||||||
}, [sentNewMessageGroupFunc]);
|
}, [sentNewMessageGroupFunc]);
|
||||||
|
|
||||||
const rowRenderer = (index) => {
|
const lastSignature = useMemo(()=> {
|
||||||
let message = messages[index];
|
if(!messages || messages?.length === 0) return null
|
||||||
let replyIndex = messages.findIndex((msg)=> msg?.signature === message?.repliedTo)
|
const lastIndex = messages.length - 1
|
||||||
let reply
|
return messages[lastIndex]?.signature
|
||||||
let reactions = null
|
}, [messages])
|
||||||
if(message?.repliedTo && replyIndex !== -1){
|
|
||||||
reply = messages[replyIndex]
|
console.log('messages', messages)
|
||||||
|
|
||||||
|
// Initialize the virtualizer
|
||||||
|
const rowVirtualizer = useVirtualizer({
|
||||||
|
count: messages.length,
|
||||||
|
getScrollElement: () => parentRef.current,
|
||||||
|
estimateSize: () => 80, // Provide an estimated height of items, adjust this as needed
|
||||||
|
overscan: 10, // Number of items to render outside the visible area to improve smoothness
|
||||||
|
measureElement:
|
||||||
|
typeof window !== 'undefined' &&
|
||||||
|
navigator.userAgent.indexOf('Firefox') === -1
|
||||||
|
? element => {
|
||||||
|
console.log('height', element?.getBoundingClientRect().height)
|
||||||
|
return element?.getBoundingClientRect().height
|
||||||
}
|
}
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div ref={parentRef} style={{ height: '100%', overflow: 'auto', position: 'relative', display: 'flex' }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
position: 'relative',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center', // Center items horizontally
|
||||||
|
gap: '10px', // Add gap between items
|
||||||
|
flexGrow: 1
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
|
||||||
|
const index = virtualRow.index;
|
||||||
|
let message = messages[index];
|
||||||
|
let replyIndex = messages.findIndex((msg) => msg?.signature === message?.repliedTo);
|
||||||
|
let reply;
|
||||||
|
let reactions = null;
|
||||||
|
|
||||||
|
if (message?.repliedTo && replyIndex !== -1) {
|
||||||
|
reply = messages[replyIndex];
|
||||||
|
}
|
||||||
|
|
||||||
if (message?.message && message?.groupDirectId) {
|
if (message?.message && message?.groupDirectId) {
|
||||||
replyIndex = messages.findIndex((msg)=> msg?.signature === message?.message?.repliedTo)
|
replyIndex = messages.findIndex((msg) => msg?.signature === message?.message?.repliedTo);
|
||||||
reply
|
|
||||||
if (message?.message?.repliedTo && replyIndex !== -1) {
|
if (message?.message?.repliedTo && replyIndex !== -1) {
|
||||||
reply = messages[replyIndex]
|
reply = messages[replyIndex];
|
||||||
}
|
}
|
||||||
message = {
|
message = {
|
||||||
...(message?.message || {}),
|
...(message?.message || {}),
|
||||||
isTemp: true,
|
isTemp: true,
|
||||||
unread: false
|
unread: false,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chatReferences && chatReferences[message?.signature]) {
|
if (chatReferences && chatReferences[message?.signature]) {
|
||||||
if (chatReferences[message.signature]?.reactions) {
|
if (chatReferences[message.signature]?.reactions) {
|
||||||
reactions = chatReferences[message.signature]?.reactions
|
reactions = chatReferences[message.signature]?.reactions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let isUpdating = false
|
|
||||||
|
let isUpdating = false;
|
||||||
if (tempChatReferences && tempChatReferences?.find((item) => item?.chatReference === message?.signature)) {
|
if (tempChatReferences && tempChatReferences?.find((item) => item?.chatReference === message?.signature)) {
|
||||||
isUpdating = true
|
isUpdating = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: '10px 0', display: 'flex', justifyContent: 'center', width: '100%', minHeight: '50px' , overscrollBehavior: "none"}}>
|
<div
|
||||||
|
data-index={virtualRow.index} //needed for dynamic row height measurement
|
||||||
|
ref={node => rowVirtualizer.measureElement(node)} //measure dynamic row height
|
||||||
|
key={message.signature}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: '50%', // Move to the center horizontally
|
||||||
|
transform: `translateY(${virtualRow.start}px) translateX(-50%)`, // Adjust for centering
|
||||||
|
width: '100%', // Control width (90% of the parent)
|
||||||
|
padding: '10px 0',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
overscrollBehavior: 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<MessageItem
|
<MessageItem
|
||||||
isLast={index === messages.length - 1}
|
isLast={index === messages.length - 1}
|
||||||
|
lastSignature={lastSignature}
|
||||||
message={message}
|
message={message}
|
||||||
onSeen={handleMessageSeen}
|
onSeen={handleMessageSeen}
|
||||||
isTemp={!!message?.isTemp}
|
isTemp={!!message?.isTemp}
|
||||||
@ -139,36 +194,18 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
|
|||||||
onReply={onReply}
|
onReply={onReply}
|
||||||
reply={reply}
|
reply={reply}
|
||||||
replyIndex={replyIndex}
|
replyIndex={replyIndex}
|
||||||
scrollToItem={scrollToItem}
|
scrollToItem={(idx) => rowVirtualizer.scrollToIndex(idx)}
|
||||||
handleReaction={handleReaction}
|
handleReaction={handleReaction}
|
||||||
reactions={reactions}
|
reactions={reactions}
|
||||||
isUpdating={isUpdating}
|
isUpdating={isUpdating}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
const handleAtBottomStateChange = (atBottom) => {
|
|
||||||
isAtBottomRef.current = atBottom;
|
|
||||||
if(atBottom){
|
|
||||||
handleMessageSeen();
|
|
||||||
setShowScrollButton(false)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ position: 'relative', height: '100%', display: 'flex', flexDirection: 'column' }}>
|
|
||||||
<Virtuoso
|
|
||||||
ref={virtuosoRef}
|
|
||||||
data={messages}
|
|
||||||
itemContent={rowRenderer}
|
|
||||||
atBottomThreshold={50}
|
|
||||||
followOutput="smooth"
|
|
||||||
atBottomStateChange={handleAtBottomStateChange} // Detect bottom status
|
|
||||||
increaseViewportBy={3000}
|
|
||||||
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
</div>
|
||||||
{showScrollButton && (
|
{showScrollButton && (
|
||||||
<button
|
<button
|
||||||
onClick={() => scrollToBottom()}
|
onClick={() => scrollToBottom()}
|
||||||
@ -187,6 +224,7 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
|
|||||||
Scroll to Unread Messages
|
Scroll to Unread Messages
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -29,7 +29,8 @@ export const MessageItem = ({
|
|||||||
scrollToItem,
|
scrollToItem,
|
||||||
handleReaction,
|
handleReaction,
|
||||||
reactions,
|
reactions,
|
||||||
isUpdating
|
isUpdating,
|
||||||
|
lastSignature
|
||||||
}) => {
|
}) => {
|
||||||
const { ref, inView } = useInView({
|
const { ref, inView } = useInView({
|
||||||
threshold: 0.7, // Fully visible
|
threshold: 0.7, // Fully visible
|
||||||
@ -38,13 +39,16 @@ export const MessageItem = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (inView && message.unread) {
|
if (inView && message.unread) {
|
||||||
|
console.log('seenlast')
|
||||||
onSeen(message.id);
|
onSeen(message.id);
|
||||||
}
|
}
|
||||||
}, [inView, message.id, message.unread, onSeen]);
|
}, [inView, message.id, message.unread, onSeen]);
|
||||||
|
|
||||||
|
console.log('isLast', lastSignature === message?.signature)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={isLast ? ref : null}
|
ref={lastSignature === message?.signature ? ref : null}
|
||||||
style={{
|
style={{
|
||||||
padding: "10px",
|
padding: "10px",
|
||||||
backgroundColor: "#232428",
|
backgroundColor: "#232428",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user