From 70f337324652ea8b35f519fb87cd0a20e16cf374 Mon Sep 17 00:00:00 2001 From: heheer <71265218+newfish-cmyk@users.noreply.github.com> Date: Fri, 17 Nov 2023 18:22:29 +0800 Subject: [PATCH] add image input (#486) * add image input * use json --- projects/app/public/locales/en/common.json | 7 +- projects/app/public/locales/zh/common.json | 5 +- .../src/components/ChatBox/MessageInput.tsx | 145 +++++++++++++++++- projects/app/src/components/ChatBox/index.tsx | 15 +- .../Icon/icons/core/chat/fileSelect.svg | 3 + projects/app/src/components/Icon/index.tsx | 3 +- .../src/components/Markdown/chat/Image.tsx | 18 +++ .../app/src/components/Markdown/index.tsx | 7 +- .../app/src/web/common/hooks/useSpeech.ts | 6 +- 9 files changed, 181 insertions(+), 28 deletions(-) create mode 100644 projects/app/src/components/Icon/icons/core/chat/fileSelect.svg create mode 100644 projects/app/src/components/Markdown/chat/Image.tsx diff --git a/projects/app/public/locales/en/common.json b/projects/app/public/locales/en/common.json index b8c2118e8..36b754852 100644 --- a/projects/app/public/locales/en/common.json +++ b/projects/app/public/locales/en/common.json @@ -233,10 +233,11 @@ }, "chat": { "Audio Speech Error": "Audio Speech Error", - "Speaking": "I'm listening...", "Record": "Speech", "Restart": "Restart", + "Select File": "Select file", "Send Message": "Send Message", + "Speaking": "I'm listening...", "Stop Speak": "Stop Speak", "Type a message": "Input problem", "tts": { @@ -589,8 +590,8 @@ "wallet": { "bill": { "Audio Speech": "Audio Speech", - "bill username": "User", - "Whisper": "Whisper" + "Whisper": "Whisper", + "bill username": "User" } } } diff --git a/projects/app/public/locales/zh/common.json b/projects/app/public/locales/zh/common.json index 881bd4a37..5340c1523 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/public/locales/zh/common.json @@ -235,6 +235,7 @@ "Audio Speech Error": "语音播报异常", "Record": "语音输入", "Restart": "重开对话", + "Select File": "选择文件", "Send Message": "发送", "Speaking": "我在听,请说...", "Stop Speak": "停止录音", @@ -589,8 +590,8 @@ "wallet": { "bill": { "Audio Speech": "语音播报", - "bill username": "用户", - "Whisper": "语音输入" + "Whisper": "语音输入", + "bill username": "用户" } } } diff --git a/projects/app/src/components/ChatBox/MessageInput.tsx b/projects/app/src/components/ChatBox/MessageInput.tsx index 28e77fd90..98c5c5ec1 100644 --- a/projects/app/src/components/ChatBox/MessageInput.tsx +++ b/projects/app/src/components/ChatBox/MessageInput.tsx @@ -1,12 +1,15 @@ import { useSpeech } from '@/web/common/hooks/useSpeech'; import { useSystemStore } from '@/web/common/system/useSystemStore'; -import { Box, Flex, Spinner, Textarea } from '@chakra-ui/react'; -import React, { useRef, useEffect } from 'react'; +import { Box, Flex, Image, Spinner, Textarea } from '@chakra-ui/react'; +import React, { useRef, useEffect, useCallback, useState, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import MyTooltip from '../MyTooltip'; import MyIcon from '../Icon'; import styles from './index.module.scss'; import { useRouter } from 'next/router'; +import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; +import { compressImgAndUpload } from '@/web/common/file/controller'; +import { useToast } from '@/web/common/hooks/useToast'; const MessageInput = ({ onChange, @@ -38,6 +41,60 @@ const MessageInput = ({ const { t } = useTranslation(); const textareaMinH = '22px'; const havInput = !!TextareaDom.current?.value; + const { toast } = useToast(); + const [imgBase64Array, setImgBase64Array] = useState([]); + const [fileList, setFileList] = useState([]); + const [imgSrcArray, setImgSrcArray] = useState([]); + + const { File, onOpen: onOpenSelectFile } = useSelectFile({ + fileType: '.jpg,.png', + multiple: true + }); + + useEffect(() => { + fileList.forEach((file) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = async () => { + setImgBase64Array((prev) => [...prev, reader.result as string]); + }; + }); + }, [fileList]); + + const onSelectFile = useCallback((e: File[]) => { + if (!e || e.length === 0) { + return; + } + setFileList(e); + }, []); + + const handleSend = useCallback(async () => { + try { + for (const file of fileList) { + const src = await compressImgAndUpload({ + file, + maxW: 1000, + maxH: 1000, + maxSize: 1024 * 1024 * 2 + }); + imgSrcArray.push(src); + } + } catch (err: any) { + toast({ + title: typeof err === 'string' ? err : '文件上传异常', + status: 'warning' + }); + } + + const textareaValue = TextareaDom.current?.value || ''; + const inputMessage = + imgSrcArray.length === 0 + ? textareaValue + : `\`\`\`img-block\n${JSON.stringify(imgSrcArray)}\n\`\`\`\n${textareaValue}`; + onSendMessage(inputMessage); + setImgBase64Array([]); + setImgSrcArray([]); + }, [TextareaDom, fileList, imgSrcArray, onSendMessage, toast]); useEffect(() => { if (!stream) { @@ -60,7 +117,7 @@ const MessageInput = ({ <> 0 ? '8px' : '18px'} position={'relative'} boxShadow={isSpeaking ? `0 0 10px rgba(54,111,255,0.4)` : `0 0 10px rgba(0,0,0,0.2)`} {...(isPc @@ -93,11 +150,74 @@ const MessageInput = ({ {t('chat.Converting to text')} + {/* file uploader */} + + + + + + + {/* file preview */} + + {imgBase64Array.length > 0 && + imgBase64Array.map((src, index) => ( + + { + setImgBase64Array((prev) => { + prev.splice(index, 1); + return [...prev]; + }); + }} + className="close-icon" + display={['', 'none']} + /> + {'img'} + + ))} + {/* input area */}