Add theme

This commit is contained in:
Nicola Benaglia
2025-04-18 19:16:45 +02:00
parent af1d44990a
commit 2013561db7
6 changed files with 845 additions and 773 deletions

View File

@@ -1,23 +1,15 @@
import React, {
useCallback,
useState,
useEffect,
useRef,
useMemo,
} from "react";
import { useVirtualizer } from "@tanstack/react-virtual";
import { MessageItem } from "./MessageItem";
import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
import { useInView } from "react-intersection-observer";
import { Box, Typography } from "@mui/material";
import { ChatOptions } from "./ChatOptions";
import ErrorBoundary from "../../common/ErrorBoundary";
import { useCallback, useState, useEffect, useRef, useMemo } from 'react';
import { useVirtualizer } from '@tanstack/react-virtual';
import { MessageItem } from './MessageItem';
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
import { Box, Typography, useTheme } from '@mui/material';
import { ChatOptions } from './ChatOptions';
import ErrorBoundary from '../../common/ErrorBoundary';
export const ChatList = ({
initialMessages,
myAddress,
tempMessages,
chatId,
onReply,
onEdit,
handleReaction,
@@ -29,7 +21,7 @@ export const ChatList = ({
enableMentions,
openQManager,
hasSecretKey,
isPrivate
isPrivate,
}) => {
const parentRef = useRef();
const [messages, setMessages] = useState(initialMessages);
@@ -42,33 +34,32 @@ export const ChatList = ({
// Initialize the virtualizer
const rowVirtualizer = useVirtualizer({
count: messages.length,
getItemKey: (index) => messages[index]?.tempSignature || messages[index].signature,
getItemKey: (index) =>
messages[index]?.tempSignature || messages[index].signature,
getScrollElement: () => parentRef?.current,
estimateSize: useCallback(() => 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
});
const isAtBottom = useMemo(()=> {
const isAtBottom = useMemo(() => {
if (parentRef.current && rowVirtualizer?.isScrolling !== undefined) {
const { scrollTop, scrollHeight, clientHeight } = parentRef.current;
const atBottom = scrollTop + clientHeight >= scrollHeight - 10; // Adjust threshold as needed
return atBottom
}
const atBottom = scrollTop + clientHeight >= scrollHeight - 10; // Adjust threshold as needed
return atBottom;
}
return false
}, [rowVirtualizer?.isScrolling])
return false;
}, [rowVirtualizer?.isScrolling]);
useEffect(() => {
if (!parentRef.current || rowVirtualizer?.isScrolling === undefined) return;
if(isAtBottom){
if (isAtBottom) {
if (scrollingIntervalRef.current) {
clearTimeout(scrollingIntervalRef.current);
}
setShowScrollDownButton(false);
return;
} else
if (rowVirtualizer?.isScrolling) {
} else if (rowVirtualizer?.isScrolling) {
if (scrollingIntervalRef.current) {
clearTimeout(scrollingIntervalRef.current);
}
@@ -108,7 +99,13 @@ export const ChatList = ({
setTimeout(() => {
const hasUnreadMessages = totalMessages.some(
(msg) => msg.unread && !msg?.chatReference && !msg?.isTemp && (!msg?.chatReference && msg?.timestamp > lastSeenUnreadMessageTimestamp.current || 0)
(msg) =>
msg.unread &&
!msg?.chatReference &&
!msg?.isTemp &&
((!msg?.chatReference &&
msg?.timestamp > lastSeenUnreadMessageTimestamp.current) ||
0)
);
if (parentRef.current) {
const { scrollTop, scrollHeight, clientHeight } = parentRef.current;
@@ -136,9 +133,9 @@ export const ChatList = ({
const index = initialMsgs ? initialMsgs.length - 1 : messages.length - 1;
if (rowVirtualizer) {
if (divideIndex) {
rowVirtualizer.scrollToIndex(divideIndex, { align: "start" });
rowVirtualizer.scrollToIndex(divideIndex, { align: 'start' });
} else {
rowVirtualizer.scrollToIndex(index, { align: "end" });
rowVirtualizer.scrollToIndex(index, { align: 'end' });
}
}
handleMessageSeen();
@@ -152,7 +149,7 @@ export const ChatList = ({
}))
);
setShowScrollButton(false);
lastSeenUnreadMessageTimestamp.current = Date.now()
lastSeenUnreadMessageTimestamp.current = Date.now();
}, []);
const sentNewMessageGroupFunc = useCallback(() => {
@@ -166,9 +163,9 @@ export const ChatList = ({
}, [messages]);
useEffect(() => {
subscribeToEvent("sent-new-message-group", sentNewMessageGroupFunc);
subscribeToEvent('sent-new-message-group', sentNewMessageGroupFunc);
return () => {
unsubscribeFromEvent("sent-new-message-group", sentNewMessageGroupFunc);
unsubscribeFromEvent('sent-new-message-group', sentNewMessageGroupFunc);
};
}, [sentNewMessageGroupFunc]);
@@ -181,21 +178,24 @@ export const ChatList = ({
const goToMessage = useCallback((idx) => {
rowVirtualizer.scrollToIndex(idx);
}, []);
const theme = useTheme();
return (
<Box
sx={{
display: "flex",
width: "100%",
height: "100%",
display: 'flex',
height: '100%',
width: '100%',
}}
>
<div
style={{
height: "100%",
position: "relative",
display: "flex",
flexDirection: "column",
width: "100%",
display: 'flex',
flexDirection: 'column',
height: '100%',
position: 'relative',
width: '100%',
}}
>
<div
@@ -203,27 +203,26 @@ export const ChatList = ({
className="List"
style={{
flexGrow: 1,
overflow: "auto",
position: "relative",
display: "flex",
height: "0px",
overflow: 'auto',
position: 'relative',
display: 'flex',
height: '0px',
}}
>
<div
style={{
height: rowVirtualizer.getTotalSize(),
width: "100%",
width: '100%',
}}
>
<div
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
position: 'absolute',
top: 0,
width: '100%',
}}
>
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
const index = virtualRow.index;
let message = messages[index] || null; // Safeguard against undefined
@@ -231,7 +230,7 @@ export const ChatList = ({
let reply = null;
let reactions = null;
let isUpdating = false;
try {
// Safeguard for message existence
if (message) {
@@ -239,16 +238,19 @@ export const ChatList = ({
replyIndex = messages.findIndex(
(msg) => msg?.signature === message?.repliedTo
);
if (message?.repliedTo && replyIndex !== -1) {
reply = { ...(messages[replyIndex] || {}) };
if (chatReferences?.[reply?.signature]?.edit) {
reply.decryptedData = chatReferences[reply?.signature]?.edit;
reply.text = chatReferences[reply?.signature]?.edit?.message;
reply.editTimestamp = chatReferences[reply?.signature]?.edit?.timestamp
reply.decryptedData =
chatReferences[reply?.signature]?.edit;
reply.text =
chatReferences[reply?.signature]?.edit?.message;
reply.editTimestamp =
chatReferences[reply?.signature]?.edit?.timestamp;
}
}
// GroupDirectId logic
if (message?.message && message?.groupDirectId) {
replyIndex = messages.findIndex(
@@ -264,24 +266,34 @@ export const ChatList = ({
status: message?.status,
};
}
// Check for reactions and edits
if (chatReferences?.[message.signature]) {
reactions = chatReferences[message.signature]?.reactions || null;
if (chatReferences[message.signature]?.edit?.message && message?.text) {
message.text = chatReferences[message.signature]?.edit?.message;
message.isEdit = true
message.editTimestamp = chatReferences[message.signature]?.edit?.timestamp
reactions =
chatReferences[message.signature]?.reactions || null;
if (
chatReferences[message.signature]?.edit?.message &&
message?.text
) {
message.text =
chatReferences[message.signature]?.edit?.message;
message.isEdit = true;
message.editTimestamp =
chatReferences[message.signature]?.edit?.timestamp;
}
if (chatReferences[message.signature]?.edit?.messageText && message?.messageText) {
message.messageText = chatReferences[message.signature]?.edit?.messageText;
message.isEdit = true
message.editTimestamp = chatReferences[message.signature]?.edit?.timestamp
if (
chatReferences[message.signature]?.edit?.messageText &&
message?.messageText
) {
message.messageText =
chatReferences[message.signature]?.edit?.messageText;
message.isEdit = true;
message.editTimestamp =
chatReferences[message.signature]?.edit?.timestamp;
}
}
// Check if message is updating
if (
tempChatReferences?.some(
@@ -292,34 +304,37 @@ export const ChatList = ({
}
}
} catch (error) {
console.error("Error processing message:", error, { index, message });
console.error('Error processing message:', error, {
index,
message,
});
// Gracefully handle the error by providing fallback data
message = null;
reply = null;
reactions = null;
}
// Render fallback if message is null
// Render fallback if message is null
if (!message) {
return (
<div
key={virtualRow.index}
style={{
position: "absolute",
top: 0,
left: "50%",
transform: `translateY(${virtualRow.start}px) translateX(-50%)`,
width: "100%",
padding: "10px 0",
display: "flex",
alignItems: "center",
flexDirection: "column",
gap: "5px",
}}
>
<Typography>Error loading message.</Typography>
</div>
);
}
return (
<div
key={virtualRow.index}
style={{
position: 'absolute',
top: 0,
left: '50%',
transform: `translateY(${virtualRow.start}px) translateX(-50%)`,
width: '100%',
padding: '10px 0',
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
gap: '5px',
}}
>
<Typography>Error loading message.</Typography>
</div>
);
}
return (
<div
@@ -327,49 +342,47 @@ export const ChatList = ({
ref={rowVirtualizer.measureElement} //measure dynamic row height
key={message.signature}
style={{
position: "absolute",
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
gap: '5px',
left: '50%', // Move to the center horizontally
overscrollBehavior: 'none',
padding: '10px 0',
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",
alignItems: "center",
overscrollBehavior: "none",
flexDirection: "column",
gap: "5px",
width: '100%', // Control width (90% of the parent)
}}
>
<ErrorBoundary
<ErrorBoundary
fallback={
<Typography>
Error loading content: Invalid Data
</Typography>
}
>
<MessageItem
isLast={index === messages.length - 1}
lastSignature={lastSignature}
message={message}
onSeen={handleMessageSeen}
isTemp={!!message?.isTemp}
myAddress={myAddress}
onReply={onReply}
onEdit={onEdit}
reply={reply}
replyIndex={replyIndex}
scrollToItem={goToMessage}
handleReaction={handleReaction}
reactions={reactions}
isUpdating={isUpdating}
isPrivate={isPrivate}
/>
</ErrorBoundary>
<MessageItem
isLast={index === messages.length - 1}
lastSignature={lastSignature}
message={message}
onSeen={handleMessageSeen}
isTemp={!!message?.isTemp}
myAddress={myAddress}
onReply={onReply}
onEdit={onEdit}
reply={reply}
replyIndex={replyIndex}
scrollToItem={goToMessage}
handleReaction={handleReaction}
reactions={reactions}
isUpdating={isUpdating}
isPrivate={isPrivate}
/>
</ErrorBoundary>
</div>
);
})}
</div>
</div>
</div>
@@ -377,17 +390,17 @@ export const ChatList = ({
<button
onClick={() => scrollToBottom()}
style={{
backgroundColor: 'var(--unread)',
border: 'none',
borderRadius: '20px',
bottom: 20,
position: "absolute",
color: theme.palette.text.primary,
cursor: 'pointer',
outline: 'none',
padding: '10px 20px',
position: 'absolute',
right: 20,
backgroundColor: "var(--unread)",
color: "black",
padding: "10px 20px",
borderRadius: "20px",
cursor: "pointer",
zIndex: 10,
border: "none",
outline: "none",
}}
>
Scroll to Unread Messages
@@ -397,18 +410,18 @@ export const ChatList = ({
<button
onClick={() => scrollToBottom()}
style={{
backgroundColor: theme.palette.background.default,
border: 'none',
borderRadius: '20px',
bottom: 20,
position: "absolute",
color: theme.palette.text.primary,
cursor: 'pointer',
fontSize: '16px',
outline: 'none',
padding: '10px 20px',
position: 'absolute',
right: 20,
backgroundColor: "var(--Mail-Background)",
color: "white",
padding: "10px 20px",
borderRadius: "20px",
cursor: "pointer",
zIndex: 10,
border: "none",
outline: "none",
fontSize: "16px",
}}
>
Scroll to bottom
@@ -417,7 +430,7 @@ export const ChatList = ({
</div>
{enableMentions && (hasSecretKey || isPrivate === false) && (
<ChatOptions
openQManager={openQManager}
openQManager={openQManager}
messages={messages}
goToMessage={goToMessage}
members={members}