forum scroll down

This commit is contained in:
PhilReact 2024-09-14 19:51:31 +03:00
parent 753ba200d4
commit a6db86f051
2 changed files with 207 additions and 106 deletions

View File

@ -147,7 +147,8 @@ export const NewThread = ({
getSecretKey, getSecretKey,
closeCallback, closeCallback,
postReply, postReply,
myName myName,
setPostReply
}: NewMessageProps) => { }: NewMessageProps) => {
const { show } = React.useContext(MyContext); const { show } = React.useContext(MyContext);
@ -171,6 +172,7 @@ export const NewThread = ({
const closeModal = () => { const closeModal = () => {
setIsOpen(false); setIsOpen(false);
setValue(""); setValue("");
setPostReply(null)
}; };
async function publishQDNResource() { async function publishQDNResource() {
@ -399,7 +401,8 @@ export const NewThread = ({
> >
<ComposeContainer <ComposeContainer
sx={{ sx={{
padding: "15px", padding: isMobile ? '5px' : "15px",
justifyContent: isMobile ? 'flex-start' : 'revert'
}} }}
onClick={() => setIsOpen(true)} onClick={() => setIsOpen(true)}
> >

View File

@ -1,10 +1,6 @@
import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from "react"; import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Box, Button, IconButton, Skeleton } from "@mui/material"; import { Box, Button, IconButton, Skeleton } from "@mui/material";
import { ShowMessage } from "./ShowMessageWithoutModal"; import { ShowMessage } from "./ShowMessageWithoutModal";
// import {
// setIsLoadingCustom,
// } from '../../state/features/globalSlice'
import { import {
ComposeP, ComposeP,
GroupContainer, GroupContainer,
@ -24,8 +20,8 @@ import { decryptPublishes, getTempPublish } from "../../Chat/GroupAnnouncements"
import { LoadingSnackbar } from "../../Snackbar/LoadingSnackbar"; import { LoadingSnackbar } from "../../Snackbar/LoadingSnackbar";
import { subscribeToEvent, unsubscribeFromEvent } from "../../../utils/events"; import { subscribeToEvent, unsubscribeFromEvent } from "../../../utils/events";
import RefreshIcon from "@mui/icons-material/Refresh"; import RefreshIcon from "@mui/icons-material/Refresh";
import { getBaseApi } from "../../../background"; import { getBaseApiReact, isMobile } from "../../../App";
import { getBaseApiReact } from "../../../App"; import { ArrowDownward as ArrowDownwardIcon, ArrowUpward as ArrowUpwardIcon } from '@mui/icons-material';
interface ThreadProps { interface ThreadProps {
currentThread: any; currentThread: any;
@ -56,7 +52,6 @@ export const Thread = ({
updateThreadActivityCurrentThread updateThreadActivityCurrentThread
}: ThreadProps) => { }: ThreadProps) => {
const [tempPublishedList, setTempPublishedList] = useState([]) const [tempPublishedList, setTempPublishedList] = useState([])
const [messages, setMessages] = useState<any[]>([]); const [messages, setMessages] = useState<any[]>([]);
const [hashMapMailMessages, setHashMapMailMessages] = useState({}); const [hashMapMailMessages, setHashMapMailMessages] = useState({});
const [hasFirstPage, setHasFirstPage] = useState(false); const [hasFirstPage, setHasFirstPage] = useState(false);
@ -66,9 +61,17 @@ export const Thread = ({
const [postReply, setPostReply] = useState(null); const [postReply, setPostReply] = useState(null);
const [hasLastPage, setHasLastPage] = useState(false); const [hasLastPage, setHasLastPage] = useState(false);
// Update: Use a new ref for the scrollable container
const threadContainerRef = useRef(null);
// New state variables
const [showScrollButton, setShowScrollButton] = useState(false);
const [isAtBottom, setIsAtBottom] = useState(false);
const secretKeyRef = useRef(null); const secretKeyRef = useRef(null);
const currentThreadRef = useRef(null); const currentThreadRef = useRef(null);
const containerRef = useRef(null); const containerRef = useRef(null);
useEffect(() => { useEffect(() => {
currentThreadRef.current = currentThread; currentThreadRef.current = currentThread;
}, [currentThread]); }, [currentThread]);
@ -84,7 +87,6 @@ export const Thread = ({
name: message.name, name: message.name,
secretKey, secretKey,
}); });
const fullObject = { const fullObject = {
...message, ...message,
@ -97,35 +99,34 @@ export const Thread = ({
[message.identifier]: fullObject, [message.identifier]: fullObject,
}; };
}); });
} catch (error) {} } catch (error) { }
}; };
const setTempData = async ()=> { const setTempData = async () => {
try { try {
let threadId = currentThread.threadId; let threadId = currentThread.threadId;
const keyTemp = 'thread-post' const keyTemp = 'thread-post'
const getTempAnnouncements = await getTempPublish() const getTempAnnouncements = await getTempPublish()
if(getTempAnnouncements?.[keyTemp]){
let tempData = []
Object.keys(getTempAnnouncements?.[keyTemp] || {}).map((key)=> {
const value = getTempAnnouncements?.[keyTemp][key]
if(value.data?.threadId === threadId){
tempData.push(value.data)
}
})
setTempPublishedList(tempData)
}
} catch (error) {
}
}
if (getTempAnnouncements?.[keyTemp]) {
let tempData = []
Object.keys(getTempAnnouncements?.[keyTemp] || {}).map((key) => {
const value = getTempAnnouncements?.[keyTemp][key]
if (value.data?.threadId === threadId) {
tempData.push(value.data)
}
})
setTempPublishedList(tempData)
}
} catch (error) {
}
}
const getMailMessages = React.useCallback( const getMailMessages = React.useCallback(
async (groupInfo: any, before, after, isReverse) => { async (groupInfo: any, before, after, isReverse) => {
@ -137,7 +138,7 @@ export const Thread = ({
setHasLastPage(false); setHasLastPage(false);
setHasNextPage(false); setHasNextPage(false);
let threadId = groupInfo.threadId; let threadId = groupInfo.threadId;
const identifier = `thmsg-${threadId}`; const identifier = `thmsg-${threadId}`;
let url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&prefix=true`; let url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&prefix=true`;
if (!isReverse) { if (!isReverse) {
@ -160,7 +161,7 @@ export const Thread = ({
}, },
}); });
const responseData = await response.json(); const responseData = await response.json();
let fullArrayMsg = [...responseData]; let fullArrayMsg = [...responseData];
if (isReverse) { if (isReverse) {
@ -175,13 +176,13 @@ export const Thread = ({
setTimeout(() => { setTimeout(() => {
containerRef.current.scrollIntoView({ behavior: "smooth" }); containerRef.current.scrollIntoView({ behavior: "smooth" });
}, 300); }, 300);
} }
if (fullArrayMsg.length === 0){ if (fullArrayMsg.length === 0) {
setTempData() setTempData()
return; return;
} }
// check if there are newer posts // check if there are newer posts
const urlNewer = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=1&includemetadata=false&reverse=false&prefix=true&before=${fullArrayMsg[0].created}`; const urlNewer = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=1&includemetadata=false&reverse=false&prefix=true&before=${fullArrayMsg[0].created}`;
const responseNewer = await fetch(urlNewer, { const responseNewer = await fetch(urlNewer, {
@ -201,7 +202,7 @@ export const Thread = ({
// check if there are older posts // check if there are older posts
const urlOlder = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=1&includemetadata=false&reverse=false&prefix=true&after=${ const urlOlder = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=1&includemetadata=false&reverse=false&prefix=true&after=${
fullArrayMsg[fullArrayMsg.length - 1].created fullArrayMsg[fullArrayMsg.length - 1].created
}`; }`;
const responseOlder = await fetch(urlOlder, { const responseOlder = await fetch(urlOlder, {
method: "GET", method: "GET",
headers: { headers: {
@ -221,13 +222,12 @@ export const Thread = ({
} catch (error) { } catch (error) {
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
}, },
[messages, secretKey] [messages, secretKey]
); );
const getMessages = React.useCallback(async () => { const getMessages = React.useCallback(async () => {
if (!currentThread || !secretKey) return; if (!currentThread || !secretKey) return;
await getMailMessages(currentThread, null, null, false); await getMailMessages(currentThread, null, null, false);
}, [getMailMessages, currentThread, secretKey]); }, [getMailMessages, currentThread, secretKey]);
@ -287,8 +287,6 @@ export const Thread = ({
} }
if (currentThread && secretKey && !firstMount.current) { if (currentThread && secretKey && !firstMount.current) {
getMessagesMiddleware(); getMessagesMiddleware();
// saveTimestamp(currentThread, user.name)
} }
}, [currentThread, secretKey]); }, [currentThread, secretKey]);
const messageCallback = useCallback((msg: any) => { const messageCallback = useCallback((msg: any) => {
@ -350,7 +348,7 @@ export const Thread = ({
} else { } else {
fullArrayMsg.unshift(fullObject); fullArrayMsg.unshift(fullObject);
} }
} catch (error) {} } catch (error) { }
} }
setMessages(fullArrayMsg); setMessages(fullArrayMsg);
} catch (error) { } catch (error) {
@ -360,25 +358,6 @@ export const Thread = ({
[messages] [messages]
); );
// const checkNewMessagesFunc = useCallback(() => {
// let isCalling = false
// interval.current = setInterval(async () => {
// if (isCalling) return
// isCalling = true
// const res = await checkNewMessages(currentThread)
// isCalling = false
// }, 8000)
// }, [checkNewMessages, currentThrefirstMount.current = truead])
// useEffect(() => {
// checkNewMessagesFunc()
// return () => {
// if (interval?.current) {
// clearInterval(interval.current)
// }
// }
// }, [checkNewMessagesFunc])
const openNewPostWithQuote = useCallback((reply) => { const openNewPostWithQuote = useCallback((reply) => {
setPostReply(reply); setPostReply(reply);
}, []); }, []);
@ -406,44 +385,153 @@ export const Thread = ({
const combinedListTempAndReal = useMemo(() => { const combinedListTempAndReal = useMemo(() => {
// Combine the two lists // Combine the two lists
const combined = [...tempPublishedList, ...messages]; const combined = [...tempPublishedList, ...messages];
// Remove duplicates based on the "identifier" // Remove duplicates based on the "identifier"
const uniqueItems = new Map(); const uniqueItems = new Map();
combined.forEach(item => { combined.forEach(item => {
uniqueItems.set(item.identifier, item); // This will overwrite duplicates, keeping the last occurrence uniqueItems.set(item.identifier, item); // This will overwrite duplicates, keeping the last occurrence
}); });
// Convert the map back to an array and sort by "created" timestamp in descending order // Convert the map back to an array and sort by "created" timestamp in descending order
const sortedList = Array.from(uniqueItems.values()).sort((a, b) => a.created - b.created); const sortedList = Array.from(uniqueItems.values()).sort((a, b) => a.created - b.created);
return sortedList; return sortedList;
}, [tempPublishedList, messages]); }, [tempPublishedList, messages]);
// Updated useEffect to handle scroll and overflow
useEffect(() => {
const container = threadContainerRef.current; // Updated reference
if (!container) return;
const handleScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = container;
// Check if user is at the bottom
if (scrollTop + clientHeight >= scrollHeight - 5) {
setIsAtBottom(true);
} else {
setIsAtBottom(false);
}
// Initial check if content overflows
if (container.scrollHeight > container.clientHeight) {
setShowScrollButton(true);
} else {
setShowScrollButton(false);
}
};
setTimeout(() => {
handleScroll()
}, 400);
container.addEventListener('scroll', handleScroll);
// Cleanup
return () => {
container.removeEventListener('scroll', handleScroll);
};
}, [messages]);
// Function to scroll to the top or bottom of the container
const scrollToPosition = () => {
const container = threadContainerRef.current; // Updated reference
if (!container) return;
if (isAtBottom) {
container.scrollTo({ top: 0, behavior: 'smooth' }); // Scroll to top
} else {
container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); // Scroll to bottom
}
};
console.log('showScrollButton', showScrollButton)
if (!currentThread) return null; if (!currentThread) return null;
return ( return (
<GroupContainer <GroupContainer
sx={{ sx={{
position: "relative", position: "relative",
overflow: "auto",
width: "100%", width: "100%",
display: 'flex',
flexDirection: 'column',
overflow: 'hidden'
}} }}
// Removed the ref from here since the scrollable area has changed
> >
<NewThread <Box sx={{
groupInfo={groupInfo} display: 'flex',
isMessage={true} justifyContent: 'space-between',
currentThread={currentThread} alignItems: 'center',
messageCallback={messageCallback} flexShrink: 0 // Corrected property name
members={members} }}>
userInfo={userInfo} <NewThread
getSecretKey={getSecretKey} groupInfo={groupInfo}
closeCallback={closeCallback} isMessage={true}
postReply={postReply} currentThread={currentThread}
myName={userInfo?.name} messageCallback={messageCallback}
publishCallback={setTempData} members={members}
/> userInfo={userInfo}
<ThreadContainerFullWidth> getSecretKey={getSecretKey}
<ThreadContainer > closeCallback={closeCallback}
<Spacer height="30px" /> postReply={postReply}
myName={userInfo?.name}
publishCallback={setTempData}
setPostReply={setPostReply}
/>
<Box sx={{
display: 'flex',
gap: isMobile ? '5px' : '25px',
alignItems: 'center'
}}>
<ShowMessageReturnButton
sx={{
padding: isMobile && '5px',
minWidth: isMobile && '50px'
}}
onClick={() => {
setMessages([]);
closeThread();
}}
>
<MailIconImg src={ReturnSVG} />
{!isMobile && (
<ComposeP>Return to Threads</ComposeP>
)}
</ShowMessageReturnButton>
{/* Conditionally render the scroll buttons */}
{showScrollButton && (
isAtBottom ? (
<ArrowUpwardIcon
onClick={scrollToPosition}
sx={{
color: 'white',
cursor: 'pointer',
fontSize: isMobile ? '28px' : '36px',
}}
/>
) : (
<ArrowDownwardIcon
onClick={scrollToPosition}
sx={{
color: 'white',
cursor: 'pointer',
fontSize: isMobile ? '28px' : '36px',
}}
/>
)
)}
</Box>
</Box>
<ThreadContainerFullWidth
sx={{
flexGrow: 1,
overflow: 'auto'
}}
ref={threadContainerRef} // Updated ref attached here
>
<ThreadContainer>
<Spacer height={isMobile ? '10px' : '30px'} />
<Box <Box
sx={{ sx={{
width: "100%", width: "100%",
@ -452,18 +540,12 @@ export const Thread = ({
justifyContent: "space-between", justifyContent: "space-between",
}} }}
> >
<GroupNameP>{currentThread?.threadData?.title}</GroupNameP> <GroupNameP sx={{
fontSize: isMobile && '18px'
<ShowMessageReturnButton }}>{currentThread?.threadData?.title}</GroupNameP>
onClick={() => {
setMessages([]);
closeThread();
}}
>
<MailIconImg src={ReturnSVG} />
<ComposeP>Return to Threads</ComposeP>
</ShowMessageReturnButton>
</Box> </Box>
<Spacer height={'15px'} />
<Box <Box
sx={{ sx={{
width: "100%", width: "100%",
@ -474,15 +556,25 @@ export const Thread = ({
}} }}
> >
<Button <Button
sx={{
padding: isMobile && '5px',
fontSize: isMobile && '14px',
textTransformation: 'capitalize'
}}
onClick={() => { onClick={() => {
getMailMessages(currentThread, null, null, false); getMailMessages(currentThread, null, null, false);
}} }}
disabled={!hasFirstPage} disabled={!hasFirstPage}
variant="contained" variant="contained"
> >
First Page First
</Button> </Button>
<Button <Button
sx={{
padding: isMobile && '5px',
fontSize: isMobile && '14px',
textTransformation: 'capitalize'
}}
onClick={() => { onClick={() => {
getMailMessages( getMailMessages(
currentThread, currentThread,
@ -494,9 +586,14 @@ export const Thread = ({
disabled={!hasPreviousPage} disabled={!hasPreviousPage}
variant="contained" variant="contained"
> >
Previous Page Previous
</Button> </Button>
<Button <Button
sx={{
padding: isMobile && '5px',
fontSize: isMobile && '14px',
textTransformation: 'capitalize'
}}
onClick={() => { onClick={() => {
getMailMessages( getMailMessages(
currentThread, currentThread,
@ -508,21 +605,26 @@ export const Thread = ({
disabled={!hasNextPage} disabled={!hasNextPage}
variant="contained" variant="contained"
> >
Next page Next
</Button> </Button>
<Button <Button
sx={{
padding: isMobile && '5px',
fontSize: isMobile && '14px',
textTransformation: 'capitalize'
}}
onClick={() => { onClick={() => {
getMailMessages(currentThread, null, null, true); getMailMessages(currentThread, null, null, true);
}} }}
disabled={!hasLastPage} disabled={!hasLastPage}
variant="contained" variant="contained"
> >
Last page Last
</Button> </Button>
</Box> </Box>
<Spacer height="60px" /> <Spacer height={isMobile ? '10px' : '30px'} />
{combinedListTempAndReal.map((message) => { {combinedListTempAndReal.map((message, index, list) => {
let fullMessage = message; let fullMessage = message;
if (hashMapMailMessages[message?.identifier]) { if (hashMapMailMessages[message?.identifier]) {
fullMessage = hashMapMailMessages[message.identifier]; fullMessage = hashMapMailMessages[message.identifier];
@ -534,7 +636,7 @@ export const Thread = ({
myName={userInfo?.name} myName={userInfo?.name}
/> />
); );
} else if(message?.tempData){ } else if (message?.tempData) {
return ( return (
<ShowMessage <ShowMessage
key={message?.identifier} key={message?.identifier}
@ -650,10 +752,6 @@ export const Thread = ({
)} )}
</ThreadContainer> </ThreadContainer>
</ThreadContainerFullWidth> </ThreadContainerFullWidth>
{/* {messages.length >= 20 && (
<LazyLoad onLoadMore={()=> getMailMessages(currentThread, false, true)}></LazyLoad>
)} */}
<LoadingSnackbar <LoadingSnackbar
open={isLoading} open={isLoading}
info={{ info={{