From 5d1ef926c80301bac54c628de2c5cb215ee7a8a9 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 1 May 2025 01:05:28 +0300 Subject: [PATCH 1/6] started --- src/App.tsx | 2 +- src/components/Chat/ChatGroup.tsx | 66 ++++++++++++++++++++++++++++++- src/components/Chat/TipTap.tsx | 55 +++++++++++++++++++++++++- src/qortalRequests/get.ts | 1 + 4 files changed, 121 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 558cd01..ad1f496 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3219,7 +3219,7 @@ function App() { onClick={onOkUnsavedChanges} autoFocus > - {t('core:action.decline', { + {t('core:action.continue_logout', { postProcess: 'capitalize', })} diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 0dd626e..2af7c2d 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -42,7 +42,7 @@ import CloseIcon from '@mui/icons-material/Close'; import { throttle } from 'lodash'; const uid = new ShortUniqueId({ length: 5 }); - +const uidImages = new ShortUniqueId({ length: 12 }); export const ChatGroup = ({ selectedGroup, secretKey, @@ -74,6 +74,7 @@ export const ChatGroup = ({ const [isOpenQManager, setIsOpenQManager] = useState(null); const [messageSize, setMessageSize] = useState(0); + const [chatImagesToSave, setChatImagesToSave] = useState([]); const hasInitializedWebsocket = useRef(false); const socketRef = useRef(null); // WebSocket reference const timeoutIdRef = useRef(null); // Timeout ID reference @@ -778,11 +779,45 @@ export const ChatGroup = ({ : { isEdited: chatReference ? true : false, }; + const imagesToPublish = []; + if (!chatReference && chatImagesToSave?.length > 0) { + chatImagesToSave.forEach((base64Img) => { + const identifier = `qchat_1_group_${selectedGroup}_${uidImages.rnd()}`; + imagesToPublish.push({ + service: 'IMAGE', + identifier, + name: myName, + base64: base64Img, + }); + }); + + const res = await window.sendMessage( + 'PUBLISH_MULTIPLE_QDN_RESOURCES', + + { + resources: imagesToPublish, + }, + 240000, + true + ); + console.log('res', res); + if (res !== true) throw new Error('Unable to publish images'); + } + const otherData = { repliedTo, ...(onEditMessage?.decryptedData || {}), type: chatReference ? 'edit' : '', specialId: uid.rnd(), + images: + onEditMessage?.images || + imagesToPublish.map((item) => { + return { + name: item.name, + identifier: item.identifier, + service: item.service, + }; + }), ...publicData, }; const objectMessage = { @@ -824,6 +859,7 @@ export const ChatGroup = ({ clearEditorContent(); setReplyMessage(null); setOnEditMessage(null); + setChatImagesToSave([]); } // send chat message } catch (error) { @@ -986,6 +1022,10 @@ export const ChatGroup = ({ const theme = useTheme(); + const insertImage = useCallback((img) => { + setChatImagesToSave((prev) => [...prev, img]); + }, []); + return (
+ + {chatImagesToSave?.map((imgBase64) => { + return ( + + ); + })} + {replyMessage && ( {messageSize > 750 && ( { - if (editor) { + if (editor && !isChat) { editor.view.dom.addEventListener('paste', handlePaste); return () => { editor.view.dom.removeEventListener('paste', handlePaste); @@ -366,12 +367,46 @@ export default ({ customEditorHeight, membersWithNames, enableMentions, + insertImage, }) => { const theme = useTheme(); const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useAtom( isDisabledEditorEnterAtom ); + const handleImageUpload = async (file) => { + try { + if (!file.type.includes('image')) return; + let compressedFile = file; + if (file.type !== 'image/gif') { + await new Promise((resolve) => { + new Compressor(file, { + quality: 0.6, + maxWidth: 1200, + mimeType: 'image/webp', + success(result) { + compressedFile = new File([result], 'image.webp', { + type: 'image/webp', + }); + resolve(); + }, + error(err) { + console.error('Image compression error:', err); + }, + }); + }); + } + + if (compressedFile) { + const toBase64 = await fileToBase64(compressedFile); + insertImage(toBase64); + console.log('toBase64', toBase64); + } + } catch (error) { + console.error(error); + } + }; + const extensionsFiltered = isChat ? extensions.filter((item) => item?.name !== 'image') : extensions; @@ -543,6 +578,24 @@ export default ({ } return false; }, + handlePaste(view, event) { + if (!isChat) return; + const items = event.clipboardData?.items; + if (!items) return false; + + for (const item of items) { + if (item.type.startsWith('image/')) { + const file = item.getAsFile(); + if (file) { + event.preventDefault(); // Block the default paste + handleImageUpload(file); // Custom handler + return true; // Let ProseMirror know we handled it + } + } + } + + return false; // fallback to default behavior otherwise + }, }} /> diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index a160305..37ef736 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -1218,6 +1218,7 @@ export const publishMultipleQDNResources = async ( sender, isFromExtension ) => { + console.log('data', data); const requiredFields = ['resources']; const missingFields: string[] = []; let feeAmount = null; From 2099401803a122c23523f587ed88e47fa0092cbc Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 10 May 2025 22:27:56 +0300 Subject: [PATCH 2/6] added event for UI language to qapp --- src/components/Apps/AppViewer.tsx | 43 ++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 06a64c5..7ffeecd 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -5,6 +5,7 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useFrame } from 'react-frame-component'; import { useQortalMessageListener } from './useQortalMessageListener'; import { useThemeContext } from '../Theme/ThemeContext'; +import { useTranslation } from 'react-i18next'; export const AppViewer = React.forwardRef( ({ app, hide, isDevMode, skipAuth }, iframeRef) => { @@ -22,11 +23,13 @@ export const AppViewer = React.forwardRef( ); const [url, setUrl] = useState(''); const { themeMode } = useThemeContext(); + const { i18n } = useTranslation(['core']); + const currentLang = i18n.language; useEffect(() => { if (app?.isPreview) return; if (isDevMode) { - setUrl(app?.url); + setUrl(app?.url + `?theme=${themeMode}&lang=${currentLang}`); return; } let hasQueryParam = false; @@ -35,14 +38,14 @@ export const AppViewer = React.forwardRef( } setUrl( - `${getBaseApiReact()}/render/${app?.service}/${app?.name}${app?.path != null ? `/${app?.path}` : ''}${hasQueryParam ? '&' : '?'}theme=${themeMode}&identifier=${app?.identifier != null && app?.identifier != 'null' ? app?.identifier : ''}` + `${getBaseApiReact()}/render/${app?.service}/${app?.name}${app?.path != null ? `/${app?.path}` : ''}${hasQueryParam ? '&' : '?'}theme=${themeMode}&lang=${currentLang}&identifier=${app?.identifier != null && app?.identifier != 'null' ? app?.identifier : ''}` ); }, [app?.service, app?.name, app?.identifier, app?.path, app?.isPreview]); useEffect(() => { if (app?.isPreview && app?.url) { resetHistory(); - setUrl(app.url); + setUrl(app.url + `&theme=${themeMode}&lang=${currentLang}`); } }, [app?.url, app?.isPreview]); const defaultUrl = useMemo(() => { @@ -55,11 +58,14 @@ export const AppViewer = React.forwardRef( if (isDevMode) { resetHistory(); if (!app?.isPreview || app?.isPrivate) { - setUrl(app?.url + `?time=${Date.now()}`); + setUrl( + app?.url + + `?time=${Date.now()}&theme=${themeMode}&lang=${currentLang}` + ); } return; } - const constructUrl = `${getBaseApiReact()}/render/${app?.service}/${app?.name}${path != null ? path : ''}?theme=${themeMode}&identifier=${app?.identifier != null ? app?.identifier : ''}&time=${new Date().getMilliseconds()}`; + const constructUrl = `${getBaseApiReact()}/render/${app?.service}/${app?.name}${path != null ? path : ''}?theme=${themeMode}&lang=${currentLang}&identifier=${app?.identifier != null ? app?.identifier : ''}&time=${new Date().getMilliseconds()}`; setUrl(constructUrl); } }; @@ -70,7 +76,7 @@ export const AppViewer = React.forwardRef( return () => { unsubscribeFromEvent('refreshApp', refreshAppFunc); }; - }, [app, path, isDevMode]); + }, [app, path, isDevMode, themeMode, currentLang]); useEffect(() => { const iframe = iframeRef?.current; @@ -87,6 +93,25 @@ export const AppViewer = React.forwardRef( } }, [themeMode]); + useEffect(() => { + const iframe = iframeRef?.current; + if (!iframe) return; + + try { + const targetOrigin = new URL(iframe.src).origin; + iframe.contentWindow?.postMessage( + { + action: 'LANGUAGE_CHANGED', + language: currentLang, + requestedHandler: 'UI', + }, + targetOrigin + ); + } catch (err) { + console.error('Failed to send theme change to iframe:', err); + } + }, [currentLang]); + const removeTrailingSlash = (str) => str.replace(/\/$/, ''); const copyLinkFunc = (e) => { @@ -181,12 +206,12 @@ export const AppViewer = React.forwardRef( } catch (error) { if (isDevMode) { setUrl( - `${url}${previousPath != null ? previousPath : ''}?theme=${themeMode}&time=${new Date().getMilliseconds()}&isManualNavigation=false` + `${url}${previousPath != null ? previousPath : ''}?theme=${themeMode}&lang=${currentLang}&time=${new Date().getMilliseconds()}&isManualNavigation=false` ); return; } setUrl( - `${getBaseApiReact()}/render/${app?.service}/${app?.name}${previousPath != null ? previousPath : ''}?theme=${themeMode}&identifier=${app?.identifier != null && app?.identifier != 'null' ? app?.identifier : ''}&time=${new Date().getMilliseconds()}&isManualNavigation=false` + `${getBaseApiReact()}/render/${app?.service}/${app?.name}${previousPath != null ? previousPath : ''}?theme=${themeMode}&lang=${currentLang}&identifier=${app?.identifier != null && app?.identifier != 'null' ? app?.identifier : ''}&time=${new Date().getMilliseconds()}&isManualNavigation=false` ); // iframeRef.current.contentWindow.location.href = previousPath; // Fallback URL update } @@ -209,7 +234,7 @@ export const AppViewer = React.forwardRef( navigateBackAppFunc ); }; - }, [app, history]); + }, [app, history, themeMode, currentLang]); // Function to navigate back in iframe const navigateForwardInIframe = async () => { From 54fa211e0653f0773d0ef3d135950ae9c6cd6d1e Mon Sep 17 00:00:00 2001 From: "nico.benaz" <52411515+nbenaglia@users.noreply.github.com> Date: Sat, 10 May 2025 23:09:18 +0200 Subject: [PATCH 3/6] Update src/components/Apps/AppViewer.tsx --- src/components/Apps/AppViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 7ffeecd..87f88e8 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -108,7 +108,7 @@ export const AppViewer = React.forwardRef( targetOrigin ); } catch (err) { - console.error('Failed to send theme change to iframe:', err); + console.error('Failed to send language change to iframe:', err); } }, [currentLang]); From 4767ddd9fa50fa4339d9085e6e3c34a90d3c8fd1 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sun, 11 May 2025 15:04:11 +0300 Subject: [PATCH 4/6] added publishing and deleting of images --- src/common/useModal.tsx | 4 +- src/components/Chat/ChatGroup.tsx | 211 +++++++++++++++++++++++----- src/components/Chat/ChatList.tsx | 5 + src/components/Chat/MessageItem.tsx | 6 +- src/components/Chat/TipTap.tsx | 1 - src/qortalRequests/get.ts | 3 +- src/utils/chat.ts | 22 +++ 7 files changed, 212 insertions(+), 40 deletions(-) create mode 100644 src/utils/chat.ts diff --git a/src/common/useModal.tsx b/src/common/useModal.tsx index e92dc99..29b5d7e 100644 --- a/src/common/useModal.tsx +++ b/src/common/useModal.tsx @@ -1,3 +1,5 @@ +//TODO + import { useRef, useState, useCallback, useMemo } from 'react'; interface State { @@ -34,7 +36,7 @@ export const useModal = () => { const onCancel = useCallback(() => { const { reject } = promiseConfig.current || {}; hide(); - reject?.(); + reject?.('Declined'); }, [hide]); return useMemo( diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 2af7c2d..f75758e 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -31,18 +31,28 @@ import { subscribeToEvent, unsubscribeFromEvent, } from '../../utils/events'; -import { Box, ButtonBase, Divider, Typography, useTheme } from '@mui/material'; +import { + Box, + ButtonBase, + Divider, + IconButton, + Tooltip, + Typography, + useTheme, +} from '@mui/material'; import ShortUniqueId from 'short-unique-id'; import { ReplyPreview } from './MessageItem'; import { ExitIcon } from '../../assets/Icons/ExitIcon'; import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from '../../constants/resourceTypes'; -import { isExtMsg } from '../../background'; +import { getFee, isExtMsg } from '../../background'; import AppViewerContainer from '../Apps/AppViewerContainer'; import CloseIcon from '@mui/icons-material/Close'; import { throttle } from 'lodash'; - +import ImageIcon from '@mui/icons-material/Image'; +import { messageHasImage } from '../../utils/chat'; const uid = new ShortUniqueId({ length: 5 }); const uidImages = new ShortUniqueId({ length: 12 }); + export const ChatGroup = ({ selectedGroup, secretKey, @@ -59,7 +69,7 @@ export const ChatGroup = ({ hideView, isPrivate, }) => { - const { isUserBlocked } = useContext(MyContext); + const { isUserBlocked, show } = useContext(MyContext); const [messages, setMessages] = useState([]); const [chatReferences, setChatReferences] = useState({}); const [isSending, setIsSending] = useState(false); @@ -72,7 +82,7 @@ export const ChatGroup = ({ const [replyMessage, setReplyMessage] = useState(null); const [onEditMessage, setOnEditMessage] = useState(null); const [isOpenQManager, setIsOpenQManager] = useState(null); - + const [isDeleteImage, setIsDeleteImage] = useState(false); const [messageSize, setMessageSize] = useState(0); const [chatImagesToSave, setChatImagesToSave] = useState([]); const hasInitializedWebsocket = useRef(false); @@ -780,15 +790,34 @@ export const ChatGroup = ({ isEdited: chatReference ? true : false, }; const imagesToPublish = []; - if (!chatReference && chatImagesToSave?.length > 0) { - chatImagesToSave.forEach((base64Img) => { - const identifier = `qchat_1_group_${selectedGroup}_${uidImages.rnd()}`; - imagesToPublish.push({ - service: 'IMAGE', - identifier, - name: myName, - base64: base64Img, - }); + const deleteImage = + onEditMessage && isDeleteImage && messageHasImage(onEditMessage); + if (deleteImage) { + const fee = await getFee('ARBITRARY'); + + await show({ + publishFee: fee.fee + ' QORT', + message: 'Would you like to delete your previous chat image?', + }); + await window.sendMessage('publishOnQDN', { + data: 'RA==', + identifier: onEditMessage?.images[0]?.identifier, + service: onEditMessage?.images[0]?.service, + }); + } + if (chatImagesToSave?.length > 0) { + const imageToSave = chatImagesToSave[0]; + + 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({ + service: 'IMAGE', + identifier, + name: myName, + base64: base64ToSave, }); const res = await window.sendMessage( @@ -800,24 +829,29 @@ export const ChatGroup = ({ 240000, true ); - console.log('res', res); if (res !== true) throw new Error('Unable to publish images'); } + const images = + imagesToPublish?.length > 0 + ? imagesToPublish.map((item) => { + return { + name: item.name, + identifier: item.identifier, + service: item.service, + timestamp: Date.now(), + }; + }) + : chatReference + ? onEditMessage?.images || [] + : []; + const otherData = { repliedTo, ...(onEditMessage?.decryptedData || {}), type: chatReference ? 'edit' : '', specialId: uid.rnd(), - images: - onEditMessage?.images || - imagesToPublish.map((item) => { - return { - name: item.name, - identifier: item.identifier, - service: item.service, - }; - }), + images: images, ...publicData, }; const objectMessage = { @@ -825,6 +859,7 @@ export const ChatGroup = ({ [isPrivate ? 'message' : 'messageText']: message, version: 3, }; + const message64: any = await objectToBase64(objectMessage); const encryptSingle = @@ -859,6 +894,7 @@ export const ChatGroup = ({ clearEditorContent(); setReplyMessage(null); setOnEditMessage(null); + setIsDeleteImage(false); setChatImagesToSave([]); } // send chat message @@ -925,6 +961,8 @@ export const ChatGroup = ({ } setReplyMessage(message); setOnEditMessage(null); + setIsDeleteImage(false); + setChatImagesToSave([]); editorRef?.current?.chain().focus(); }, [onEditMessage] @@ -1022,10 +1060,23 @@ export const ChatGroup = ({ const theme = useTheme(); - const insertImage = useCallback((img) => { - setChatImagesToSave((prev) => [...prev, img]); - }, []); - + const insertImage = useCallback( + (img) => { + if ( + chatImagesToSave?.length > 0 || + (messageHasImage(onEditMessage) && !isDeleteImage) + ) { + setInfoSnack({ + type: 'error', + message: 'This message already has an image', + }); + setOpenSnack(true); + return; + } + setChatImagesToSave((prev) => [...prev, img]); + }, + [chatImagesToSave, onEditMessage?.images, isDeleteImage] + ); return (
- {chatImagesToSave?.map((imgBase64) => { - return ( - ( +
+ + + setIsDeleteImage(true)} + size="small" + sx={{ + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + backgroundColor: (theme) => + theme.palette.background.paper, + color: (theme) => theme.palette.text.primary, + borderRadius: '50%', + opacity: 0, + transition: 'opacity 0.2s', + boxShadow: (theme) => theme.shadows[2], + '&:hover': { + backgroundColor: (theme) => + theme.palette.background.default, + opacity: 1, + }, + pointerEvents: 'auto', + }} + > + + + +
+ ))} + {chatImagesToSave.map((imgBase64, index) => ( +
+ - ); - })} + + + setChatImagesToSave((prev) => + prev.filter((_, i) => i !== index) + ) + } + size="small" + sx={{ + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + backgroundColor: (theme) => + theme.palette.background.paper, + color: (theme) => theme.palette.text.primary, + borderRadius: '50%', + opacity: 0, + transition: 'opacity 0.2s', + boxShadow: (theme) => theme.shadows[2], + '&:hover': { + backgroundColor: (theme) => + theme.palette.background.default, + opacity: 1, + }, + pointerEvents: 'auto', + }} + > + + + +
+ ))} {replyMessage && ( @@ -1149,7 +1289,8 @@ export const ChatGroup = ({ onClick={() => { setReplyMessage(null); setOnEditMessage(null); - + setIsDeleteImage(false); + setChatImagesToSave([]); clearEditorContent(); }} > diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 8362fad..a7adc41 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -292,6 +292,11 @@ export const ChatList = ({ message.editTimestamp = chatReferences[message.signature]?.edit?.timestamp; } + if (chatReferences[message.signature]?.edit?.images) { + message.images = + chatReferences[message.signature]?.edit?.images; + message.isEdit = true; + } } // Check if message is updating diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index d861dac..a29e417 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -45,6 +45,8 @@ import level7Img from '../../assets/badges/level-7.png'; import level8Img from '../../assets/badges/level-8.png'; import level9Img from '../../assets/badges/level-9.png'; import level10Img from '../../assets/badges/level-10.png'; +import { Embed } from '../Embeds/Embed'; +import { buildImageEmbedLink, messageHasImage } from '../../utils/chat'; const getBadgeImg = (level) => { switch (level?.toString()) { @@ -366,7 +368,9 @@ export const MessageItem = React.memo( ) : ( )} - + {message?.images && messageHasImage(message) && ( + + )} { - console.log('data', data); const requiredFields = ['resources']; const missingFields: string[] = []; let feeAmount = null; @@ -1695,7 +1694,7 @@ export const sendChatMessage = async (data, isFromExtension, appInfo) => { ? fullMessageObject : { messageText: tiptapJson, - images: [''], + images: [], repliedTo: '', version: 3, }; diff --git a/src/utils/chat.ts b/src/utils/chat.ts new file mode 100644 index 0000000..c7099c0 --- /dev/null +++ b/src/utils/chat.ts @@ -0,0 +1,22 @@ +export function buildImageEmbedLink(image?: { + name?: string; + identifier?: string; + service?: string; + timestamp?: number; +}): string | null { + if (!image?.name || !image.identifier || !image.service) return null; + + const base = `qortal://use-embed/IMAGE?name=${image.name}&identifier=${image.identifier}&service=${image.service}&mimeType=image%2Fpng×tamp=${image?.timestamp || ''}`; + + const isEncrypted = image.identifier.startsWith('grp-q-manager_0'); + return isEncrypted ? `${base}&encryptionType=group` : base; +} + +export const messageHasImage = (message) => { + return ( + Array.isArray(message?.images) && + message.images[0]?.identifier && + message.images[0]?.name && + message.images[0]?.service + ); +}; From 62d1c4e08d05dc6570e228ca474f35967281bc7e Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sun, 11 May 2025 15:10:43 +0300 Subject: [PATCH 5/6] fix --- src/components/Chat/TipTap.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 54a8985..353e3de 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -385,9 +385,7 @@ export default ({ maxWidth: 1200, mimeType: 'image/webp', success(result) { - compressedFile = new File([result], 'image.webp', { - type: 'image/webp', - }); + compressedFile = result; resolve(); }, error(err) { From 0b9cc572eda71979002bc4ab9ba95eeba1aa9c1a Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 12 May 2025 02:11:56 +0300 Subject: [PATCH 6/6] fix viewing of update images --- src/components/Chat/ChatGroup.tsx | 4 ++- src/components/Chat/TipTap.tsx | 57 ++++++++++++++++--------------- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index f75758e..a4f6e5a 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -843,7 +843,9 @@ export const ChatGroup = ({ }; }) : chatReference - ? onEditMessage?.images || [] + ? isDeleteImage + ? [] + : onEditMessage?.images || [] : []; const otherData = { diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 353e3de..5890736 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -121,7 +121,7 @@ const MenuBar = React.memo( editor.view.dom.removeEventListener('paste', handlePaste); }; } - }, [editor]); + }, [editor, isChat]); return (
@@ -374,35 +374,38 @@ export default ({ isDisabledEditorEnterAtom ); - const handleImageUpload = async (file) => { - try { - if (!file.type.includes('image')) return; - let compressedFile = file; - if (file.type !== 'image/gif') { - await new Promise((resolve) => { - new Compressor(file, { - quality: 0.6, - maxWidth: 1200, - mimeType: 'image/webp', - success(result) { - compressedFile = result; - resolve(); - }, - error(err) { - console.error('Image compression error:', err); - }, + const handleImageUpload = useCallback( + async (file) => { + try { + if (!file.type.includes('image')) return; + let compressedFile = file; + if (file.type !== 'image/gif') { + await new Promise((resolve) => { + new Compressor(file, { + quality: 0.6, + maxWidth: 1200, + mimeType: 'image/webp', + success(result) { + compressedFile = result; + resolve(); + }, + error(err) { + console.error('Image compression error:', err); + }, + }); }); - }); - } + } - if (compressedFile) { - const toBase64 = await fileToBase64(compressedFile); - insertImage(toBase64); + if (compressedFile) { + const toBase64 = await fileToBase64(compressedFile); + insertImage(toBase64); + } + } catch (error) { + console.error(error); } - } catch (error) { - console.error(error); - } - }; + }, + [insertImage] + ); const extensionsFiltered = isChat ? extensions.filter((item) => item?.name !== 'image')