mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 15:00:57 +03:00
Feature/add ability to upload file from chat (#3059)
add ability to upload file from chat
This commit is contained in:
@@ -1,11 +1,16 @@
|
||||
import client from './client'
|
||||
|
||||
const upsertVectorStore = (id, input) => client.post(`/vector/internal-upsert/${id}`, input)
|
||||
const upsertVectorStoreWithFormData = (id, formData) =>
|
||||
client.post(`/vector/internal-upsert/${id}`, formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
const getUpsertHistory = (id, params = {}) => client.get(`/upsert-history/${id}`, { params: { order: 'DESC', ...params } })
|
||||
const deleteUpsertHistory = (ids) => client.patch(`/upsert-history`, { ids })
|
||||
|
||||
export default {
|
||||
getUpsertHistory,
|
||||
upsertVectorStore,
|
||||
upsertVectorStoreWithFormData,
|
||||
deleteUpsertHistory
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import userPNG from '@/assets/images/account.png'
|
||||
import msgEmptySVG from '@/assets/images/message_empty.svg'
|
||||
import multiagent_supervisorPNG from '@/assets/images/multiagent_supervisor.png'
|
||||
import multiagent_workerPNG from '@/assets/images/multiagent_worker.png'
|
||||
import { IconTool, IconDeviceSdCard, IconFileExport, IconEraser, IconX, IconDownload } from '@tabler/icons-react'
|
||||
import { IconTool, IconDeviceSdCard, IconFileExport, IconEraser, IconX, IconDownload, IconPaperclip } from '@tabler/icons-react'
|
||||
|
||||
// Project import
|
||||
import { MemoizedReactMarkdown } from '@/ui-component/markdown/MemoizedReactMarkdown'
|
||||
@@ -438,6 +438,59 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
setSourceDialogOpen(true)
|
||||
}
|
||||
|
||||
const renderFileUploads = (item, index) => {
|
||||
if (item?.mime?.startsWith('image/')) {
|
||||
return (
|
||||
<Card
|
||||
key={index}
|
||||
sx={{
|
||||
p: 0,
|
||||
m: 0,
|
||||
maxWidth: 128,
|
||||
marginRight: '10px',
|
||||
flex: '0 0 auto'
|
||||
}}
|
||||
>
|
||||
<CardMedia component='img' image={item.data} sx={{ height: 64 }} alt={'preview'} style={messageImageStyle} />
|
||||
</Card>
|
||||
)
|
||||
} else if (item?.mime?.startsWith('audio/')) {
|
||||
return (
|
||||
/* eslint-disable jsx-a11y/media-has-caption */
|
||||
<audio controls='controls'>
|
||||
Your browser does not support the <audio> tag.
|
||||
<source src={item.data} type={item.mime} />
|
||||
</audio>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Card
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
height: '48px',
|
||||
width: 'max-content',
|
||||
p: 2,
|
||||
mr: 1,
|
||||
flex: '0 0 auto',
|
||||
backgroundColor: customization.isDarkMode ? 'rgba(0, 0, 0, 0.3)' : 'transparent'
|
||||
}}
|
||||
variant='outlined'
|
||||
>
|
||||
<IconPaperclip size={20} />
|
||||
<span
|
||||
style={{
|
||||
marginLeft: '5px',
|
||||
color: customization.isDarkMode ? 'white' : 'inherit'
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</span>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const leadEmailFromChatMessages = chatMessages.filter((message) => message.type === 'userMessage' && message.leadEmail)
|
||||
if (leadEmailFromChatMessages.length) {
|
||||
@@ -855,37 +908,7 @@ const ViewMessagesDialog = ({ show, dialogProps, onCancel }) => {
|
||||
}}
|
||||
>
|
||||
{message.fileUploads.map((item, index) => {
|
||||
return (
|
||||
<>
|
||||
{item.mime.startsWith('image/') ? (
|
||||
<Card
|
||||
key={index}
|
||||
sx={{
|
||||
p: 0,
|
||||
m: 0,
|
||||
maxWidth: 128,
|
||||
marginRight: '10px',
|
||||
flex: '0 0 auto'
|
||||
}}
|
||||
>
|
||||
<CardMedia
|
||||
component='img'
|
||||
image={item.data}
|
||||
sx={{ height: 64 }}
|
||||
alt={'preview'}
|
||||
style={messageImageStyle}
|
||||
/>
|
||||
</Card>
|
||||
) : (
|
||||
// eslint-disable-next-line jsx-a11y/media-has-caption
|
||||
<audio controls='controls'>
|
||||
Your browser does not support the <audio>
|
||||
tag.
|
||||
<source src={item.data} type={item.mime} />
|
||||
</audio>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
return <>{renderFileUploads(item, index)}</>
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -37,7 +37,8 @@ import {
|
||||
IconTool,
|
||||
IconSquareFilled,
|
||||
IconDeviceSdCard,
|
||||
IconCheck
|
||||
IconCheck,
|
||||
IconPaperclip
|
||||
} from '@tabler/icons-react'
|
||||
import robotPNG from '@/assets/images/robot.png'
|
||||
import userPNG from '@/assets/images/account.png'
|
||||
@@ -64,6 +65,7 @@ import './ChatMessage.css'
|
||||
import chatmessageApi from '@/api/chatmessage'
|
||||
import chatflowsApi from '@/api/chatflows'
|
||||
import predictionApi from '@/api/prediction'
|
||||
import vectorstoreApi from '@/api/vectorstore'
|
||||
import chatmessagefeedbackApi from '@/api/chatmessagefeedback'
|
||||
import leadsApi from '@/api/lead'
|
||||
|
||||
@@ -84,6 +86,71 @@ const messageImageStyle = {
|
||||
objectFit: 'cover'
|
||||
}
|
||||
|
||||
const CardWithDeleteOverlay = ({ item, customization, onDelete }) => {
|
||||
const [isHovered, setIsHovered] = useState(false)
|
||||
const defaultBackgroundColor = customization.isDarkMode ? 'rgba(0, 0, 0, 0.3)' : 'transparent'
|
||||
|
||||
return (
|
||||
<div
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
style={{ position: 'relative', display: 'inline-block' }}
|
||||
>
|
||||
<Card
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
height: '48px',
|
||||
width: 'max-content',
|
||||
p: 2,
|
||||
mr: 1,
|
||||
flex: '0 0 auto',
|
||||
transition: 'opacity 0.3s',
|
||||
opacity: isHovered ? 1 : 1,
|
||||
backgroundColor: isHovered ? 'rgba(0, 0, 0, 0.3)' : defaultBackgroundColor
|
||||
}}
|
||||
variant='outlined'
|
||||
>
|
||||
<IconPaperclip size={20} style={{ transition: 'filter 0.3s', filter: isHovered ? 'blur(2px)' : 'none' }} />
|
||||
<span
|
||||
style={{
|
||||
marginLeft: '5px',
|
||||
color: customization.isDarkMode ? 'white' : 'inherit',
|
||||
transition: 'filter 0.3s',
|
||||
filter: isHovered ? 'blur(2px)' : 'none'
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</span>
|
||||
</Card>
|
||||
{isHovered && (
|
||||
<Button
|
||||
onClick={() => onDelete(item)}
|
||||
startIcon={<IconTrash color='white' size={22} />}
|
||||
title='Remove attachment'
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: 'transparent',
|
||||
'&:hover': {
|
||||
backgroundColor: 'transparent'
|
||||
}
|
||||
}}
|
||||
></Button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
CardWithDeleteOverlay.propTypes = {
|
||||
item: PropTypes.object,
|
||||
customization: PropTypes.object,
|
||||
onDelete: PropTypes.func
|
||||
}
|
||||
|
||||
export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, previews, setPreviews }) => {
|
||||
const theme = useTheme()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
@@ -111,6 +178,9 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
const [sourceDialogProps, setSourceDialogProps] = useState({})
|
||||
const [chatId, setChatId] = useState(uuidv4())
|
||||
const [isMessageStopping, setIsMessageStopping] = useState(false)
|
||||
const [uploadedFiles, setUploadedFiles] = useState([])
|
||||
const [imageUploadAllowedTypes, setImageUploadAllowedTypes] = useState('')
|
||||
const [fileUploadAllowedTypes, setFileUploadAllowedTypes] = useState('')
|
||||
|
||||
const inputRef = useRef(null)
|
||||
const getChatmessageApi = useApi(chatmessageApi.getInternalChatmessageFromChatflow)
|
||||
@@ -134,8 +204,10 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
const [isLeadSaved, setIsLeadSaved] = useState(false)
|
||||
|
||||
// drag & drop and file input
|
||||
const imgUploadRef = useRef(null)
|
||||
const fileUploadRef = useRef(null)
|
||||
const [isChatFlowAvailableForUploads, setIsChatFlowAvailableForUploads] = useState(false)
|
||||
const [isChatFlowAvailableForImageUploads, setIsChatFlowAvailableForImageUploads] = useState(false)
|
||||
const [isChatFlowAvailableForFileUploads, setIsChatFlowAvailableForFileUploads] = useState(false)
|
||||
const [isDragActive, setIsDragActive] = useState(false)
|
||||
|
||||
// recording
|
||||
@@ -158,6 +230,18 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
}
|
||||
})
|
||||
}
|
||||
if (constraints.isFileUploadAllowed) {
|
||||
const fileExt = file.name.split('.').pop()
|
||||
if (fileExt) {
|
||||
constraints.fileUploadSizeAndTypes.map((allowed) => {
|
||||
if (allowed.fileTypes.length === 1 && allowed.fileTypes[0] === '*') {
|
||||
acceptFile = true
|
||||
} else if (allowed.fileTypes.includes(`.${fileExt}`)) {
|
||||
acceptFile = true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
if (!acceptFile) {
|
||||
alert(`Cannot upload file. Kindly check the allowed file types and maximum allowed size.`)
|
||||
}
|
||||
@@ -165,12 +249,14 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
}
|
||||
|
||||
const handleDrop = async (e) => {
|
||||
if (!isChatFlowAvailableForUploads) {
|
||||
if (!isChatFlowAvailableForImageUploads && !isChatFlowAvailableForFileUploads) {
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
setIsDragActive(false)
|
||||
let files = []
|
||||
let uploadedFiles = []
|
||||
|
||||
if (e.dataTransfer.files.length > 0) {
|
||||
for (const file of e.dataTransfer.files) {
|
||||
if (isFileAllowedForUpload(file) === false) {
|
||||
@@ -178,6 +264,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
}
|
||||
const reader = new FileReader()
|
||||
const { name } = file
|
||||
uploadedFiles.push(file)
|
||||
files.push(
|
||||
new Promise((resolve) => {
|
||||
reader.onload = (evt) => {
|
||||
@@ -188,7 +275,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
let previewUrl
|
||||
if (file.type.startsWith('audio/')) {
|
||||
previewUrl = audioUploadSVG
|
||||
} else if (file.type.startsWith('image/')) {
|
||||
} else {
|
||||
previewUrl = URL.createObjectURL(file)
|
||||
}
|
||||
resolve({
|
||||
@@ -205,10 +292,12 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
}
|
||||
|
||||
const newFiles = await Promise.all(files)
|
||||
setUploadedFiles(uploadedFiles)
|
||||
setPreviews((prevPreviews) => [...prevPreviews, ...newFiles])
|
||||
}
|
||||
|
||||
if (e.dataTransfer.items) {
|
||||
//TODO set files
|
||||
for (const item of e.dataTransfer.items) {
|
||||
if (item.kind === 'string' && item.type.match('^text/uri-list')) {
|
||||
item.getAsString((s) => {
|
||||
@@ -246,10 +335,12 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
return
|
||||
}
|
||||
let files = []
|
||||
let uploadedFiles = []
|
||||
for (const file of event.target.files) {
|
||||
if (isFileAllowedForUpload(file) === false) {
|
||||
return
|
||||
}
|
||||
uploadedFiles.push(file)
|
||||
const reader = new FileReader()
|
||||
const { name } = file
|
||||
files.push(
|
||||
@@ -273,6 +364,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
}
|
||||
|
||||
const newFiles = await Promise.all(files)
|
||||
setUploadedFiles(uploadedFiles)
|
||||
setPreviews((prevPreviews) => [...prevPreviews, ...newFiles])
|
||||
// 👇️ reset file input
|
||||
event.target.value = null
|
||||
@@ -303,7 +395,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
}
|
||||
|
||||
const handleDrag = (e) => {
|
||||
if (isChatFlowAvailableForUploads) {
|
||||
if (isChatFlowAvailableForImageUploads || isChatFlowAvailableForFileUploads) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
if (e.type === 'dragenter' || e.type === 'dragover') {
|
||||
@@ -343,11 +435,16 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
setPreviews(previews.filter((item) => item !== itemToDelete))
|
||||
}
|
||||
|
||||
const handleUploadClick = () => {
|
||||
const handleFileUploadClick = () => {
|
||||
// 👇️ open file input box on click of another element
|
||||
fileUploadRef.current.click()
|
||||
}
|
||||
|
||||
const handleImageUploadClick = () => {
|
||||
// 👇️ open file input box on click of another element
|
||||
imgUploadRef.current.click()
|
||||
}
|
||||
|
||||
const clearPreviews = () => {
|
||||
// Revoke the data uris to avoid memory leaks
|
||||
previews.forEach((file) => URL.revokeObjectURL(file.preview))
|
||||
@@ -489,6 +586,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
setMessages((prevMessages) => [...prevMessages, { message, type: 'apiMessage' }])
|
||||
setLoading(false)
|
||||
setUserInput('')
|
||||
setUploadedFiles([])
|
||||
setTimeout(() => {
|
||||
inputRef.current?.focus()
|
||||
}, 100)
|
||||
@@ -526,7 +624,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
if (selectedInput !== undefined && selectedInput.trim() !== '') input = selectedInput
|
||||
|
||||
setLoading(true)
|
||||
const urls = previews.map((item) => {
|
||||
const uploads = previews.map((item) => {
|
||||
return {
|
||||
data: item.data,
|
||||
type: item.type,
|
||||
@@ -535,7 +633,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
}
|
||||
})
|
||||
clearPreviews()
|
||||
setMessages((prevMessages) => [...prevMessages, { message: input, type: 'userMessage', fileUploads: urls }])
|
||||
setMessages((prevMessages) => [...prevMessages, { message: input, type: 'userMessage', fileUploads: uploads }])
|
||||
|
||||
// Send user question to Prediction Internal API
|
||||
try {
|
||||
@@ -543,11 +641,30 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
question: input,
|
||||
chatId
|
||||
}
|
||||
if (urls && urls.length > 0) params.uploads = urls
|
||||
if (uploads && uploads.length > 0) params.uploads = uploads
|
||||
if (leadEmail) params.leadEmail = leadEmail
|
||||
if (isChatFlowAvailableToStream) params.socketIOClientId = socketIOClientId
|
||||
if (action) params.action = action
|
||||
|
||||
if (uploadedFiles.length > 0) {
|
||||
const formData = new FormData()
|
||||
for (const file of uploadedFiles) {
|
||||
formData.append('files', file)
|
||||
}
|
||||
formData.append('chatId', chatId)
|
||||
|
||||
const response = await vectorstoreApi.upsertVectorStoreWithFormData(chatflowid, formData)
|
||||
if (!response.data) {
|
||||
setMessages((prevMessages) => [...prevMessages, { message: 'Unable to upload documents', type: 'apiMessage' }])
|
||||
} else {
|
||||
// delay for vector store to be updated
|
||||
const delay = (delayInms) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, delayInms))
|
||||
}
|
||||
await delay(2500) //TODO: check if embeddings can be retrieved using file name as metadata filter
|
||||
}
|
||||
}
|
||||
|
||||
const response = await predictionApi.sendMessageAndGetPrediction(chatflowid, params)
|
||||
|
||||
if (response.data) {
|
||||
@@ -598,6 +715,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
setLocalStorageChatflow(chatflowid, data.chatId)
|
||||
setLoading(false)
|
||||
setUserInput('')
|
||||
setUploadedFiles([])
|
||||
setTimeout(() => {
|
||||
inputRef.current?.focus()
|
||||
scrollToBottom()
|
||||
@@ -717,8 +835,11 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
// Get chatflow uploads capability
|
||||
useEffect(() => {
|
||||
if (getAllowChatFlowUploads.data) {
|
||||
setIsChatFlowAvailableForUploads(getAllowChatFlowUploads.data?.isImageUploadAllowed ?? false)
|
||||
setIsChatFlowAvailableForImageUploads(getAllowChatFlowUploads.data?.isImageUploadAllowed ?? false)
|
||||
setIsChatFlowAvailableForFileUploads(getAllowChatFlowUploads.data?.isFileUploadAllowed ?? false)
|
||||
setIsChatFlowAvailableForSpeech(getAllowChatFlowUploads.data?.isSpeechToTextEnabled ?? false)
|
||||
setImageUploadAllowedTypes(getAllowChatFlowUploads.data?.imgUploadSizeAndTypes.map((allowed) => allowed.fileTypes).join(','))
|
||||
setFileUploadAllowedTypes(getAllowChatFlowUploads.data?.fileUploadSizeAndTypes.map((allowed) => allowed.fileTypes).join(','))
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [getAllowChatFlowUploads.data])
|
||||
@@ -822,6 +943,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
|
||||
return () => {
|
||||
setUserInput('')
|
||||
setUploadedFiles([])
|
||||
setLoading(false)
|
||||
setMessages([
|
||||
{
|
||||
@@ -965,6 +1087,105 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
)
|
||||
}
|
||||
|
||||
const previewDisplay = (item) => {
|
||||
if (item.mime.startsWith('image/')) {
|
||||
return (
|
||||
<ImageButton
|
||||
focusRipple
|
||||
style={{
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
marginRight: '10px',
|
||||
flex: '0 0 auto'
|
||||
}}
|
||||
onClick={() => handleDeletePreview(item)}
|
||||
>
|
||||
<ImageSrc style={{ backgroundImage: `url(${item.data})` }} />
|
||||
<ImageBackdrop className='MuiImageBackdrop-root' />
|
||||
<ImageMarked className='MuiImageMarked-root'>
|
||||
<IconTrash size={20} color='white' />
|
||||
</ImageMarked>
|
||||
</ImageButton>
|
||||
)
|
||||
} else if (item.mime.startsWith('audio/')) {
|
||||
return (
|
||||
<Card
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
height: '48px',
|
||||
width: isDialog ? ps?.current?.offsetWidth / 4 : ps?.current?.offsetWidth / 2,
|
||||
p: 0.5,
|
||||
mr: 1,
|
||||
backgroundColor: theme.palette.grey[500],
|
||||
flex: '0 0 auto'
|
||||
}}
|
||||
variant='outlined'
|
||||
>
|
||||
<CardMedia component='audio' sx={{ color: 'transparent' }} controls src={item.data} />
|
||||
<IconButton onClick={() => handleDeletePreview(item)} size='small'>
|
||||
<IconTrash size={20} color='white' />
|
||||
</IconButton>
|
||||
</Card>
|
||||
)
|
||||
} else {
|
||||
return <CardWithDeleteOverlay item={item} customization={customization} onDelete={() => handleDeletePreview(item)} />
|
||||
}
|
||||
}
|
||||
|
||||
const renderFileUploads = (item, index) => {
|
||||
if (item?.mime?.startsWith('image/')) {
|
||||
return (
|
||||
<Card
|
||||
key={index}
|
||||
sx={{
|
||||
p: 0,
|
||||
m: 0,
|
||||
maxWidth: 128,
|
||||
marginRight: '10px',
|
||||
flex: '0 0 auto'
|
||||
}}
|
||||
>
|
||||
<CardMedia component='img' image={item.data} sx={{ height: 64 }} alt={'preview'} style={messageImageStyle} />
|
||||
</Card>
|
||||
)
|
||||
} else if (item?.mime?.startsWith('audio/')) {
|
||||
return (
|
||||
/* eslint-disable jsx-a11y/media-has-caption */
|
||||
<audio controls='controls'>
|
||||
Your browser does not support the <audio> tag.
|
||||
<source src={item.data} type={item.mime} />
|
||||
</audio>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Card
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
height: '48px',
|
||||
width: 'max-content',
|
||||
p: 2,
|
||||
mr: 1,
|
||||
flex: '0 0 auto',
|
||||
backgroundColor: customization.isDarkMode ? 'rgba(0, 0, 0, 0.3)' : 'transparent'
|
||||
}}
|
||||
variant='outlined'
|
||||
>
|
||||
<IconPaperclip size={20} />
|
||||
<span
|
||||
style={{
|
||||
marginLeft: '5px',
|
||||
color: customization.isDarkMode ? 'white' : 'inherit'
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</span>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div onDragEnter={handleDrag}>
|
||||
{isDragActive && (
|
||||
@@ -976,19 +1197,25 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
onDrop={handleDrop}
|
||||
/>
|
||||
)}
|
||||
{isDragActive && getAllowChatFlowUploads.data?.isImageUploadAllowed && (
|
||||
<Box className='drop-overlay'>
|
||||
<Typography variant='h2'>Drop here to upload</Typography>
|
||||
{getAllowChatFlowUploads.data.imgUploadSizeAndTypes.map((allowed) => {
|
||||
return (
|
||||
<>
|
||||
<Typography variant='subtitle1'>{allowed.fileTypes?.join(', ')}</Typography>
|
||||
<Typography variant='subtitle1'>Max Allowed Size: {allowed.maxUploadSize} MB</Typography>
|
||||
</>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
{isDragActive &&
|
||||
(getAllowChatFlowUploads.data?.isImageUploadAllowed || getAllowChatFlowUploads.data?.isFileAllowedForUpload) && (
|
||||
<Box className='drop-overlay'>
|
||||
<Typography variant='h2'>Drop here to upload</Typography>
|
||||
{[
|
||||
...getAllowChatFlowUploads.data.imgUploadSizeAndTypes,
|
||||
...getAllowChatFlowUploads.data.fileUploadSizeAndTypes
|
||||
].map((allowed) => {
|
||||
return (
|
||||
<>
|
||||
<Typography variant='subtitle1'>{allowed.fileTypes?.join(', ')}</Typography>
|
||||
{allowed.maxUploadSize && (
|
||||
<Typography variant='subtitle1'>Max Allowed Size: {allowed.maxUploadSize} MB</Typography>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
<div ref={ps} className={`${isDialog ? 'cloud-dialog' : 'cloud'}`}>
|
||||
<div id='messagelist' className={'messagelist'}>
|
||||
{messages &&
|
||||
@@ -1038,36 +1265,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
}}
|
||||
>
|
||||
{message.fileUploads.map((item, index) => {
|
||||
return (
|
||||
<>
|
||||
{item?.mime?.startsWith('image/') ? (
|
||||
<Card
|
||||
key={index}
|
||||
sx={{
|
||||
p: 0,
|
||||
m: 0,
|
||||
maxWidth: 128,
|
||||
marginRight: '10px',
|
||||
flex: '0 0 auto'
|
||||
}}
|
||||
>
|
||||
<CardMedia
|
||||
component='img'
|
||||
image={item.data}
|
||||
sx={{ height: 64 }}
|
||||
alt={'preview'}
|
||||
style={messageImageStyle}
|
||||
/>
|
||||
</Card>
|
||||
) : (
|
||||
// eslint-disable-next-line jsx-a11y/media-has-caption
|
||||
<audio controls='controls'>
|
||||
Your browser does not support the <audio> tag.
|
||||
<source src={item.data} type={item.mime} />
|
||||
</audio>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
return <>{renderFileUploads(item, index)}</>
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
@@ -1564,45 +1762,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
{previews && previews.length > 0 && (
|
||||
<Box sx={{ width: '100%', mb: 1.5, display: 'flex', alignItems: 'center' }}>
|
||||
{previews.map((item, index) => (
|
||||
<Fragment key={index}>
|
||||
{item.mime.startsWith('image/') ? (
|
||||
<ImageButton
|
||||
focusRipple
|
||||
style={{
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
marginRight: '10px',
|
||||
flex: '0 0 auto'
|
||||
}}
|
||||
onClick={() => handleDeletePreview(item)}
|
||||
>
|
||||
<ImageSrc style={{ backgroundImage: `url(${item.data})` }} />
|
||||
<ImageBackdrop className='MuiImageBackdrop-root' />
|
||||
<ImageMarked className='MuiImageMarked-root'>
|
||||
<IconTrash size={20} color='white' />
|
||||
</ImageMarked>
|
||||
</ImageButton>
|
||||
) : (
|
||||
<Card
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
height: '48px',
|
||||
width: isDialog ? ps?.current?.offsetWidth / 4 : ps?.current?.offsetWidth / 2,
|
||||
p: 0.5,
|
||||
mr: 1,
|
||||
backgroundColor: theme.palette.grey[500],
|
||||
flex: '0 0 auto'
|
||||
}}
|
||||
variant='outlined'
|
||||
>
|
||||
<CardMedia component='audio' sx={{ color: 'transparent' }} controls src={item.data} />
|
||||
<IconButton onClick={() => handleDeletePreview(item)} size='small'>
|
||||
<IconTrash size={20} color='white' />
|
||||
</IconButton>
|
||||
</Card>
|
||||
)}
|
||||
</Fragment>
|
||||
<Fragment key={index}>{previewDisplay(item)}</Fragment>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
@@ -1679,15 +1839,62 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
multiline={true}
|
||||
maxRows={isDialog ? 7 : 2}
|
||||
startAdornment={
|
||||
isChatFlowAvailableForUploads && (
|
||||
<InputAdornment position='start' sx={{ pl: 2 }}>
|
||||
<IconButton onClick={handleUploadClick} type='button' disabled={getInputDisabled()} edge='start'>
|
||||
<IconPhotoPlus
|
||||
color={getInputDisabled() ? '#9e9e9e' : customization.isDarkMode ? 'white' : '#1e88e5'}
|
||||
/>
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
<>
|
||||
{isChatFlowAvailableForImageUploads && !isChatFlowAvailableForFileUploads && (
|
||||
<InputAdornment position='start' sx={{ ml: 2 }}>
|
||||
<IconButton
|
||||
onClick={handleImageUploadClick}
|
||||
type='button'
|
||||
disabled={getInputDisabled()}
|
||||
edge='start'
|
||||
>
|
||||
<IconPhotoPlus
|
||||
color={getInputDisabled() ? '#9e9e9e' : customization.isDarkMode ? 'white' : '#1e88e5'}
|
||||
/>
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)}
|
||||
{!isChatFlowAvailableForImageUploads && isChatFlowAvailableForFileUploads && (
|
||||
<InputAdornment position='start' sx={{ ml: 2 }}>
|
||||
<IconButton
|
||||
onClick={handleFileUploadClick}
|
||||
type='button'
|
||||
disabled={getInputDisabled()}
|
||||
edge='start'
|
||||
>
|
||||
<IconPaperclip
|
||||
color={getInputDisabled() ? '#9e9e9e' : customization.isDarkMode ? 'white' : '#1e88e5'}
|
||||
/>
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)}
|
||||
{isChatFlowAvailableForImageUploads && isChatFlowAvailableForFileUploads && (
|
||||
<InputAdornment position='start' sx={{ ml: 2 }}>
|
||||
<IconButton
|
||||
onClick={handleImageUploadClick}
|
||||
type='button'
|
||||
disabled={getInputDisabled()}
|
||||
edge='start'
|
||||
>
|
||||
<IconPhotoPlus
|
||||
color={getInputDisabled() ? '#9e9e9e' : customization.isDarkMode ? 'white' : '#1e88e5'}
|
||||
/>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
sx={{ ml: 0 }}
|
||||
onClick={handleFileUploadClick}
|
||||
type='button'
|
||||
disabled={getInputDisabled()}
|
||||
edge='start'
|
||||
>
|
||||
<IconPaperclip
|
||||
color={getInputDisabled() ? '#9e9e9e' : customization.isDarkMode ? 'white' : '#1e88e5'}
|
||||
/>
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)}
|
||||
{!isChatFlowAvailableForImageUploads && !isChatFlowAvailableForFileUploads && <Box sx={{ pl: 1 }} />}
|
||||
</>
|
||||
}
|
||||
endAdornment={
|
||||
<>
|
||||
@@ -1707,7 +1914,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
</InputAdornment>
|
||||
)}
|
||||
{!isAgentCanvas && (
|
||||
<InputAdornment position='end' sx={{ padding: '15px' }}>
|
||||
<InputAdornment position='end' sx={{ paddingRight: '15px' }}>
|
||||
<IconButton type='submit' disabled={getInputDisabled()} edge='end'>
|
||||
{loading ? (
|
||||
<div>
|
||||
@@ -1727,7 +1934,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
{isAgentCanvas && (
|
||||
<>
|
||||
{!loading && (
|
||||
<InputAdornment position='end' sx={{ padding: '15px' }}>
|
||||
<InputAdornment position='end' sx={{ paddingRight: '15px' }}>
|
||||
<IconButton type='submit' disabled={getInputDisabled()} edge='end'>
|
||||
<IconSend
|
||||
color={
|
||||
@@ -1765,8 +1972,25 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
|
||||
</>
|
||||
}
|
||||
/>
|
||||
{isChatFlowAvailableForUploads && (
|
||||
<input style={{ display: 'none' }} multiple ref={fileUploadRef} type='file' onChange={handleFileChange} />
|
||||
{isChatFlowAvailableForImageUploads && (
|
||||
<input
|
||||
style={{ display: 'none' }}
|
||||
multiple
|
||||
ref={imgUploadRef}
|
||||
type='file'
|
||||
onChange={handleFileChange}
|
||||
accept={imageUploadAllowedTypes || '*'}
|
||||
/>
|
||||
)}
|
||||
{isChatFlowAvailableForFileUploads && (
|
||||
<input
|
||||
style={{ display: 'none' }}
|
||||
multiple
|
||||
ref={fileUploadRef}
|
||||
type='file'
|
||||
onChange={handleFileChange}
|
||||
accept={fileUploadAllowedTypes.includes('*') ? '*' : fileUploadAllowedTypes || '*'}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user