mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-29 15:01:11 +03:00
UI touchup
This commit is contained in:
@@ -214,3 +214,8 @@ export interface ICredentialReqBody {
|
|||||||
export interface ICredentialReturnResponse extends ICredential {
|
export interface ICredentialReturnResponse extends ICredential {
|
||||||
plainDataObj: ICredentialDataDecrypted
|
plainDataObj: ICredentialDataDecrypted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IUploadFileSizeAndTypes {
|
||||||
|
fileTypes: string[]
|
||||||
|
maxUploadSize: number
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ import {
|
|||||||
chatType,
|
chatType,
|
||||||
IChatMessage,
|
IChatMessage,
|
||||||
IDepthQueue,
|
IDepthQueue,
|
||||||
INodeDirectedGraph
|
INodeDirectedGraph,
|
||||||
|
IUploadFileSizeAndTypes
|
||||||
} from './Interface'
|
} from './Interface'
|
||||||
import {
|
import {
|
||||||
getNodeModulesPackagePath,
|
getNodeModulesPackagePath,
|
||||||
@@ -57,7 +58,7 @@ import { Tool } from './database/entities/Tool'
|
|||||||
import { Assistant } from './database/entities/Assistant'
|
import { Assistant } from './database/entities/Assistant'
|
||||||
import { ChatflowPool } from './ChatflowPool'
|
import { ChatflowPool } from './ChatflowPool'
|
||||||
import { CachePool } from './CachePool'
|
import { CachePool } from './CachePool'
|
||||||
import { ICommonObject, IMessage, INodeOptionsValue, handleEscapeCharacters } from 'flowise-components'
|
import { ICommonObject, IMessage, INodeOptionsValue, INodeParams, handleEscapeCharacters } from 'flowise-components'
|
||||||
import { createRateLimiter, getRateLimiter, initializeRateLimiter } from './utils/rateLimit'
|
import { createRateLimiter, getRateLimiter, initializeRateLimiter } from './utils/rateLimit'
|
||||||
import { addAPIKey, compareKeys, deleteAPIKey, getApiKey, getAPIKeys, updateAPIKey } from './utils/apiKey'
|
import { addAPIKey, compareKeys, deleteAPIKey, getApiKey, getAPIKeys, updateAPIKey } from './utils/apiKey'
|
||||||
import { sanitizeMiddleware } from './utils/XSS'
|
import { sanitizeMiddleware } from './utils/XSS'
|
||||||
@@ -147,7 +148,9 @@ export class App {
|
|||||||
'/api/v1/node-icon/',
|
'/api/v1/node-icon/',
|
||||||
'/api/v1/components-credentials-icon/',
|
'/api/v1/components-credentials-icon/',
|
||||||
'/api/v1/chatflows-streaming',
|
'/api/v1/chatflows-streaming',
|
||||||
|
'/api/v1/chatflows-uploads',
|
||||||
'/api/v1/openai-assistants-file',
|
'/api/v1/openai-assistants-file',
|
||||||
|
'/api/v1/get-upload-file',
|
||||||
'/api/v1/ip'
|
'/api/v1/ip'
|
||||||
]
|
]
|
||||||
this.app.use((req, res, next) => {
|
this.app.use((req, res, next) => {
|
||||||
@@ -464,8 +467,45 @@ export class App {
|
|||||||
})
|
})
|
||||||
if (!chatflow) return res.status(404).send(`Chatflow ${req.params.id} not found`)
|
if (!chatflow) return res.status(404).send(`Chatflow ${req.params.id} not found`)
|
||||||
|
|
||||||
const obj = this.shouldAllowUploads(chatflow)
|
const uploadAllowedNodes = ['OpenAIMultiModalChain', 'OpenAIWhisper']
|
||||||
return res.json(obj)
|
|
||||||
|
try {
|
||||||
|
const flowObj = JSON.parse(chatflow.flowData)
|
||||||
|
let isUploadAllowed = false
|
||||||
|
const allowances: IUploadFileSizeAndTypes[] = []
|
||||||
|
|
||||||
|
flowObj.nodes.forEach((node: IReactFlowNode) => {
|
||||||
|
if (uploadAllowedNodes.indexOf(node.data.type) > -1) {
|
||||||
|
logger.debug(`[server]: Found Eligible Node ${node.data.type}, Allowing Uploads.`)
|
||||||
|
isUploadAllowed = true
|
||||||
|
|
||||||
|
const allowance: IUploadFileSizeAndTypes = {
|
||||||
|
fileTypes: [],
|
||||||
|
maxUploadSize: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
node.data.inputParams.map((param: INodeParams) => {
|
||||||
|
if (param.name === 'allowedUploadTypes') {
|
||||||
|
allowance.fileTypes = (param.default as string).split(';')
|
||||||
|
}
|
||||||
|
if (param.name === 'maxUploadSize') {
|
||||||
|
allowance.maxUploadSize = parseInt(param.default ? (param.default as string) : '0')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (allowance.fileTypes && allowance.maxUploadSize) {
|
||||||
|
allowances.push(allowance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
isUploadAllowed,
|
||||||
|
uploadFileSizeAndTypes: allowances
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
return res.status(500).send(e)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
@@ -1058,10 +1098,14 @@ export class App {
|
|||||||
return res.status(500).send(`Invalid file path`)
|
return res.status(500).send(`Invalid file path`)
|
||||||
}
|
}
|
||||||
const filePath = path.join(getUserHome(), '.flowise', 'gptvision', req.query.chatId as string, req.params.id)
|
const filePath = path.join(getUserHome(), '.flowise', 'gptvision', req.query.chatId as string, req.params.id)
|
||||||
console.log(filePath)
|
//raise error if file path is not absolute
|
||||||
if (!path.isAbsolute(filePath) || !fs.existsSync(filePath)) {
|
if (!path.isAbsolute(filePath)) return res.status(500).send(`Invalid file path`)
|
||||||
|
//raise error if file path contains '..'
|
||||||
|
if (filePath.includes('..')) return res.status(500).send(`Invalid file path`)
|
||||||
|
//only return from the .flowise gptvision folder
|
||||||
|
if (!(filePath.includes('.flowise') && filePath.includes('gptvision') && filePath.includes(req.query.chatId as string)))
|
||||||
return res.status(500).send(`Invalid file path`)
|
return res.status(500).send(`Invalid file path`)
|
||||||
}
|
res.setHeader('Content-Disposition', 'attachment; filename=' + path.basename(filePath))
|
||||||
streamFileToUser(res, filePath)
|
streamFileToUser(res, filePath)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1350,35 +1394,6 @@ export class App {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private uploadAllowedNodes = ['OpenAIMultiModalChain', 'OpenAIWhisper']
|
|
||||||
private shouldAllowUploads(result: ChatFlow): any {
|
|
||||||
const flowObj = JSON.parse(result.flowData)
|
|
||||||
let allowUploads = false
|
|
||||||
const allowances: any = []
|
|
||||||
flowObj.nodes.forEach((node: IReactFlowNode) => {
|
|
||||||
if (this.uploadAllowedNodes.indexOf(node.data.type) > -1) {
|
|
||||||
logger.debug(`[server]: Found Eligible Node ${node.data.type}, Allowing Uploads.`)
|
|
||||||
allowUploads = true
|
|
||||||
const allowance: any = {}
|
|
||||||
node.data.inputParams.map((param: any) => {
|
|
||||||
if (param.name === 'allowedUploadTypes') {
|
|
||||||
allowance.allowedTypes = param.default.split(';')
|
|
||||||
}
|
|
||||||
if (param.name === 'maxUploadSize') {
|
|
||||||
allowance.maxUploadSize = parseInt(param.default ? param.default : '0')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (allowance.allowedTypes && allowance.maxUploadSize) {
|
|
||||||
allowances.push(allowance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
allowUploads,
|
|
||||||
allowed: allowances
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate API Key
|
* Validate API Key
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const updateChatflow = (id, body) => client.put(`/chatflows/${id}`, body)
|
|||||||
const deleteChatflow = (id) => client.delete(`/chatflows/${id}`)
|
const deleteChatflow = (id) => client.delete(`/chatflows/${id}`)
|
||||||
|
|
||||||
const getIsChatflowStreaming = (id) => client.get(`/chatflows-streaming/${id}`)
|
const getIsChatflowStreaming = (id) => client.get(`/chatflows-streaming/${id}`)
|
||||||
|
|
||||||
const getAllowChatflowUploads = (id) => client.get(`/chatflows-uploads/${id}`)
|
const getAllowChatflowUploads = (id) => client.get(`/chatflows-uploads/${id}`)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { styled } from '@mui/material/styles'
|
||||||
|
import ButtonBase from '@mui/material/ButtonBase'
|
||||||
|
|
||||||
|
export const ImageButton = styled(ButtonBase)(({ theme }) => ({
|
||||||
|
position: 'relative',
|
||||||
|
height: 200,
|
||||||
|
borderRadius: '10px',
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
width: '100% !important', // Overrides inline-style
|
||||||
|
height: 100
|
||||||
|
},
|
||||||
|
'&:hover, &.Mui-focusVisible': {
|
||||||
|
zIndex: 1,
|
||||||
|
'& .MuiImageBackdrop-root': {
|
||||||
|
opacity: 0.4
|
||||||
|
},
|
||||||
|
'& .MuiImageMarked-root': {
|
||||||
|
opacity: 1
|
||||||
|
},
|
||||||
|
'& .MuiTypography-root': {
|
||||||
|
border: '4px solid currentColor'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const ImageSrc = styled('span')({
|
||||||
|
position: 'absolute',
|
||||||
|
borderRadius: '10px',
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
backgroundSize: 'cover',
|
||||||
|
backgroundPosition: 'center 40%'
|
||||||
|
})
|
||||||
|
|
||||||
|
export const ImageBackdrop = styled('span')(({ theme }) => ({
|
||||||
|
position: 'absolute',
|
||||||
|
borderRadius: '10px',
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
backgroundColor: theme.palette.common.black,
|
||||||
|
opacity: 0.1,
|
||||||
|
transition: theme.transitions.create('opacity')
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const ImageMarked = styled('span')(() => ({
|
||||||
|
height: 25,
|
||||||
|
width: 25,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 'auto',
|
||||||
|
left: 'auto',
|
||||||
|
opacity: 0
|
||||||
|
}))
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
.button-container {
|
.button-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import PropTypes from 'prop-types'
|
|||||||
import { Chip } from '@mui/material'
|
import { Chip } from '@mui/material'
|
||||||
import './StarterPromptsCard.css'
|
import './StarterPromptsCard.css'
|
||||||
|
|
||||||
const StarterPromptsCard = ({ isGrid, starterPrompts, onPromptClick }) => {
|
const StarterPromptsCard = ({ isGrid, starterPrompts, sx, onPromptClick }) => {
|
||||||
return (
|
return (
|
||||||
<Box className={'button-container'} sx={{ maxWidth: isGrid ? 'inherit' : '400px', m: 1 }}>
|
<Box className={'button-container'} sx={{ bottom: 0, maxWidth: isGrid ? 'inherit' : '400px', m: 1, ...sx }}>
|
||||||
{starterPrompts.map((sp, index) => (
|
{starterPrompts.map((sp, index) => (
|
||||||
<Chip label={sp.prompt} className={'button'} key={index} onClick={(e) => onPromptClick(sp.prompt, e)} />
|
<Chip label={sp.prompt} className={'button'} key={index} onClick={(e) => onPromptClick(sp.prompt, e)} />
|
||||||
))}
|
))}
|
||||||
@@ -15,7 +15,8 @@ const StarterPromptsCard = ({ isGrid, starterPrompts, onPromptClick }) => {
|
|||||||
|
|
||||||
StarterPromptsCard.propTypes = {
|
StarterPromptsCard.propTypes = {
|
||||||
isGrid: PropTypes.bool,
|
isGrid: PropTypes.bool,
|
||||||
starterPrompts: PropTypes.arrayOf(PropTypes.string),
|
starterPrompts: PropTypes.array,
|
||||||
|
sx: PropTypes.object,
|
||||||
onPromptClick: PropTypes.func
|
onPromptClick: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -146,6 +146,16 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch; /* For momentum scroll on mobile devices */
|
||||||
|
scrollbar-width: none; /* For Firefox */
|
||||||
|
}
|
||||||
|
|
||||||
.file-drop-field {
|
.file-drop-field {
|
||||||
position: relative; /* Needed to position the icon correctly */
|
position: relative; /* Needed to position the icon correctly */
|
||||||
/* Other styling for the field */
|
/* Other styling for the field */
|
||||||
@@ -162,26 +172,6 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
z-index: 10; /* Ensure it's above other content */
|
z-index: 2000; /* Ensure it's above other content */
|
||||||
border: 2px dashed #0094ff; /* Example style */
|
border: 2px dashed #0094ff; /* Example style */
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-container {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-card {
|
|
||||||
border: 2px solid #E7EDF3;
|
|
||||||
border-radius: 16%;
|
|
||||||
transition: 0.4s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-card&:hover {
|
|
||||||
border-color: #5B9FED;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.button {
|
|
||||||
flex: 0 0 auto; /* Don't grow, don't shrink, base width on content */
|
|
||||||
margin: 5px; /* Adjust as needed for spacing between buttons */
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useState, useRef, useEffect, useCallback } from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import socketIOClient from 'socket.io-client'
|
import socketIOClient from 'socket.io-client'
|
||||||
@@ -8,30 +8,33 @@ import rehypeRaw from 'rehype-raw'
|
|||||||
import remarkGfm from 'remark-gfm'
|
import remarkGfm from 'remark-gfm'
|
||||||
import remarkMath from 'remark-math'
|
import remarkMath from 'remark-math'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import audioUploadSVG from 'assets/images/wave-sound.jpg'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
CardActions,
|
|
||||||
CardMedia,
|
CardMedia,
|
||||||
Chip,
|
Chip,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
Divider,
|
Divider,
|
||||||
Grid,
|
|
||||||
IconButton,
|
IconButton,
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
OutlinedInput,
|
OutlinedInput,
|
||||||
Typography
|
Typography
|
||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
import { useTheme } from '@mui/material/styles'
|
import { useTheme } from '@mui/material/styles'
|
||||||
import { IconDownload, IconSend, IconMicrophone, IconPhotoPlus, IconCircleDot } from '@tabler/icons'
|
import { IconDownload, IconSend, IconMicrophone, IconPhotoPlus, IconCircleDot, IconTrash } from '@tabler/icons'
|
||||||
|
import robotPNG from 'assets/images/robot.png'
|
||||||
|
import userPNG from 'assets/images/account.png'
|
||||||
|
import audioUploadSVG from 'assets/images/wave-sound.jpg'
|
||||||
|
|
||||||
// project import
|
// project import
|
||||||
import { CodeBlock } from 'ui-component/markdown/CodeBlock'
|
import { CodeBlock } from 'ui-component/markdown/CodeBlock'
|
||||||
import { MemoizedReactMarkdown } from 'ui-component/markdown/MemoizedReactMarkdown'
|
import { MemoizedReactMarkdown } from 'ui-component/markdown/MemoizedReactMarkdown'
|
||||||
import SourceDocDialog from 'ui-component/dialog/SourceDocDialog'
|
import SourceDocDialog from 'ui-component/dialog/SourceDocDialog'
|
||||||
|
import StarterPromptsCard from 'ui-component/cards/StarterPromptsCard'
|
||||||
|
import { cancelAudioRecording, startAudioRecording, stopAudioRecording } from './audio-recording'
|
||||||
|
import { ImageButton, ImageSrc, ImageBackdrop, ImageMarked } from 'ui-component/button/ImageButton'
|
||||||
import './ChatMessage.css'
|
import './ChatMessage.css'
|
||||||
import './audio-recording.css'
|
import './audio-recording.css'
|
||||||
|
|
||||||
@@ -46,12 +49,14 @@ import useApi from 'hooks/useApi'
|
|||||||
// Const
|
// Const
|
||||||
import { baseURL, maxScroll } from 'store/constant'
|
import { baseURL, maxScroll } from 'store/constant'
|
||||||
|
|
||||||
import robotPNG from 'assets/images/robot.png'
|
// Utils
|
||||||
import userPNG from 'assets/images/account.png'
|
|
||||||
import StarterPromptsCard from '../../ui-component/cards/StarterPromptsCard'
|
|
||||||
import { isValidURL, removeDuplicateURL, setLocalStorageChatflow } from 'utils/genericHelper'
|
import { isValidURL, removeDuplicateURL, setLocalStorageChatflow } from 'utils/genericHelper'
|
||||||
import DeleteIcon from '@mui/icons-material/Delete'
|
|
||||||
import { cancelAudioRecording, startAudioRecording, stopAudioRecording } from './audio-recording'
|
const messageImageStyle = {
|
||||||
|
width: '128px',
|
||||||
|
height: '128px',
|
||||||
|
objectFit: 'cover'
|
||||||
|
}
|
||||||
|
|
||||||
export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
@@ -76,13 +81,13 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
const inputRef = useRef(null)
|
const inputRef = useRef(null)
|
||||||
const getChatmessageApi = useApi(chatmessageApi.getInternalChatmessageFromChatflow)
|
const getChatmessageApi = useApi(chatmessageApi.getInternalChatmessageFromChatflow)
|
||||||
const getIsChatflowStreamingApi = useApi(chatflowsApi.getIsChatflowStreaming)
|
const getIsChatflowStreamingApi = useApi(chatflowsApi.getIsChatflowStreaming)
|
||||||
|
const getAllowChatFlowUploads = useApi(chatflowsApi.getAllowChatflowUploads)
|
||||||
const getChatflowConfig = useApi(chatflowsApi.getSpecificChatflow)
|
const getChatflowConfig = useApi(chatflowsApi.getSpecificChatflow)
|
||||||
|
|
||||||
const [starterPrompts, setStarterPrompts] = useState([])
|
const [starterPrompts, setStarterPrompts] = useState([])
|
||||||
|
|
||||||
// drag & drop and file input
|
// drag & drop and file input
|
||||||
const fileUploadRef = useRef(null)
|
const fileUploadRef = useRef(null)
|
||||||
const getAllowChatFlowUploads = useApi(chatflowsApi.getAllowChatflowUploads)
|
|
||||||
const [isChatFlowAvailableForUploads, setIsChatFlowAvailableForUploads] = useState(false)
|
const [isChatFlowAvailableForUploads, setIsChatFlowAvailableForUploads] = useState(false)
|
||||||
const [previews, setPreviews] = useState([])
|
const [previews, setPreviews] = useState([])
|
||||||
const [isDragOver, setIsDragOver] = useState(false)
|
const [isDragOver, setIsDragOver] = useState(false)
|
||||||
@@ -91,20 +96,17 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
const [isRecording, setIsRecording] = useState(false)
|
const [isRecording, setIsRecording] = useState(false)
|
||||||
const [recordingNotSupported, setRecordingNotSupported] = useState(false)
|
const [recordingNotSupported, setRecordingNotSupported] = useState(false)
|
||||||
|
|
||||||
const handleDragOver = (e) => {
|
|
||||||
if (!isChatFlowAvailableForUploads) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
const isFileAllowedForUpload = (file) => {
|
const isFileAllowedForUpload = (file) => {
|
||||||
const constraints = getAllowChatFlowUploads.data
|
const constraints = getAllowChatFlowUploads.data
|
||||||
|
/**
|
||||||
|
* {isUploadAllowed: boolean, uploadFileSizeAndTypes: Array<{ fileTypes: string[], maxUploadSize: number }>}
|
||||||
|
*/
|
||||||
let acceptFile = false
|
let acceptFile = false
|
||||||
if (constraints.allowUploads) {
|
if (constraints.isUploadAllowed) {
|
||||||
const fileType = file.type
|
const fileType = file.type
|
||||||
const sizeInMB = file.size / 1024 / 1024
|
const sizeInMB = file.size / 1024 / 1024
|
||||||
constraints.allowed.map((allowed) => {
|
constraints.uploadFileSizeAndTypes.map((allowed) => {
|
||||||
if (allowed.allowedTypes.includes(fileType) && sizeInMB <= allowed.maxUploadSize) {
|
if (allowed.fileTypes.includes(fileType) && sizeInMB <= allowed.maxUploadSize) {
|
||||||
acceptFile = true
|
acceptFile = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -114,11 +116,13 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
}
|
}
|
||||||
return acceptFile
|
return acceptFile
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDrop = async (e) => {
|
const handleDrop = async (e) => {
|
||||||
if (!isChatFlowAvailableForUploads) {
|
if (!isChatFlowAvailableForUploads) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
setIsDragOver(false)
|
setIsDragOver(false)
|
||||||
let files = []
|
let files = []
|
||||||
if (e.dataTransfer.files.length > 0) {
|
if (e.dataTransfer.files.length > 0) {
|
||||||
@@ -156,10 +160,8 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
|
|
||||||
const newFiles = await Promise.all(files)
|
const newFiles = await Promise.all(files)
|
||||||
setPreviews((prevPreviews) => [...prevPreviews, ...newFiles])
|
setPreviews((prevPreviews) => [...prevPreviews, ...newFiles])
|
||||||
// if (newFiles.length > 0) {
|
|
||||||
// document.getElementById('messagelist').style.height = '80%'
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.dataTransfer.items) {
|
if (e.dataTransfer.items) {
|
||||||
for (const item of e.dataTransfer.items) {
|
for (const item of e.dataTransfer.items) {
|
||||||
if (item.kind === 'string' && item.type.match('^text/uri-list')) {
|
if (item.kind === 'string' && item.type.match('^text/uri-list')) {
|
||||||
@@ -191,6 +193,7 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleFileChange = async (event) => {
|
const handleFileChange = async (event) => {
|
||||||
const fileObj = event.target.files && event.target.files[0]
|
const fileObj = event.target.files && event.target.files[0]
|
||||||
if (!fileObj) {
|
if (!fileObj) {
|
||||||
@@ -247,9 +250,15 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDragOver = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
const handleDragEnter = (e) => {
|
const handleDragEnter = (e) => {
|
||||||
if (isChatFlowAvailableForUploads) {
|
if (isChatFlowAvailableForUploads) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
setIsDragOver(true)
|
setIsDragOver(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,34 +266,27 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
const handleDragLeave = (e) => {
|
const handleDragLeave = (e) => {
|
||||||
if (isChatFlowAvailableForUploads) {
|
if (isChatFlowAvailableForUploads) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
if (e.originalEvent?.pageX !== 0 || e.originalEvent?.pageY !== 0) {
|
if (e.originalEvent?.pageX !== 0 || e.originalEvent?.pageY !== 0) {
|
||||||
|
setIsDragOver(false)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
setIsDragOver(false) // Set the drag over state to false when the drag leaves
|
setIsDragOver(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDeletePreview = (itemToDelete) => {
|
const handleDeletePreview = (itemToDelete) => {
|
||||||
if (itemToDelete.type === 'file') {
|
if (itemToDelete.type === 'file') {
|
||||||
URL.revokeObjectURL(itemToDelete.preview) // Clean up for file
|
URL.revokeObjectURL(itemToDelete.preview) // Clean up for file
|
||||||
}
|
}
|
||||||
setPreviews(previews.filter((item) => item !== itemToDelete))
|
setPreviews(previews.filter((item) => item !== itemToDelete))
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUploadClick = () => {
|
const handleUploadClick = () => {
|
||||||
// 👇️ open file input box on click of another element
|
// 👇️ open file input box on click of another element
|
||||||
fileUploadRef.current.click()
|
fileUploadRef.current.click()
|
||||||
}
|
}
|
||||||
|
|
||||||
const previewStyle = {
|
|
||||||
width: '128px',
|
|
||||||
height: '64px',
|
|
||||||
objectFit: 'fit' // This makes the image cover the area, cropping it if necessary
|
|
||||||
}
|
|
||||||
const messageImageStyle = {
|
|
||||||
width: '128px',
|
|
||||||
height: '128px',
|
|
||||||
objectFit: 'cover' // This makes the image cover the area, cropping it if necessary
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearPreviews = () => {
|
const clearPreviews = () => {
|
||||||
// Revoke the data uris to avoid memory leaks
|
// Revoke the data uris to avoid memory leaks
|
||||||
previews.forEach((file) => URL.revokeObjectURL(file.preview))
|
previews.forEach((file) => URL.revokeObjectURL(file.preview))
|
||||||
@@ -295,11 +297,13 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
setIsRecording(true)
|
setIsRecording(true)
|
||||||
startAudioRecording(setIsRecording, setRecordingNotSupported)
|
startAudioRecording(setIsRecording, setRecordingNotSupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onRecordingCancelled = () => {
|
const onRecordingCancelled = () => {
|
||||||
cancelAudioRecording()
|
cancelAudioRecording()
|
||||||
setIsRecording(false)
|
setIsRecording(false)
|
||||||
setRecordingNotSupported(false)
|
setRecordingNotSupported(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onRecordingStopped = () => {
|
const onRecordingStopped = () => {
|
||||||
stopAudioRecording(addRecordingToPreviews)
|
stopAudioRecording(addRecordingToPreviews)
|
||||||
setIsRecording(false)
|
setIsRecording(false)
|
||||||
@@ -505,7 +509,7 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
// Get chatflow uploads capability
|
// Get chatflow uploads capability
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (getAllowChatFlowUploads.data) {
|
if (getAllowChatFlowUploads.data) {
|
||||||
setIsChatFlowAvailableForUploads(getAllowChatFlowUploads.data?.allowUploads ?? false)
|
setIsChatFlowAvailableForUploads(getAllowChatFlowUploads.data?.isUploadAllowed ?? false)
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [getAllowChatFlowUploads.data])
|
}, [getAllowChatFlowUploads.data])
|
||||||
@@ -544,12 +548,18 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let socket
|
let socket
|
||||||
if (open && chatflowid) {
|
if (open && chatflowid) {
|
||||||
|
// API request
|
||||||
getChatmessageApi.request(chatflowid)
|
getChatmessageApi.request(chatflowid)
|
||||||
getIsChatflowStreamingApi.request(chatflowid)
|
getIsChatflowStreamingApi.request(chatflowid)
|
||||||
getAllowChatFlowUploads.request(chatflowid)
|
getAllowChatFlowUploads.request(chatflowid)
|
||||||
getChatflowConfig.request(chatflowid)
|
getChatflowConfig.request(chatflowid)
|
||||||
|
|
||||||
|
// Scroll to bottom
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
|
|
||||||
setIsRecording(false)
|
setIsRecording(false)
|
||||||
|
|
||||||
|
// SocketIO
|
||||||
socket = socketIOClient(baseURL)
|
socket = socketIOClient(baseURL)
|
||||||
|
|
||||||
socket.on('connect', () => {
|
socket.on('connect', () => {
|
||||||
@@ -584,20 +594,14 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
}, [open, chatflowid])
|
}, [open, chatflowid])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
onDragOver={handleDragOver}
|
{isDragOver && getAllowChatFlowUploads.data?.isUploadAllowed && (
|
||||||
onDragEnter={handleDragEnter}
|
|
||||||
onDragLeave={handleDragLeave}
|
|
||||||
onDrop={handleDrop}
|
|
||||||
className={`file-drop-field`}
|
|
||||||
>
|
|
||||||
{isDragOver && getAllowChatFlowUploads.data?.allowUploads && (
|
|
||||||
<Box className='drop-overlay'>
|
<Box className='drop-overlay'>
|
||||||
<Typography variant='h2'>Drop here to upload</Typography>
|
<Typography variant='h2'>Drop here to upload</Typography>
|
||||||
{getAllowChatFlowUploads.data.allowed.map((allowed) => {
|
{getAllowChatFlowUploads.data.uploadFileSizeAndTypes.map((allowed) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography variant='subtitle1'>{allowed.allowedTypes?.join(', ')}</Typography>
|
<Typography variant='subtitle1'>{allowed.fileTypes?.join(', ')}</Typography>
|
||||||
<Typography variant='subtitle1'>Max Allowed Size: {allowed.maxUploadSize} MB</Typography>
|
<Typography variant='subtitle1'>Max Allowed Size: {allowed.maxUploadSize} MB</Typography>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
@@ -639,7 +643,13 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<div className={`${isDialog ? 'cloud-dialog' : 'cloud'}`}>
|
<div
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDragEnter={handleDragEnter}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
className={`${isDialog ? 'cloud-dialog' : 'cloud'}`}
|
||||||
|
>
|
||||||
<div ref={ps} id='messagelist' className={'messagelist'}>
|
<div ref={ps} id='messagelist' className={'messagelist'}>
|
||||||
{messages &&
|
{messages &&
|
||||||
messages.map((message, index) => {
|
messages.map((message, index) => {
|
||||||
@@ -687,6 +697,42 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{message.fileUploads && message.fileUploads.length > 0 && (
|
||||||
|
<div style={{ display: 'flex', flexWrap: 'wrap', flexDirection: 'row', width: '100%' }}>
|
||||||
|
{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>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className='markdownanswer'>
|
<div className='markdownanswer'>
|
||||||
{/* Messages are being rendered in Markdown format */}
|
{/* Messages are being rendered in Markdown format */}
|
||||||
<MemoizedReactMarkdown
|
<MemoizedReactMarkdown
|
||||||
@@ -732,30 +778,6 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{message.fileUploads &&
|
|
||||||
message.fileUploads.map((item, index) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{item.mime.startsWith('image/') ? (
|
|
||||||
<Card key={index} sx={{ maxWidth: 128, margin: 5 }}>
|
|
||||||
<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>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
{message.sourceDocuments && (
|
{message.sourceDocuments && (
|
||||||
<div style={{ display: 'block', flexDirection: 'row', width: '100%' }}>
|
<div style={{ display: 'block', flexDirection: 'row', width: '100%' }}>
|
||||||
{removeDuplicateURL(message).map((source, index) => {
|
{removeDuplicateURL(message).map((source, index) => {
|
||||||
@@ -796,48 +818,72 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
|
|
||||||
<div style={{ position: 'relative' }}>
|
<div style={{ position: 'relative' }}>
|
||||||
{messages && messages.length === 1 && (
|
{messages && messages.length === 1 && (
|
||||||
<StarterPromptsCard starterPrompts={starterPrompts || []} onPromptClick={handlePromptClick} isGrid={isDialog} />
|
<StarterPromptsCard
|
||||||
|
sx={{ bottom: previews && previews.length > 0 ? 70 : 0 }}
|
||||||
|
starterPrompts={starterPrompts || []}
|
||||||
|
onPromptClick={handlePromptClick}
|
||||||
|
isGrid={isDialog}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
<Divider />
|
<Divider />
|
||||||
</div>
|
</div>
|
||||||
<Divider />
|
|
||||||
<div>
|
<div style={{ position: 'relative' }}>
|
||||||
{previews && previews.length > 0 && (
|
{previews && previews.length > 0 && (
|
||||||
<div className='flex-col flex'>
|
<Box className={'preview'} sx={{ maxWidth: isDialog ? 'inherit' : '400px', m: 1, pb: 0.5 }}>
|
||||||
<div className='flex justify-between items-center h-[80px] px-1 py-1'>
|
|
||||||
<Grid container spacing={2} sx={{ mt: '2px' }}>
|
|
||||||
{previews.map((item, index) => (
|
{previews.map((item, index) => (
|
||||||
<>
|
<>
|
||||||
{item.mime.startsWith('image/') ? (
|
{item.mime.startsWith('image/') ? (
|
||||||
<Grid item xs={6} sm={3} md={3} lg={3} key={index}>
|
<ImageButton
|
||||||
<Card key={index} sx={{ maxWidth: 128 }} variant='outlined'>
|
focusRipple
|
||||||
<CardMedia style={previewStyle} component='img' image={item.data} alt={'preview'} />
|
key={index}
|
||||||
<CardActions className='center'>
|
style={{
|
||||||
<IconButton onClick={() => handleDeletePreview(item)} size='small'>
|
width: '48px',
|
||||||
<DeleteIcon />
|
height: '48px',
|
||||||
</IconButton>
|
marginRight: '10px',
|
||||||
</CardActions>
|
flex: '0 0 auto'
|
||||||
</Card>
|
}}
|
||||||
</Grid>
|
onClick={() => handleDeletePreview(item)}
|
||||||
|
>
|
||||||
|
<ImageSrc style={{ backgroundImage: `url(${item.data})` }} />
|
||||||
|
<ImageBackdrop className='MuiImageBackdrop-root' />
|
||||||
|
<ImageMarked className='MuiImageMarked-root'>
|
||||||
|
<IconTrash size={20} color='white' />
|
||||||
|
</ImageMarked>
|
||||||
|
</ImageButton>
|
||||||
) : (
|
) : (
|
||||||
<Grid item xs={12} sm={6} md={6} lg={6} key={index}>
|
<Card
|
||||||
<Card key={index} variant='outlined'>
|
sx={{
|
||||||
<CardMedia component='audio' sx={{ h: 68 }} controls src={item.data} />
|
display: 'flex',
|
||||||
<CardActions className='center'>
|
alignItems: 'center',
|
||||||
|
height: '48px',
|
||||||
|
width: isDialog ? ps?.current?.offsetWidth / 4 : ps?.current?.offsetWidth / 2,
|
||||||
|
pl: 0.5,
|
||||||
|
mr: 1,
|
||||||
|
backgroundColor: theme.palette.grey[500],
|
||||||
|
flex: '0 0 auto'
|
||||||
|
}}
|
||||||
|
key={index}
|
||||||
|
variant='outlined'
|
||||||
|
>
|
||||||
|
<CardMedia
|
||||||
|
component='audio'
|
||||||
|
sx={{ height: '40px', color: 'transparent' }}
|
||||||
|
controls
|
||||||
|
src={item.data}
|
||||||
|
/>
|
||||||
<IconButton onClick={() => handleDeletePreview(item)} size='small'>
|
<IconButton onClick={() => handleDeletePreview(item)} size='small'>
|
||||||
<DeleteIcon />
|
<IconTrash size={20} color='white' />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</CardActions>
|
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Box>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
<Divider />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='center'>
|
<div className='center'>
|
||||||
<div style={{ width: '100%' }}>
|
<div style={{ width: '100%' }}>
|
||||||
<form style={{ width: '100%' }} onSubmit={handleSubmit}>
|
<form style={{ width: '100%' }} onSubmit={handleSubmit}>
|
||||||
@@ -857,7 +903,7 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
maxRows={isDialog ? 7 : 2}
|
maxRows={isDialog ? 7 : 2}
|
||||||
startAdornment={
|
startAdornment={
|
||||||
isChatFlowAvailableForUploads && (
|
isChatFlowAvailableForUploads && (
|
||||||
<InputAdornment position='start' sx={{ padding: '15px' }}>
|
<InputAdornment position='start' sx={{ pl: 2 }}>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={handleUploadClick}
|
onClick={handleUploadClick}
|
||||||
type='button'
|
type='button'
|
||||||
@@ -910,13 +956,13 @@ export const ChatMessage = ({ open, chatflowid, isDialog }) => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{isChatFlowAvailableForUploads && (
|
{isChatFlowAvailableForUploads && (
|
||||||
<input style={{ display: 'none' }} ref={fileUploadRef} type='file' onChange={handleFileChange} />
|
<input style={{ display: 'none' }} multiple ref={fileUploadRef} type='file' onChange={handleFileChange} />
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SourceDocDialog show={sourceDialogOpen} dialogProps={sourceDialogProps} onCancel={() => setSourceDialogOpen(false)} />
|
<SourceDocDialog show={sourceDialogOpen} dialogProps={sourceDialogProps} onCancel={() => setSourceDialogOpen(false)} />
|
||||||
</div>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,30 +68,39 @@ export function startAudioRecording(onRecordingStart, onUnsupportedBrowser) {
|
|||||||
//Error handling structure
|
//Error handling structure
|
||||||
switch (error.name) {
|
switch (error.name) {
|
||||||
case 'AbortError': //error from navigator.mediaDevices.getUserMedia
|
case 'AbortError': //error from navigator.mediaDevices.getUserMedia
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('An AbortError has occurred.')
|
console.log('An AbortError has occurred.')
|
||||||
break
|
break
|
||||||
case 'NotAllowedError': //error from navigator.mediaDevices.getUserMedia
|
case 'NotAllowedError': //error from navigator.mediaDevices.getUserMedia
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('A NotAllowedError has occurred. User might have denied permission.')
|
console.log('A NotAllowedError has occurred. User might have denied permission.')
|
||||||
break
|
break
|
||||||
case 'NotFoundError': //error from navigator.mediaDevices.getUserMedia
|
case 'NotFoundError': //error from navigator.mediaDevices.getUserMedia
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('A NotFoundError has occurred.')
|
console.log('A NotFoundError has occurred.')
|
||||||
break
|
break
|
||||||
case 'NotReadableError': //error from navigator.mediaDevices.getUserMedia
|
case 'NotReadableError': //error from navigator.mediaDevices.getUserMedia
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('A NotReadableError has occurred.')
|
console.log('A NotReadableError has occurred.')
|
||||||
break
|
break
|
||||||
case 'SecurityError': //error from navigator.mediaDevices.getUserMedia or from the MediaRecorder.start
|
case 'SecurityError': //error from navigator.mediaDevices.getUserMedia or from the MediaRecorder.start
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('A SecurityError has occurred.')
|
console.log('A SecurityError has occurred.')
|
||||||
break
|
break
|
||||||
case 'TypeError': //error from navigator.mediaDevices.getUserMedia
|
case 'TypeError': //error from navigator.mediaDevices.getUserMedia
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('A TypeError has occurred.')
|
console.log('A TypeError has occurred.')
|
||||||
break
|
break
|
||||||
case 'InvalidStateError': //error from the MediaRecorder.start
|
case 'InvalidStateError': //error from the MediaRecorder.start
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('An InvalidStateError has occurred.')
|
console.log('An InvalidStateError has occurred.')
|
||||||
break
|
break
|
||||||
case 'UnknownError': //error from the MediaRecorder.start
|
case 'UnknownError': //error from the MediaRecorder.start
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('An UnknownError has occurred.')
|
console.log('An UnknownError has occurred.')
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('An error occurred with the error name ' + error.name)
|
console.log('An error occurred with the error name ' + error.name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -113,9 +122,11 @@ export function stopAudioRecording(addRecordingToPreviews) {
|
|||||||
//Error handling structure
|
//Error handling structure
|
||||||
switch (error.name) {
|
switch (error.name) {
|
||||||
case 'InvalidStateError': //error from the MediaRecorder.stop
|
case 'InvalidStateError': //error from the MediaRecorder.stop
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('An InvalidStateError has occurred.')
|
console.log('An InvalidStateError has occurred.')
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log('An error occurred with the error name ' + error.name)
|
console.log('An error occurred with the error name ' + error.name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user