diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index a4f6e5a..e7df961 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -1,4 +1,4 @@ -import React, { +import { useCallback, useContext, useEffect, @@ -50,6 +50,8 @@ import CloseIcon from '@mui/icons-material/Close'; import { throttle } from 'lodash'; import ImageIcon from '@mui/icons-material/Image'; import { messageHasImage } from '../../utils/chat'; +import { useTranslation } from 'react-i18next'; + const uid = new ShortUniqueId({ length: 5 }); const uidImages = new ShortUniqueId({ length: 12 }); @@ -75,8 +77,8 @@ export const ChatGroup = ({ const [isSending, setIsSending] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isMoved, setIsMoved] = useState(false); - const [openSnack, setOpenSnack] = React.useState(false); - const [infoSnack, setInfoSnack] = React.useState(null); + const [openSnack, setOpenSnack] = useState(false); + const [infoSnack, setInfoSnack] = useState(null); const hasInitialized = useRef(false); const [isFocusedParent, setIsFocusedParent] = useState(false); const [replyMessage, setReplyMessage] = useState(null); @@ -93,8 +95,8 @@ export const ChatGroup = ({ const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue(); const [, forceUpdate] = useReducer((x) => x + 1, 0); const lastReadTimestamp = useRef(null); - const handleUpdateRef = useRef(null); + const { t } = useTranslation(['auth', 'core', 'group']); const getTimestampEnterChat = async (selectedGroup) => { try { @@ -129,7 +131,12 @@ export const ChatGroup = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); } catch (error) { @@ -154,9 +161,6 @@ export const ChatGroup = ({ return Array.from(uniqueMembers); }, [messages]); - const triggerRerender = () => { - forceUpdate(); // Trigger re-render by updating the state - }; const setEditorRef = (editorInstance) => { editorRef.current = editorInstance; }; @@ -168,6 +172,7 @@ export const ChatGroup = ({ } return []; }, [selectedGroup, queueChats]); + const tempChatReferences = useMemo(() => { if (!selectedGroup) return []; if (queueChats[selectedGroup]) { @@ -184,12 +189,6 @@ export const ChatGroup = ({ } }, [secretKey]); - // const getEncryptedSecretKey = useCallback(()=> { - // const response = getResource() - // const decryptResponse = decryptResource() - // return - // }, []) - const checkForFirstSecretKeyNotification = (messages) => { messages?.forEach((message) => { try { @@ -250,7 +249,6 @@ export const ChatGroup = ({ const dataRemovedBlock = responseData?.filter((item) => { return !isUserBlocked(item?.sender, item?.senderName); }); - decryptMessages(dataRemovedBlock, false); } catch (error) { console.error(error); @@ -273,6 +271,7 @@ export const ChatGroup = ({ const filterUIMessages = encryptedMessages.filter( (item) => !isExtMsg(item.data) ); + const decodedUIMessages = decodeBase64ForUIChatMessages(filterUIMessages); @@ -280,6 +279,7 @@ export const ChatGroup = ({ ...decodedUIMessages, ...response, ]; + const combineUIAndExtensionMsgs = processWithNewMessages( combineUIAndExtensionMsgsBefore.map((item) => ({ ...item, @@ -287,16 +287,24 @@ export const ChatGroup = ({ })), selectedGroup ); + res(combineUIAndExtensionMsgs); if (isInitiated) { const formatted = combineUIAndExtensionMsgs .filter((rawItem) => !rawItem?.chatReference) .map((item) => { + const message = ( +

+ {t('group:message.generic.group_key_created', { + postProcess: 'capitalizeFirst', + })} +

+ ); const additionalFields = - item?.data === 'NDAwMQ==' + item?.data === 'NDAwMQ==' // TODO put magic string somewhere in a file ? { - text: '

First group key created.

', + text: message, } : {}; return { @@ -362,7 +370,9 @@ export const ChatGroup = ({ !newTimestamp ) { console.warn( - 'Invalid content, sender, or timestamp in reaction data', + t('group:message.generic.invalid_content', { + postProcess: 'capitalizeFirst', + }), item ); return; @@ -435,10 +445,17 @@ export const ChatGroup = ({ const formatted = combineUIAndExtensionMsgs .filter((rawItem) => !rawItem?.chatReference) .map((item) => { + const message = ( +

+ {t('group:message.generic.group_key_created', { + postProcess: 'capitalizeFirst', + })} +

+ ); const additionalFields = item?.data === 'NDAwMQ==' ? { - text: '

First group key created.

', + text: message, } : {}; const divide = @@ -510,7 +527,9 @@ export const ChatGroup = ({ !newTimestamp ) { console.warn( - 'Invalid content, sender, or timestamp in reaction data', + t('group:message.generic.invalid_content', { + postProcess: 'capitalizeFirst', + }), item ); return; @@ -583,7 +602,12 @@ export const ChatGroup = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); } catch (error) { @@ -615,6 +639,7 @@ export const ChatGroup = ({ console.error('Error during ping:', error); } }; + const initWebsocketMessageGroup = () => { let socketLink = `${getBaseApiReactSocket()}/websockets/chat/messages?txGroupId=${selectedGroup}&encoding=BASE64&limit=100`; socketRef.current = new WebSocket(socketLink); @@ -709,7 +734,12 @@ export const ChatGroup = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); } catch (error) { @@ -744,7 +774,12 @@ export const ChatGroup = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); } catch (error) { @@ -760,12 +795,22 @@ export const ChatGroup = ({ const sendMessage = async () => { try { - if (messageSize > 4000) return; + if (messageSize > 4000) return; // TODO magic number if (isPrivate === null) - throw new Error('Unable to determine if group is private'); + throw new Error( + t('group:message.error.unable_determine_group_private', { + postProcess: 'capitalizeFirst', + }) + ); if (isSending) return; if (+balance < 4) - throw new Error('You need at least 4 QORT to send a message'); + // TODO magic number + throw new Error( + t('group:message.error.qortals_required', { + quantity: 4, + postProcess: 'capitalizeFirst', + }) + ); pauseAllQueues(); if (editorRef.current) { const htmlContent = editorRef.current.getHTML(); @@ -792,13 +837,16 @@ export const ChatGroup = ({ const imagesToPublish = []; const deleteImage = onEditMessage && isDeleteImage && messageHasImage(onEditMessage); + if (deleteImage) { const fee = await getFee('ARBITRARY'); - + // TODO translate await show({ publishFee: fee.fee + ' QORT', message: 'Would you like to delete your previous chat image?', }); + + // TODO magic string await window.sendMessage('publishOnQDN', { data: 'RA==', identifier: onEditMessage?.images[0]?.identifier, @@ -811,6 +859,7 @@ export const ChatGroup = ({ const base64ToSave = isPrivate ? await encryptChatMessage(imageToSave, secretKeyObject) : imageToSave; + // 1 represents public group, 0 is private const identifier = `grp-q-manager_${isPrivate ? 0 : 1}_group_${selectedGroup}_${uidImages.rnd()}`; imagesToPublish.push({ @@ -822,7 +871,6 @@ export const ChatGroup = ({ const res = await window.sendMessage( 'PUBLISH_MULTIPLE_QDN_RESOURCES', - { resources: imagesToPublish, }, @@ -979,6 +1027,7 @@ export const ChatGroup = ({ .setContent(message?.messageText || message?.text) .run(); }, []); + const handleReaction = useCallback( async (reaction, chatMessage, reactionState = true) => { try { @@ -1033,12 +1082,6 @@ export const ChatGroup = ({ chatReference: chatMessage.signature, }; addToQueue(sendMessageFunc, messageObj, 'chat-reaction', selectedGroup); - // setTimeout(() => { - // executeEvent("sent-new-message-group", {}) - // }, 150); - // clearEditorContent() - // setReplyMessage(null) - // send chat message } catch (error) { const errorMsg = error?.message || error; @@ -1417,6 +1460,7 @@ export const ChatGroup = ({ }} > Q-Manager + { setIsOpenQManager(false); @@ -1429,7 +1473,9 @@ export const ChatGroup = ({ /> + +
+ {showScrollButton && ( + {avatarFile?.name} + + {!myName && ( + { } } } catch (error) { - //error + console.log(error); } + const res = extractComponents(url); if (res) { const { service, name, identifier, path } = res; diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index a29e417..7b72d79 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -1,4 +1,5 @@ -import React, { +import { + memo, useCallback, useContext, useEffect, @@ -77,7 +78,7 @@ const getBadgeImg = (level) => { } }; -const UserBadge = React.memo(({ userInfo }) => { +const UserBadge = memo(({ userInfo }) => { return ( { ); }); -export const MessageItem = React.memo( +export const MessageItem = memo( ({ message, onSeen, diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 5890736..94894ab 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useRef } from 'react'; +import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { EditorProvider, useCurrentEditor } from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import { Color } from '@tiptap/extension-color'; @@ -42,7 +42,7 @@ function textMatcher(doc, from) { return { start, query }; } -const MenuBar = React.memo( +const MenuBar = memo( ({ setEditorRef, isChat, diff --git a/src/i18n/locales/en/group.json b/src/i18n/locales/en/group.json index e3b52ad..78292c4 100644 --- a/src/i18n/locales/en/group.json +++ b/src/i18n/locales/en/group.json @@ -61,6 +61,8 @@ "descrypt_wallet": "decrypting wallet...", "encryption_key": "the group's first common encryption key is in the process of creation. Please wait a few minutes for it to be retrieved by the network. Checking every 2 minutes...", "group_invited_you": "{{group}} has invited you", + "group_key_created": "first group key created.", + "invalid_content": "invalid content, sender, or timestamp in reaction data", "invalid_data": "error loading content: Invalid Data", "latest_promotion": "only the latest promotion from the week will be shown for your group.", "loading_members": "loading member list with names... please wait.", @@ -97,6 +99,7 @@ "qortals_required": "you need at least {{ quantity }} QORT to send a message", "timeout_reward": "timeout waiting for reward share confirmation", "thread_id": "unable to locate thread Id", + "unable_determine_group_private": "unable to determine if group is private", "unable_minting": "unable to start minting" }, "success": {