fix get latest secretkey, multi-send group chat

This commit is contained in:
2024-09-11 15:42:01 +03:00
parent 09e690e43f
commit 82503efa8c
14 changed files with 558 additions and 232 deletions

View File

@@ -10,7 +10,7 @@ import { decryptPublishes, getTempPublish, saveTempPublish } from "./GroupAnnoun
import { AnnouncementList } from "./AnnouncementList";
import { Spacer } from "../../common/Spacer";
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import { getBaseApiReact } from "../../App";
import { getBaseApiReact, pauseAllQueues, resumeAllQueues } from "../../App";
const tempKey = 'accouncement-comment'
@@ -107,6 +107,7 @@ export const AnnouncementDiscussion = ({
const publishComment = async () => {
try {
pauseAllQueues()
const fee = await getFee('ARBITRARY')
await show({
message: "Would you like to perform a ARBITRARY transaction?" ,
@@ -123,7 +124,7 @@ export const AnnouncementDiscussion = ({
extra: {},
message: htmlContent,
};
const secretKeyObject = await getSecretKey();
const secretKeyObject = await getSecretKey(false, true);
const message64: any = await objectToBase64(message);
const encryptSingle = await encryptChatMessage(
@@ -154,6 +155,7 @@ export const AnnouncementDiscussion = ({
} catch (error) {
console.error(error);
} finally {
resumeAllQueues()
setIsSending(false);
}
};
@@ -161,6 +163,7 @@ export const AnnouncementDiscussion = ({
const getComments = React.useCallback(
async (selectedAnnouncement) => {
try {
setIsLoading(true);
const offset = 0;

View File

@@ -241,7 +241,7 @@ const clearEditorContent = () => {
</>
)}
<ChatList initialMessages={messages} myAddress={myAddress}/>
<ChatList initialMessages={messages} myAddress={myAddress} tempMessages={[]}/>
<div style={{

View File

@@ -10,15 +10,16 @@ import Tiptap from './TipTap'
import { CustomButton } from '../../App-styles'
import CircularProgress from '@mui/material/CircularProgress';
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'
import { getBaseApiReactSocket } from '../../App'
import { getBaseApiReactSocket, pauseAllQueues, resumeAllQueues } from '../../App'
import { CustomizedSnackbars } from '../Snackbar/Snackbar'
import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../../constants/codes'
import { useMessageQueue } from '../../MessageQueueContext'
export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, myAddress, handleNewEncryptionNotification, hide, handleSecretKeyCreationInProgress, triedToFetchSecretKey}) => {
export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, myAddress, handleNewEncryptionNotification, hide, handleSecretKeyCreationInProgress, triedToFetchSecretKey, myName}) => {
const [messages, setMessages] = useState([])
const [isSending, setIsSending] = useState(false)
const [isLoading, setIsLoading] = useState(false)
@@ -31,11 +32,20 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey,
const timeoutIdRef = useRef(null); // Timeout ID reference
const groupSocketTimeoutRef = useRef(null); // Group Socket Timeout reference
const editorRef = useRef(null);
const { queueChats, addToQueue, } = useMessageQueue();
const setEditorRef = (editorInstance) => {
editorRef.current = editorInstance;
};
const tempMessages = useMemo(()=> {
if(!selectedGroup) return []
if(queueChats[selectedGroup]){
return queueChats[selectedGroup]
}
return []
}, [selectedGroup, queueChats])
const secretKeyRef = useRef(null)
useEffect(()=> {
@@ -88,7 +98,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey,
...item,
id: item.signature,
text: item.text,
unread: true
unread: item?.sender === myAddress ? false : true
}
} )
setMessages((prev)=> [...prev, ...formatted])
@@ -98,7 +108,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey,
...item,
id: item.signature,
text: item.text,
unread: false
unread: false
}
} )
setMessages(formatted)
@@ -199,6 +209,10 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey,
forceCloseWebSocket()
setMessages([])
setIsLoading(true)
pauseAllQueues()
setTimeout(() => {
resumeAllQueues()
}, 6000);
initWebsocketMessageGroup()
hasInitializedWebsocket.current = true
}, [secretKey])
@@ -262,18 +276,35 @@ const clearEditorContent = () => {
const sendMessage = async ()=> {
try {
if(isSending) return
pauseAllQueues()
if (editorRef.current) {
const htmlContent = editorRef.current.getHTML();
if(!htmlContent?.trim() || htmlContent?.trim() === '<p></p>') return
setIsSending(true)
const message = htmlContent
const secretKeyObject = await getSecretKey()
const secretKeyObject = await getSecretKey(false, true)
const message64: any = await objectToBase64(message)
const encryptSingle = await encryptChatMessage(message64, secretKeyObject)
const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle})
// const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle})
const sendMessageFunc = async () => {
await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle})
};
// Add the function to the queue
const messageObj = {
message: {
text: message,
timestamp: Date.now(),
senderName: myName,
sender: myAddress
},
}
addToQueue(sendMessageFunc, messageObj, 'chat',
selectedGroup );
clearEditorContent()
}
// send chat message
@@ -286,6 +317,7 @@ const clearEditorContent = () => {
console.error(error)
} finally {
setIsSending(false)
resumeAllQueues()
}
}
@@ -305,12 +337,11 @@ const clearEditorContent = () => {
flexDirection: 'column',
width: '100%',
opacity: hide ? 0 : 1,
visibility: hide && 'hidden',
position: hide ? 'fixed' : 'relative',
left: hide && '-1000px',
position: hide ? 'absolute' : 'relative',
left: hide && '-100000px',
}}>
<ChatList initialMessages={messages} myAddress={myAddress}/>
<ChatList initialMessages={messages} myAddress={myAddress} tempMessages={tempMessages}/>
<div style={{

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useState, useEffect, useRef } from 'react';
import React, { useCallback, useState, useEffect, useRef, useMemo } from 'react';
import { List, AutoSizer, CellMeasurerCache, CellMeasurer } from 'react-virtualized';
import { MessageItem } from './MessageItem';
@@ -7,35 +7,34 @@ const cache = new CellMeasurerCache({
defaultHeight: 50,
});
export const ChatList = ({ initialMessages, myAddress }) => {
export const ChatList = ({ initialMessages, myAddress, tempMessages }) => {
const hasLoadedInitialRef = useRef(false);
const listRef = useRef();
const [messages, setMessages] = useState(initialMessages);
const [showScrollButton, setShowScrollButton] = useState(false);
useEffect(()=> {
useEffect(() => {
cache.clearAll();
}, [])
const handleMessageSeen = useCallback((messageId) => {
}, []);
const handleMessageSeen = useCallback(() => {
setMessages((prevMessages) =>
prevMessages.map((msg) => {
return { ...msg, unread: false }
}
)
prevMessages.map((msg) => ({
...msg,
unread: false,
}))
);
}, []);
const handleScroll = ({ scrollTop, scrollHeight, clientHeight }) => {
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 50;
const hasUnreadMessages = messages.some((msg) => msg.unread);
if (!isAtBottom && hasUnreadMessages) {
setShowScrollButton(true);
} else {
setShowScrollButton(false);
if(isAtBottom){
handleMessageSeen()
}
setShowScrollButton(!isAtBottom && hasUnreadMessages);
};
const debounce = (func, delay) => {
@@ -47,96 +46,113 @@ export const ChatList = ({ initialMessages, myAddress }) => {
}, delay);
};
};
const handleScrollDebounced = debounce(handleScroll, 100);
const scrollToBottom = () => {
const scrollToBottom = (initialmsgs) => {
if (listRef.current) {
const msgs = initialmsgs?.length ? initialmsgs : messages
listRef.current?.recomputeRowHeights();
listRef.current.scrollToRow(messages.length - 1);
listRef.current.scrollToRow(msgs.length - 1);
setTimeout(() => {
listRef.current?.recomputeRowHeights();
listRef.current.scrollToRow(messages.length - 1);
listRef.current.scrollToRow(msgs.length - 1);
}, 100);
setShowScrollButton(false);
}
};
const preserveScrollPosition = (callback) => {
const recomputeListHeights = () => {
if (listRef.current) {
const scrollContainer = listRef.current.Grid._scrollingContainer;
const currentScrollTop = scrollContainer.scrollTop; // Get current scroll position
callback(); // Perform the action that could change the layout (e.g., recompute row heights)
// Restore the scroll position after the layout change
setTimeout(() => {
scrollContainer.scrollTop = currentScrollTop;
}, 0);
listRef.current.recomputeRowHeights();
}
};
const rowRenderer = ({ index, key, parent, style }) => {
const message = messages[index];
let message = messages[index];
const isLargeMessage = message.text?.length > 200; // Adjust based on your message size threshold
if(message?.message && message?.groupDirectId){
message = {
...(message?.message || {}),
isTemp: true,
unread: false
}
}
return (
<CellMeasurer
key={key}
cache={cache}
parent={parent}
columnIndex={0}
rowIndex={index}
>
{({ measure }) => (
<div style={style}>
<div
onLoad={() => preserveScrollPosition(measure)} // Prevent jumps while measuring
style={{
marginBottom: '10px',
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
key={key}
cache={cache}
parent={parent}
columnIndex={0}
rowIndex={index}
>
<MessageItem isLast={index === messages.length - 1} message={message} onSeen={handleMessageSeen} />
</div>
</div>
)}
</CellMeasurer>
{({ measure }) => (
<div style={style}>
<div
onLoad={() => {
if (isLargeMessage) {
measure(); // Ensure large messages are properly measured
}
}}
style={{
marginBottom: '10px',
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<MessageItem
isLast={index === messages.length - 1}
message={message}
onSeen={handleMessageSeen}
isTemp={!!message?.isTemp}
/>
</div>
</div>
)}
</CellMeasurer>
);
};
useEffect(() => {
setMessages(initialMessages);
const totalMessages = [...initialMessages, ...(tempMessages || [])]
if(totalMessages.length === 0) return
setMessages(totalMessages);
// cache.clearAll(); // Clear cache so the list can properly re-render with new messages
setTimeout(() => {
if (listRef.current) {
// Accessing scrollTop, scrollHeight, clientHeight from List's methods
const scrollTop = listRef.current.Grid._scrollingContainer.scrollTop;
const scrollHeight = listRef.current.Grid._scrollingContainer.scrollHeight;
const clientHeight = listRef.current.Grid._scrollingContainer.clientHeight;
handleScroll({ scrollTop, scrollHeight, clientHeight });
const { scrollTop, scrollHeight, clientHeight } = listRef.current.Grid._scrollingContainer;
handleScroll({ scrollTop, scrollHeight, clientHeight });
recomputeListHeights(); // Ensure heights are recomputed on message load
setTimeout(() => {
if(!hasLoadedInitialRef.current){
scrollToBottom(totalMessages);
hasLoadedInitialRef.current = true
}
}, 100);
}
}, 100);
}, [initialMessages]);
}, 500);
}, [tempMessages, initialMessages]);
useEffect(() => {
// Scroll to the bottom on initial load or when messages change
if (listRef.current && messages.length > 0 && hasLoadedInitialRef.current === false) {
scrollToBottom();
hasLoadedInitialRef.current = true;
} else if (messages.length > 0 && messages[messages.length - 1].sender === myAddress) {
scrollToBottom();
}
}, [messages, myAddress]);
// useEffect(() => {
// // Scroll to the bottom on initial load or when messages change
// if (listRef.current && messages.length > 0 && !hasLoadedInitialRef.current) {
// scrollToBottom();
// hasLoadedInitialRef.current = true;
// } else if (messages.length > 0 && messages[messages.length - 1].sender === myAddress) {
// scrollToBottom();
// }
// }, [messages, myAddress]);
return (
<div style={{ position: 'relative', flexGrow: 1, width: '100%', display: 'flex', flexDirection: 'column', flexShrink: 1 }}>
<AutoSizer>
{({ height, width }) => (
<List
@@ -148,6 +164,8 @@ export const ChatList = ({ initialMessages, myAddress }) => {
rowRenderer={rowRenderer}
onScroll={handleScrollDebounced}
deferredMeasurementCache={cache}
onRowsRendered={recomputeListHeights} // Force recompute on render
overscanRowCount={10} // For performance: pre-render some rows
/>
)}
</AutoSizer>
@@ -168,7 +186,9 @@ export const ChatList = ({ initialMessages, myAddress }) => {
>
Scroll to Unread Messages
</button>
)}
</div>
);
};
};

View File

@@ -28,7 +28,7 @@ const uid = new ShortUniqueId({ length: 8 });
import CampaignIcon from '@mui/icons-material/Campaign';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import { AnnouncementDiscussion } from "./AnnouncementDiscussion";
import { MyContext, getBaseApiReact } from "../../App";
import { MyContext, getBaseApiReact, pauseAllQueues, resumeAllQueues } from "../../App";
import { RequestQueueWithPromise } from "../../utils/queue/queue";
import { CustomizedSnackbars } from "../Snackbar/Snackbar";
@@ -248,6 +248,8 @@ export const GroupAnnouncements = ({
const publishAnnouncement = async () => {
try {
pauseAllQueues()
const fee = await getFee('ARBITRARY')
await show({
message: "Would you like to perform a ARBITRARY transaction?" ,
@@ -263,7 +265,7 @@ export const GroupAnnouncements = ({
extra: {},
message: htmlContent
}
const secretKeyObject = await getSecretKey();
const secretKeyObject = await getSecretKey(false, true);
const message64: any = await objectToBase64(message);
const encryptSingle = await encryptChatMessage(
message64,
@@ -295,6 +297,7 @@ export const GroupAnnouncements = ({
});
setOpenSnack(true)
} finally {
resumeAllQueues()
setIsSending(false);
}
};

View File

@@ -7,7 +7,7 @@ import { formatTimestamp } from "../../utils/time";
import { getBaseApi } from "../../background";
import { getBaseApiReact } from "../../App";
export const MessageItem = ({ message, onSeen, isLast }) => {
export const MessageItem = ({ message, onSeen, isLast, isTemp }) => {
const { ref, inView } = useInView({
threshold: 0.7, // Fully visible
@@ -30,6 +30,7 @@ export const MessageItem = ({ message, onSeen, isLast }) => {
width: "95%",
display: "flex",
gap: '7px',
opacity: isTemp ? 0.5 : 1
}}
>
<Avatar
@@ -67,13 +68,23 @@ export const MessageItem = ({ message, onSeen, isLast }) => {
<Box sx={{
display: 'flex',
justifyContent: 'flex-end',
width: '100%'
width: '100%',
}}>
<Typography sx={{
fontSize: '14px',
color: 'gray',
fontFamily: 'Inter'
}}>{formatTimestamp(message.timestamp)}</Typography>
{isTemp ? (
<Typography sx={{
fontSize: '14px',
color: 'gray',
fontFamily: 'Inter'
}}>Sending...</Typography>
): (
<Typography sx={{
fontSize: '14px',
color: 'gray',
fontFamily: 'Inter'
}}>{formatTimestamp(message.timestamp)}</Typography>
) }
</Box>
</Box>

View File

@@ -269,7 +269,7 @@ const extensions = [
const content = ``;
export default ({ setEditorRef, onEnter, disableEnter, isChat }) => {
console.log('exte', extensions)
const extensionsFiltered = isChat ? extensions.filter((item)=> item?.name !== 'image') : extensions
return (
<EditorProvider