From 8878e238d2e5edada023298c31ea6244e819c2a3 Mon Sep 17 00:00:00 2001 From: dakai Date: Sat, 5 Oct 2024 10:27:22 +0800 Subject: [PATCH 01/15] feat: add functionality to upload documents in chat --- app/components/chat.module.scss | 59 +++++++++++++- app/components/chat.tsx | 135 ++++++++++++++++++++++++++------ app/icons/upload-doc.svg | 1 + package.json | 6 +- yarn.lock | 22 +++++- 5 files changed, 193 insertions(+), 30 deletions(-) create mode 100644 app/icons/upload-doc.svg diff --git a/app/components/chat.module.scss b/app/components/chat.module.scss index 73542fc67f1..9dc57c0b9a5 100644 --- a/app/components/chat.module.scss +++ b/app/components/chat.module.scss @@ -1,10 +1,18 @@ @import "../styles/animation.scss"; -.attach-images { +.attachments { position: absolute; left: 30px; bottom: 32px; display: flex; + flex-direction: row; +} + +.attach-images { + //position: absolute; + //left: 30px; + //bottom: 32px; + display: flex; } .attach-image { @@ -42,6 +50,53 @@ } } +.attach-files { + //position: absolute; + //left: 30px; + //bottom: 32px; + display: flex; + flex-direction: column; + row-gap: 11px; +} + +.attach-file { + cursor: default; + //width: 64px; + width: 14px; + height: 14px; + //border: rgba($color: #888, $alpha: 0.2) 1px solid; + border-radius: 5px; + margin-right: 10px; + background-size: cover; + background-position: center; + background-color: var(--white); + + .attach-image-mask { + width: 100%; + height: 100%; + opacity: 0; + transition: all ease 0.2s; + } + + .attach-image-mask:hover { + opacity: 1; + } + + .delete-image { + width: 24px; + height: 24px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + border-radius: 5px; + float: right; + background-color: var(--white); + } +} + + + .chat-input-actions { display: flex; flex-wrap: wrap; @@ -693,4 +748,4 @@ .shortcut-key span { font-size: 12px; color: var(--black); -} \ No newline at end of file +} diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 3d519dee722..1159a22db88 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -46,6 +46,7 @@ import StyleIcon from "../icons/palette.svg"; import PluginIcon from "../icons/plugin.svg"; import ShortcutkeyIcon from "../icons/shortcutkey.svg"; import ReloadIcon from "../icons/reload.svg"; +import UploadDocIcon from "../icons/upload-doc.svg"; import { ChatMessage, @@ -96,6 +97,7 @@ import { showToast, } from "./ui-lib"; import { useNavigate } from "react-router-dom"; +import { FileIcon, defaultStyles } from "react-file-icon"; import { CHAT_PAGE_SIZE, DEFAULT_TTS_ENGINE, @@ -442,8 +444,10 @@ function useScrollToBottom( } export function ChatActions(props: { + uploadDocument: () => void; uploadImage: () => void; setAttachImages: (images: string[]) => void; + setAttachFiles: (files: string[]) => void; setUploading: (uploading: boolean) => void; showPromptModal: () => void; scrollToBottom: () => void; @@ -502,7 +506,8 @@ export function ChatActions(props: { }, [models, currentModel, currentProviderName]); const [showModelSelector, setShowModelSelector] = useState(false); const [showPluginSelector, setShowPluginSelector] = useState(false); - const [showUploadImage, setShowUploadImage] = useState(false); + // TODO: remember to make it false + const [showUploadImage, setShowUploadImage] = useState(true); const [showSizeSelector, setShowSizeSelector] = useState(false); const [showQualitySelector, setShowQualitySelector] = useState(false); @@ -521,7 +526,8 @@ export function ChatActions(props: { useEffect(() => { const show = isVisionModel(currentModel); - setShowUploadImage(show); + //NOTE: temporary disable upload image + //setShowUploadImage(show); if (!show) { props.setAttachImages([]); props.setUploading(false); @@ -577,6 +583,11 @@ export function ChatActions(props: { icon={props.uploading ? : } /> )} + : } + /> ([]); + const [attachFiles, setAttachFiles] = useState([]); const [uploading, setUploading] = useState(false); // prompt hints @@ -1460,6 +1472,51 @@ function _Chat() { [attachImages, chatStore], ); + async function uploadDocument() { + const files: string[] = []; + files.push(...attachFiles); + + files.push( + ...(await new Promise((res, rej) => { + const fileInput = document.createElement("input"); + fileInput.type = "file"; + fileInput.accept = "text/*"; + fileInput.multiple = true; + fileInput.onchange = (event: any) => { + setUploading(true); + const files = event.target.files; + const imagesData: string[] = []; + for (let i = 0; i < files.length; i++) { + const file = event.target.files[i]; + uploadImageRemote(file) + .then((dataUrl) => { + imagesData.push(dataUrl); + if ( + imagesData.length === 3 || + imagesData.length === files.length + ) { + setUploading(false); + res(imagesData); + } + }) + .catch((e) => { + setUploading(false); + rej(e); + }); + } + }; + fileInput.click(); + })), + ); + + const filesLength = files.length; + if (filesLength > 3) { + files.splice(3, filesLength - 3); + } + setAttachFiles(files); + console.log("upload files: ", files); + } + async function uploadImage() { const images: string[] = []; images.push(...attachImages); @@ -1897,8 +1954,10 @@ function _Chat() { setShowPromptModal(true)} scrollToBottom={scrollToBottom} @@ -1920,7 +1979,7 @@ function _Chat() { />