|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
import React, { useState, useRef, useEffect } from 'react';
|
|
|
|
|
import { Input, Flex, Button, Image } from 'antd';
|
|
|
|
|
import { App, Input, Flex, Button, Image, } from 'antd';
|
|
|
|
|
// import { Input } from 'react-chat-elements';
|
|
|
|
|
import useAuthStore from '@/stores/AuthStore';
|
|
|
|
|
import useConversationStore from '@/stores/ConversationStore';
|
|
|
|
@ -23,17 +23,36 @@ import { sentMsgTypeMapped } from '@/lib/msgUtils';
|
|
|
|
|
import InputTemplate from './Input/Template';
|
|
|
|
|
import InputEmoji from './Input/Emoji';
|
|
|
|
|
import InputMediaUpload from './Input/MediaUpload';
|
|
|
|
|
import { postUploadFileItem } from '@/actions/CommonActions';
|
|
|
|
|
import dayjs from 'dayjs';
|
|
|
|
|
|
|
|
|
|
const aliOSSHost = `https://haina-sale-system.oss-cn-shenzhen.aliyuncs.com/WAMedia/`;
|
|
|
|
|
/**
|
|
|
|
|
* image
|
|
|
|
|
* ext: ani;bmp;gif;ico;jpe;jpeg;jpg;pcx;png;psd;tga;tif;tiff;wmf
|
|
|
|
|
*
|
|
|
|
|
* audio
|
|
|
|
|
* ext: aac;ac3;aif;aifc;aiff;au;cda;dts;fla;flac;it;m1a;m2a;m3u;m4a;mid;midi;mka;mod;mp2;mp3;mpa;ogg;ra;rmi;spc;rmi;snd;umx;voc;wav;wma;xm
|
|
|
|
|
*
|
|
|
|
|
* video
|
|
|
|
|
* ext: 3g2;3gp;3gp2;3gpp;amr;amv;asf;avi;bdmv;bik;d2v;divx;drc;dsa;dsm;dss;dsv;evo;f4v;flc;fli;flic;flv;hdmov;ifo;ivf;m1v;m2p;m2t;m2ts;m2v;m4b;m4p;m4v;mkv;mp2v;mp4;mp4v;mpe;mpeg;mpg;mpls;mpv2;mpv4;mov;mts;ogm;ogv;pss;pva;qt;ram;ratdvd;rm;rmm;rmvb;roq;rpm;smil;smk;swf;tp;tpr;ts;vob;vp6;webm;wm;wmp;wmv
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
const fileTypesExt = {
|
|
|
|
|
sticker: ['webp'],
|
|
|
|
|
photo: ['jpeg', 'jpg', 'png'],
|
|
|
|
|
video: ['gif', 'mp4', '3gp'],
|
|
|
|
|
document: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'csv'],
|
|
|
|
|
audio: ['aac', 'mp4', 'm4a', 'mp3', 'amr', 'ogg'],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const InputComposer = () => {
|
|
|
|
|
const userId = useAuthStore(state => state.loginUser.userId);
|
|
|
|
|
const websocket = useConversationStore(state => state.websocket);
|
|
|
|
|
const websocketOpened = useConversationStore(state => state.websocketOpened);
|
|
|
|
|
const currentConversation = useConversationStore(state => state.currentConversation);
|
|
|
|
|
const referenceMsg = useConversationStore(state => state.referenceMsg);
|
|
|
|
|
const setReferenceMsg = useConversationStore(state => state.setReferenceMsg);
|
|
|
|
|
const complexMsg = useConversationStore(state => state.complexMsg);
|
|
|
|
|
const setComplexMsg = useConversationStore(state => state.setComplexMsg);
|
|
|
|
|
const [referenceMsg, setReferenceMsg] = useConversationStore(state => [state.referenceMsg, state.setReferenceMsg]);
|
|
|
|
|
const [complexMsg, setComplexMsg] = useConversationStore(state => [state.complexMsg, state.setComplexMsg]);
|
|
|
|
|
const sentOrReceivedNewMessage = useConversationStore(state => state.sentOrReceivedNewMessage);
|
|
|
|
|
|
|
|
|
|
const talkabled = !isEmpty(currentConversation.sn) && websocketOpened;
|
|
|
|
@ -103,6 +122,69 @@ const InputComposer = () => {
|
|
|
|
|
websocket.sendMessage({ ...contentToSend, opi_sn: userId, coli_sn: currentConversation.coli_sn });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const { message } = App.useApp();
|
|
|
|
|
const [pastedUploading, setPastedUploading] = useState(false);
|
|
|
|
|
const readPasted = async (file, rename = false) => {
|
|
|
|
|
// 使用 FileReader 读取文件对象
|
|
|
|
|
const reader = new FileReader();
|
|
|
|
|
const suffix = file.name.slice(file.name.lastIndexOf('.')+1);
|
|
|
|
|
const newName = rename ? `${uuid()}.${suffix}` : file.name;
|
|
|
|
|
const type = Object.keys(fileTypesExt).find((type) => fileTypesExt[type].includes(suffix));
|
|
|
|
|
const dataUri = aliOSSHost + newName;
|
|
|
|
|
const msgObj = {
|
|
|
|
|
type: type,
|
|
|
|
|
name: newName,
|
|
|
|
|
uploadStatus: 'loading',
|
|
|
|
|
data: { dataUri: dataUri, link: dataUri, width: '100%', height: 150, loading: 0.01 },
|
|
|
|
|
id: uuid(),
|
|
|
|
|
};
|
|
|
|
|
// 读取完毕后获取结果
|
|
|
|
|
reader.onload = (event) => {
|
|
|
|
|
const previewSrc = event.target.result;
|
|
|
|
|
msgObj.data.uri = previewSrc;
|
|
|
|
|
};
|
|
|
|
|
file.newName = newName;
|
|
|
|
|
file.msgData = msgObj;
|
|
|
|
|
// 把文件对象作为一个 dataURL 读入
|
|
|
|
|
reader.readAsDataURL(file);
|
|
|
|
|
return file;
|
|
|
|
|
};
|
|
|
|
|
const handlePaste = async (event) => {
|
|
|
|
|
const items = (event.clipboardData || window.clipboardData).items;
|
|
|
|
|
let tmpfile = null;
|
|
|
|
|
if (!items || items.length === 0) {
|
|
|
|
|
// 当前浏览器不支持本地
|
|
|
|
|
message.warning('当前浏览器不支持本地');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
let isNotFile = true;
|
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
|
|
|
// if (items[i].type.indexOf("image") !== -1) {
|
|
|
|
|
if (items[i].kind.indexOf("file") !== -1) {
|
|
|
|
|
isNotFile = false;
|
|
|
|
|
tmpfile = items[i].getAsFile();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (isNotFile) {
|
|
|
|
|
// 普通的粘贴
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!tmpfile) {
|
|
|
|
|
message.warning('没有读取到粘贴内容');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const shouldRename = tmpfile.type.indexOf('image') !== -1;
|
|
|
|
|
const _tmpFile = await readPasted(tmpfile, shouldRename);
|
|
|
|
|
setComplexMsg(_tmpFile.msgData);
|
|
|
|
|
setPastedUploading(true);
|
|
|
|
|
const { file_url } = await postUploadFileItem(tmpfile, _tmpFile.newName);
|
|
|
|
|
setPastedUploading(false);
|
|
|
|
|
setComplexMsg({..._tmpFile.msgData, uploadStatus: file_url ? 'done' : 'error'});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const focusInput = () => {
|
|
|
|
|
textInputRef.current.focus({ cursor: 'end', preventScroll: true, });
|
|
|
|
|
};
|
|
|
|
@ -144,17 +226,27 @@ const InputComposer = () => {
|
|
|
|
|
{complexMsg.id && (
|
|
|
|
|
<Flex justify='space-between' className='reply-to bg-gray-100 p-1 rounded-none text-slate-500'>
|
|
|
|
|
<div className='pl-2 pr-1 py-1'>
|
|
|
|
|
{(complexMsg.type === 'photo' && complexMsg.data.uri) && <Image width={100} src={complexMsg.data.uri} />}
|
|
|
|
|
{complexMsg.type === 'video' && <FileOutlined className=' text-red-400' />}
|
|
|
|
|
{complexMsg.type !== 'photo' && <span className='px-1'>{complexMsg.name}</span>}
|
|
|
|
|
{complexMsg.status === 'loading' && <LoadingOutlined className='px-1' />}
|
|
|
|
|
{complexMsg.status === 'done' && <CheckCircleOutlined className='px-1 text-primary' />}
|
|
|
|
|
{complexMsg.status === 'error' && <><CloseCircleOutlined className='px-1 text-red-400' /> <span>添加失败</span> </>}
|
|
|
|
|
{['photo', 'sticker'].includes(complexMsg.type) && complexMsg.data.uri ? (
|
|
|
|
|
<Image width={100} src={complexMsg.data.uri} />
|
|
|
|
|
) : (
|
|
|
|
|
<>
|
|
|
|
|
<FileOutlined className=' text-red-400' />
|
|
|
|
|
<span className='px-1'>{complexMsg.name}</span>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
{complexMsg.uploadStatus === 'loading' && <LoadingOutlined className='px-1' />}
|
|
|
|
|
{/* {complexMsg.uploadStatus === 'done' && <CheckCircleOutlined className='px-1 text-primary' />} */}
|
|
|
|
|
{complexMsg.uploadStatus === 'error' && (
|
|
|
|
|
<>
|
|
|
|
|
<CloseCircleOutlined className='px-1 text-red-400' /> <span>添加失败</span>{' '}
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<Button type='text' title='删除' className=' rounded-none text-slate-500' icon={<CloseCircleOutlined />} size={'middle'} onClick={() => setComplexMsg({})} />
|
|
|
|
|
</Flex>
|
|
|
|
|
)}
|
|
|
|
|
<Input.TextArea
|
|
|
|
|
onPaste={handlePaste}
|
|
|
|
|
ref={textInputRef}
|
|
|
|
|
size='large'
|
|
|
|
|
placeholder={gt24h ? 'This session has expired. Please send a template message to activate the session' : 'Enter for Send, Shift+Enter for new line'}
|
|
|
|
@ -175,14 +267,14 @@ const InputComposer = () => {
|
|
|
|
|
<Flex gap={4} className='*:text-primary *:rounded-none'>
|
|
|
|
|
<InputTemplate key='templates' disabled={!talkabled || textabled} invokeSendMessage={invokeSendMessage} />
|
|
|
|
|
<InputEmoji key='emoji' disabled={!textabled} inputEmoji={addEmoji} />
|
|
|
|
|
<InputMediaUpload key={'addNewMedia'} disabled={!textabled} {...{invokeUploadFileMessage, invokeSendUploadMessage}} />
|
|
|
|
|
<InputMediaUpload key={'addNewMedia'} disabled={!textabled} {...{ invokeUploadFileMessage, invokeSendUploadMessage }} />
|
|
|
|
|
{/* <Button type='text' className='' icon={<YoutubeOutlined />} size={'middle'} />
|
|
|
|
|
<Button type='text' className='' icon={<AudioOutlined />} size={'middle'} />
|
|
|
|
|
<Button type='text' className='' icon={<FolderAddOutlined />} size={'middle'} />
|
|
|
|
|
<Button type='text' className='' icon={<CloudUploadOutlined />} size={'middle'} />
|
|
|
|
|
<Button type='text' className='' icon={<FilePdfOutlined />} size={'middle'} /> */}
|
|
|
|
|
</Flex>
|
|
|
|
|
<Button key={'send-btn'} onClick={handleSendText} type='primary' size='middle' icon={<SendOutlined />} disabled={!textabled}>
|
|
|
|
|
<Button key={'send-btn'} onClick={handleSendText} type='primary' size='middle' icon={<SendOutlined />} disabled={!textabled || pastedUploading}>
|
|
|
|
|
Send
|
|
|
|
|
</Button>
|
|
|
|
|
</Flex>
|
|
|
|
|