added edit messages

This commit is contained in:
PhilReact 2024-11-25 02:51:55 +02:00
parent 8cb88a6380
commit 25fe950438
6 changed files with 415 additions and 142 deletions

View File

@ -53,6 +53,7 @@ export const MessageQueueProvider = ({ children }) => {
// Function to process the message queue
const processQueue = useCallback((newMessages = [], groupDirectId) => {
processingPromiseRef.current = processingPromiseRef.current
.then(() => processQueueInternal(newMessages, groupDirectId))
.catch((err) => console.error('Error in processQueue:', err));
@ -61,33 +62,7 @@ export const MessageQueueProvider = ({ children }) => {
// Internal function to handle queue processing
const processQueueInternal = async (newMessages, groupDirectId) => {
// Remove any messages from the queue that match the specialId from newMessages
if (newMessages.length > 0) {
messageQueueRef.current = messageQueueRef.current.filter((msg) => {
return !newMessages.some(newMsg => newMsg?.specialId === msg?.specialId);
});
// Remove corresponding entries in queueChats for the provided groupDirectId
setQueueChats((prev) => {
const updatedChats = { ...prev };
if (updatedChats[groupDirectId]) {
updatedChats[groupDirectId] = updatedChats[groupDirectId].filter((chat) => {
return !newMessages.some(newMsg => newMsg?.specialId === chat?.message?.specialId);
});
// Remove messages with status 'failed-permanent'
updatedChats[groupDirectId] = updatedChats[groupDirectId].filter((chat) => {
return chat?.status !== 'failed-permanent';
});
// If no more chats for this group, delete the groupDirectId entry
if (updatedChats[groupDirectId].length === 0) {
delete updatedChats[groupDirectId];
}
}
return updatedChats;
});
}
// If the queue is empty, no need to process
if (messageQueueRef.current.length === 0) return;
@ -112,11 +87,11 @@ export const MessageQueueProvider = ({ children }) => {
try {
// Execute the function stored in the messageQueueRef
await currentMessage.func();
// Remove the message from the queue after successful sending
messageQueueRef.current.shift();
// Remove the message from queueChats
setQueueChats((prev) => {
const updatedChats = { ...prev };
@ -167,7 +142,33 @@ export const MessageQueueProvider = ({ children }) => {
// Method to process with new messages and groupDirectId
const processWithNewMessages = (newMessages, groupDirectId) => {
processQueue(newMessages, groupDirectId);
if (newMessages.length > 0) {
messageQueueRef.current = messageQueueRef.current.filter((msg) => {
return !newMessages.some(newMsg => newMsg?.specialId === msg?.specialId);
});
// Remove corresponding entries in queueChats for the provided groupDirectId
setQueueChats((prev) => {
const updatedChats = { ...prev };
if (updatedChats[groupDirectId]) {
updatedChats[groupDirectId] = updatedChats[groupDirectId].filter((chat) => {
return !newMessages.some(newMsg => newMsg?.specialId === chat?.message?.specialId);
});
// Remove messages with status 'failed-permanent'
updatedChats[groupDirectId] = updatedChats[groupDirectId].filter((chat) => {
return chat?.status !== 'failed-permanent';
});
// If no more chats for this group, delete the groupDirectId entry
if (updatedChats[groupDirectId].length === 0) {
delete updatedChats[groupDirectId];
}
}
return updatedChats;
});
}
};
return (

View File

@ -0,0 +1,36 @@
import React, { ReactNode } from 'react'
interface ErrorBoundaryProps {
children: ReactNode
fallback: ReactNode
}
interface ErrorBoundaryState {
hasError: boolean
}
class ErrorBoundary extends React.Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
state: ErrorBoundaryState = {
hasError: false
}
static getDerivedStateFromError(_: Error): ErrorBoundaryState {
return { hasError: true }
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
// You can log the error and errorInfo here, for example, to an error reporting service.
console.error('Error caught in ErrorBoundary:', error, errorInfo)
}
render(): React.ReactNode {
if (this.state.hasError) return this.props.fallback
return this.props.children
}
}
export default ErrorBoundary

View File

@ -28,6 +28,7 @@ const uid = new ShortUniqueId({ length: 5 });
export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDirect, setNewChat, getTimestampEnterChat, myName, balance, close, setMobileViewModeKeepOpen}) => {
const { queueChats, addToQueue, processWithNewMessages} = useMessageQueue();
const [isFocusedParent, setIsFocusedParent] = useState(false);
const [onEditMessage, setOnEditMessage] = useState(null)
const [messages, setMessages] = useState([])
const [isSending, setIsSending] = useState(false)
@ -38,6 +39,8 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi
const [infoSnack, setInfoSnack] = React.useState(null);
const [publicKeyOfRecipient, setPublicKeyOfRecipient] = React.useState("")
const hasInitializedWebsocket = useRef(false)
const [chatReferences, setChatReferences] = useState({})
const editorRef = useRef(null);
const socketRef = useRef(null);
const timeoutIdRef = useRef(null);
@ -66,10 +69,19 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi
const tempMessages = useMemo(()=> {
if(!selectedDirect?.address) return []
if(queueChats[selectedDirect?.address]){
return queueChats[selectedDirect?.address]
return queueChats[selectedDirect?.address]?.filter((item)=> !item?.chatReference)
}
return []
}, [selectedDirect?.address, queueChats])
const tempChatReferences = useMemo(()=> {
if(!selectedDirect?.address) return []
if(queueChats[selectedDirect?.address]){
return queueChats[selectedDirect?.address]?.filter((item)=> !!item?.chatReference)
}
return []
}, [selectedDirect?.address, queueChats])
useEffect(()=> {
if(selectedDirect?.address){
publicKeyOfRecipientRef.current = selectedDirect?.address
@ -112,22 +124,54 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi
res(response);
if (isInitiated) {
const formatted = response.map((item) => ({
const formatted = response.filter((rawItem) => !rawItem?.chatReference).map((item) => ({
...item,
id: item.signature,
text: item.message,
unread: item?.sender === myAddress ? false : true,
}));
setMessages((prev) => [...prev, ...formatted]);
setChatReferences((prev) => {
const organizedChatReferences = { ...prev };
response.filter((rawItem) => !!rawItem?.chatReference && rawItem?.type === 'edit').forEach((item) => {
try {
organizedChatReferences[item.chatReference] = {
...(organizedChatReferences[item.chatReference] || {}),
edit: item
};
} catch(error){
}
})
return organizedChatReferences
})
} else {
const formatted = response.map((item) => ({
hasInitialized.current = true;
const formatted = response.filter((rawItem) => !rawItem?.chatReference)
.map((item) => ({
...item,
id: item.signature,
text: item.message,
unread: false,
}));
setMessages(formatted);
hasInitialized.current = true;
setChatReferences((prev) => {
const organizedChatReferences = { ...prev };
response.filter((rawItem) => !!rawItem?.chatReference && rawItem?.type === 'edit').forEach((item) => {
try {
organizedChatReferences[item.chatReference] = {
...(organizedChatReferences[item.chatReference] || {}),
edit: item
};
} catch(error){
}
})
return organizedChatReferences
})
}
return;
}
@ -333,7 +377,7 @@ useEffect(() => {
const sendMessage = async ()=> {
try {
if(+balance < 4) throw new Error('You need at least 4 QORT to send a message')
if(isSending) return
@ -355,12 +399,16 @@ useEffect(() => {
if (replyMessage?.chatReference) {
repliedTo = replyMessage?.chatReference
}
let chatReference = onEditMessage?.signature
const otherData = {
...(onEditMessage?.decryptedData || {}),
specialId: uid.rnd(),
repliedTo
repliedTo: onEditMessage ? onEditMessage?.repliedTo : repliedTo,
type: chatReference ? 'edit' : ''
}
const sendMessageFunc = async () => {
return await sendChatDirect({ chatReference: undefined, messageText: htmlContent, otherData}, selectedDirect?.address, publicKeyOfRecipient, false)
return await sendChatDirect({ chatReference, messageText: htmlContent, otherData}, selectedDirect?.address, publicKeyOfRecipient, false)
};
@ -368,13 +416,13 @@ useEffect(() => {
// Add the function to the queue
const messageObj = {
message: {
text: htmlContent,
timestamp: Date.now(),
senderName: myName,
sender: myAddress,
...(otherData || {})
...(otherData || {}),
text: htmlContent,
},
chatReference
}
addToQueue(sendMessageFunc, messageObj, 'chat-direct',
selectedDirect?.address );
@ -383,6 +431,8 @@ useEffect(() => {
}, 150);
clearEditorContent()
setReplyMessage(null)
setOnEditMessage(null)
}
// send chat message
} catch (error) {
@ -399,12 +449,22 @@ useEffect(() => {
}
}
const onReply = useCallback((message)=> {
setReplyMessage(message)
editorRef?.current?.chain().focus()
}, [])
const onReply = useCallback((message)=> {
if(onEditMessage){
editorRef.current.chain().focus().clearContent().run()
}
setReplyMessage(message)
setOnEditMessage(null)
editorRef?.current?.chain().focus()
}, [onEditMessage])
const onEdit = useCallback((message)=> {
setOnEditMessage(message)
setReplyMessage(null)
editorRef.current.chain().focus().setContent(message?.text).run();
}, [])
return (
<div style={{
@ -511,7 +571,7 @@ useEffect(() => {
</>
)}
<ChatList onReply={onReply} chatId={selectedDirect?.address} initialMessages={messages} myAddress={myAddress} tempMessages={tempMessages}/>
<ChatList chatReferences={chatReferences} onEdit={onEdit} onReply={onReply} chatId={selectedDirect?.address} initialMessages={messages} myAddress={myAddress} tempMessages={tempMessages} tempChatReferences={tempChatReferences}/>
<div style={{
@ -551,6 +611,30 @@ useEffect(() => {
<ButtonBase
onClick={() => {
setReplyMessage(null)
setOnEditMessage(null)
}}
>
<ExitIcon />
</ButtonBase>
</Box>
)}
{onEditMessage && (
<Box sx={{
display: 'flex',
gap: '5px',
alignItems: 'flex-start',
width: '100%'
}}>
<ReplyPreview isEdit message={onEditMessage} />
<ButtonBase
onClick={() => {
setReplyMessage(null)
setOnEditMessage(null)
editorRef.current.chain().focus().clearContent().run()
}}
>
<ExitIcon />

View File

@ -35,6 +35,9 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey,
const hasInitialized = useRef(false)
const [isFocusedParent, setIsFocusedParent] = useState(false);
const [replyMessage, setReplyMessage] = useState(null)
const [onEditMessage, setOnEditMessage] = useState(null)
const [messageSize, setMessageSize] = useState(0)
const hasInitializedWebsocket = useRef(false)
const socketRef = useRef(null); // WebSocket reference
@ -218,52 +221,59 @@ const [messageSize, setMessageSize] = useState(0)
setChatReferences((prev) => {
const organizedChatReferences = { ...prev };
combineUIAndExtensionMsgs
.filter((rawItem) => rawItem && rawItem.chatReference && rawItem.decryptedData?.type === "reaction")
.filter((rawItem) => rawItem && rawItem.chatReference && (rawItem.decryptedData?.type === "reaction" || rawItem.decryptedData?.type === "edit"))
.forEach((item) => {
try {
const content = item.decryptedData?.content;
const sender = item.sender;
const newTimestamp = item.timestamp;
const contentState = item.decryptedData?.contentState;
if (!content || typeof content !== "string" || !sender || typeof sender !== "string" || !newTimestamp) {
console.warn("Invalid content, sender, or timestamp in reaction data", item);
return;
}
organizedChatReferences[item.chatReference] = {
...(organizedChatReferences[item.chatReference] || {}),
reactions: organizedChatReferences[item.chatReference]?.reactions || {},
};
organizedChatReferences[item.chatReference].reactions[content] =
organizedChatReferences[item.chatReference].reactions[content] || [];
let latestTimestampForSender = null;
organizedChatReferences[item.chatReference].reactions[content] =
organizedChatReferences[item.chatReference].reactions[content].filter((reaction) => {
if (reaction.sender === sender) {
latestTimestampForSender = Math.max(latestTimestampForSender || 0, reaction.timestamp);
}
return reaction.sender !== sender;
});
if (latestTimestampForSender && newTimestamp < latestTimestampForSender) {
return;
}
if (contentState !== false) {
organizedChatReferences[item.chatReference].reactions[content].push(item);
}
if (organizedChatReferences[item.chatReference].reactions[content].length === 0) {
delete organizedChatReferences[item.chatReference].reactions[content];
if(item.decryptedData?.type === "edit"){
organizedChatReferences[item.chatReference] = {
...(organizedChatReferences[item.chatReference] || {}),
edit: item.decryptedData,
};
} else {
const content = item.decryptedData?.content;
const sender = item.sender;
const newTimestamp = item.timestamp;
const contentState = item.decryptedData?.contentState;
if (!content || typeof content !== "string" || !sender || typeof sender !== "string" || !newTimestamp) {
console.warn("Invalid content, sender, or timestamp in reaction data", item);
return;
}
organizedChatReferences[item.chatReference] = {
...(organizedChatReferences[item.chatReference] || {}),
reactions: organizedChatReferences[item.chatReference]?.reactions || {},
};
organizedChatReferences[item.chatReference].reactions[content] =
organizedChatReferences[item.chatReference].reactions[content] || [];
let latestTimestampForSender = null;
organizedChatReferences[item.chatReference].reactions[content] =
organizedChatReferences[item.chatReference].reactions[content].filter((reaction) => {
if (reaction.sender === sender) {
latestTimestampForSender = Math.max(latestTimestampForSender || 0, reaction.timestamp);
}
return reaction.sender !== sender;
});
if (latestTimestampForSender && newTimestamp < latestTimestampForSender) {
return;
}
if (contentState !== false) {
organizedChatReferences[item.chatReference].reactions[content].push(item);
}
if (organizedChatReferences[item.chatReference].reactions[content].length === 0) {
delete organizedChatReferences[item.chatReference].reactions[content];
}
}
} catch (error) {
console.error("Error processing reaction item:", error, item);
console.error("Error processing reaction/edit item:", error, item);
}
});
@ -295,9 +305,15 @@ const [messageSize, setMessageSize] = useState(0)
const organizedChatReferences = { ...prev };
combineUIAndExtensionMsgs
.filter((rawItem) => rawItem && rawItem.chatReference && rawItem.decryptedData?.type === "reaction")
.filter((rawItem) => rawItem && rawItem.chatReference && (rawItem.decryptedData?.type === "reaction" || rawItem.decryptedData?.type === "edit"))
.forEach((item) => {
try {
if(item.decryptedData?.type === "edit"){
organizedChatReferences[item.chatReference] = {
...(organizedChatReferences[item.chatReference] || {}),
edit: item.decryptedData,
};
} else {
const content = item.decryptedData?.content;
const sender = item.sender;
const newTimestamp = item.timestamp;
@ -337,6 +353,7 @@ const [messageSize, setMessageSize] = useState(0)
if (organizedChatReferences[item.chatReference].reactions[content].length === 0) {
delete organizedChatReferences[item.chatReference].reactions[content];
}
}
} catch (error) {
console.error("Error processing reaction item:", error, item);
}
@ -549,13 +566,17 @@ const clearEditorContent = () => {
if (replyMessage?.chatReference) {
repliedTo = replyMessage?.chatReference
}
let chatReference = onEditMessage?.signature
const otherData = {
repliedTo,
...(onEditMessage?.decryptedData || {}),
type: chatReference ? 'edit' : '',
specialId: uid.rnd(),
repliedTo
}
const objectMessage = {
message,
...(otherData || {})
...(otherData || {}),
message
}
const message64: any = await objectToBase64(objectMessage)
@ -563,7 +584,7 @@ const clearEditorContent = () => {
// const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle})
const sendMessageFunc = async () => {
return await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle})
return await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle, chatReference})
};
// Add the function to the queue
@ -575,7 +596,7 @@ const clearEditorContent = () => {
sender: myAddress,
...(otherData || {})
},
chatReference
}
addToQueue(sendMessageFunc, messageObj, 'chat',
selectedGroup );
@ -584,6 +605,7 @@ const clearEditorContent = () => {
}, 150);
clearEditorContent()
setReplyMessage(null)
setOnEditMessage(null)
}
// send chat message
} catch (error) {
@ -627,10 +649,21 @@ const clearEditorContent = () => {
}, [hide]);
const onReply = useCallback((message)=> {
if(onEditMessage){
editorRef.current.chain().focus().clearContent().run()
}
setReplyMessage(message)
setOnEditMessage(null)
editorRef?.current?.chain().focus()
}, [])
}, [onEditMessage])
const onEdit = useCallback((message)=> {
setOnEditMessage(message)
setReplyMessage(null)
editorRef.current.chain().focus().setContent(message?.text).run();
}, [])
const handleReaction = useCallback(async (reaction, chatMessage, reactionState = true)=> {
try {
@ -708,7 +741,7 @@ const clearEditorContent = () => {
left: hide && '-100000px',
}}>
<ChatList enableMentions onReply={onReply} chatId={selectedGroup} initialMessages={messages} myAddress={myAddress} tempMessages={tempMessages} handleReaction={handleReaction} chatReferences={chatReferences} tempChatReferences={tempChatReferences} members={members} myName={myName} selectedGroup={selectedGroup} />
<ChatList enableMentions onReply={onReply} onEdit={onEdit} chatId={selectedGroup} initialMessages={messages} myAddress={myAddress} tempMessages={tempMessages} handleReaction={handleReaction} chatReferences={chatReferences} tempChatReferences={tempChatReferences} members={members} myName={myName} selectedGroup={selectedGroup} />
<div style={{
@ -748,6 +781,31 @@ const clearEditorContent = () => {
<ButtonBase
onClick={() => {
setReplyMessage(null)
setOnEditMessage(null)
}}
>
<ExitIcon />
</ButtonBase>
</Box>
)}
{onEditMessage && (
<Box sx={{
display: 'flex',
gap: '5px',
alignItems: 'flex-start',
width: '100%'
}}>
<ReplyPreview isEdit message={onEditMessage} />
<ButtonBase
onClick={() => {
setReplyMessage(null)
setOnEditMessage(null)
editorRef.current.chain().focus().clearContent().run()
}}
>
<ExitIcon />

View File

@ -9,8 +9,9 @@ import { useVirtualizer } from "@tanstack/react-virtual";
import { MessageItem } from "./MessageItem";
import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
import { useInView } from "react-intersection-observer";
import { Box } from "@mui/material";
import { Box, Typography } from "@mui/material";
import { ChatOptions } from "./ChatOptions";
import ErrorBoundary from "../../common/ErrorBoundary";
export const ChatList = ({
initialMessages,
@ -18,6 +19,7 @@ export const ChatList = ({
tempMessages,
chatId,
onReply,
onEdit,
handleReaction,
chatReferences,
tempChatReferences,
@ -155,7 +157,6 @@ export const ChatList = ({
// Check if the user is within 200px from the bottom
const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
console.log("distanceFromBottom", distanceFromBottom);
if (distanceFromBottom <= 700) {
scrollToBottom();
}
@ -177,7 +178,6 @@ export const ChatList = ({
const goToMessage = useCallback((idx) => {
rowVirtualizer.scrollToIndex(idx);
}, []);
return (
<Box
sx={{
@ -220,49 +220,98 @@ export const ChatList = ({
width: "100%",
}}
>
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
const index = virtualRow.index;
let message = messages[index];
let replyIndex = messages.findIndex(
(msg) => msg?.signature === message?.repliedTo
);
let reply;
let message = messages[index] || null; // Safeguard against undefined
let replyIndex = -1;
let reply = null;
let reactions = null;
if (message?.repliedTo && replyIndex !== -1) {
reply = messages[replyIndex];
}
if (message?.message && message?.groupDirectId) {
replyIndex = messages.findIndex(
(msg) => msg?.signature === message?.message?.repliedTo
);
if (message?.message?.repliedTo && replyIndex !== -1) {
reply = messages[replyIndex];
}
message = {
...(message?.message || {}),
isTemp: true,
unread: false,
status: message?.status,
};
}
if (chatReferences && chatReferences[message?.signature]) {
if (chatReferences[message.signature]?.reactions) {
reactions = chatReferences[message.signature]?.reactions;
}
}
let isUpdating = false;
if (
tempChatReferences &&
tempChatReferences?.find(
(item) => item?.chatReference === message?.signature
)
) {
isUpdating = true;
try {
// Safeguard for message existence
if (message) {
// Check for repliedTo logic
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;
}
}
// GroupDirectId logic
if (message?.message && message?.groupDirectId) {
replyIndex = messages.findIndex(
(msg) => msg?.signature === message?.message?.repliedTo
);
if (message?.message?.repliedTo && replyIndex !== -1) {
reply = messages[replyIndex] || null;
}
message = {
...(message?.message || {}),
isTemp: true,
unread: false,
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
}
}
// Check if message is updating
if (
tempChatReferences?.some(
(item) => item?.chatReference === message?.signature
)
) {
isUpdating = true;
}
}
} catch (error) {
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
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
@ -283,6 +332,13 @@ export const ChatList = ({
gap: "5px",
}}
>
<ErrorBoundary
fallback={
<Typography>
Error loading content: Invalid Data
</Typography>
}
>
<MessageItem
isLast={index === messages.length - 1}
lastSignature={lastSignature}
@ -291,6 +347,7 @@ export const ChatList = ({
isTemp={!!message?.isTemp}
myAddress={myAddress}
onReply={onReply}
onEdit={onEdit}
reply={reply}
replyIndex={replyIndex}
scrollToItem={goToMessage}
@ -298,9 +355,11 @@ export const ChatList = ({
reactions={reactions}
isUpdating={isUpdating}
/>
</ErrorBoundary>
</div>
);
})}
</div>
</div>
</div>

View File

@ -16,6 +16,8 @@ import ReplyIcon from "@mui/icons-material/Reply";
import { Spacer } from "../../common/Spacer";
import { ReactionPicker } from "../ReactionPicker";
import KeyOffIcon from '@mui/icons-material/KeyOff';
import EditIcon from '@mui/icons-material/Edit';
export const MessageItem = ({
message,
onSeen,
@ -30,7 +32,8 @@ export const MessageItem = ({
handleReaction,
reactions,
isUpdating,
lastSignature
lastSignature,
onEdit
}) => {
const { ref, inView } = useInView({
threshold: 0.7, // Fully visible
@ -46,6 +49,7 @@ export const MessageItem = ({
}, [inView, message.id, isLast]);
return (
<>
{message?.divide && (
@ -130,6 +134,15 @@ export const MessageItem = ({
gap: '10px',
alignItems: 'center'
}}>
{message?.sender === myAddress && !message?.isNotEncrypted && (
<ButtonBase
onClick={() => {
onEdit(message);
}}
>
<EditIcon />
</ButtonBase>
)}
{!isShowingAsReply && (
<ButtonBase
onClick={() => {
@ -289,6 +302,19 @@ export const MessageItem = ({
{message?.status === 'failed-permanent' ? 'Failed to send' : 'Sending...'}
</Typography>
) : (
<>
{message?.isEdit && (
<Typography
sx={{
fontSize: "14px",
color: "gray",
fontFamily: "Inter",
fontStyle: 'italic'
}}
>
Edited
</Typography>
)}
<Typography
sx={{
fontSize: "14px",
@ -298,6 +324,7 @@ export const MessageItem = ({
>
{formatTimestamp(message.timestamp)}
</Typography>
</>
)}
</Box>
</Box>
@ -320,7 +347,7 @@ export const MessageItem = ({
};
export const ReplyPreview = ({message})=> {
export const ReplyPreview = ({message, isEdit})=> {
return (
<Box
@ -344,10 +371,18 @@ export const ReplyPreview = ({message})=> {
<Box sx={{
padding: '5px'
}}>
<Typography sx={{
fontSize: '12px',
fontWeight: 600
}}>Replied to {message?.senderName || message?.senderAddress}</Typography>
{isEdit ? (
<Typography sx={{
fontSize: '12px',
fontWeight: 600
}}>Editing Message</Typography>
) : (
<Typography sx={{
fontSize: '12px',
fontWeight: 600
}}>Replied to {message?.senderName || message?.senderAddress}</Typography>
)}
{message?.messageText && (
<MessageDisplay
htmlContent={generateHTML(message?.messageText, [