feat: 输入区域的小工具栏

dev/emitter
Lei OT 10 months ago
parent 283718c4c7
commit 6796d90ce6

@ -0,0 +1,35 @@
import { createContext, useEffect, useState } from 'react'
import { Flex } from 'antd'
import InputTemplate from './Template'
import InputEmoji from './Emoji'
import InputMediaUpload from './MediaUpload'
import PaymentlinkBtn from './PaymentlinkBtn'
import SnippestBtn from './SnippestBtn'
import useConversationStore from '@/stores/ConversationStore'
import { isEmpty } from '@/utils/commons'
const ComposerTools = ({ channel, invokeSendUploadMessage, invokeSendMessage, invokeUploadFileMessage, inputEmoji, ...props }) => {
const websocket = useConversationStore((state) => state.websocket)
const websocketOpened = useConversationStore((state) => state.websocketOpened)
const currentConversation = useConversationStore((state) => state.currentConversation)
const talkabled = !isEmpty(currentConversation.sn) && websocketOpened
return (
<>
<Flex gap={4} className='*:text-primary *:rounded-none items-center'>
{['waba'].includes(channel) && <InputTemplate key='templates' disabled={!talkabled} invokeSendMessage={invokeSendMessage} />}
<InputEmoji key='emoji' disabled={!talkabled} inputEmoji={inputEmoji} />
{['waba', 'wa'].includes(channel) && <InputMediaUpload key={'addNewMedia'} disabled={!talkabled} {...{ invokeUploadFileMessage, invokeSendUploadMessage }} />}
<PaymentlinkBtn />
<SnippestBtn />
{/* <Button type='text' className='' icon={<YoutubeOutlined />} size={'middle'} />
<Button type='text' className='' icon={<AudioOutlined />} size={'middle'} /> */}
</Flex>
</>
)
}
export default ComposerTools

@ -1,6 +1,6 @@
import { useState, useEffect, useRef } from 'react'
import { App, Button, ConfigProvider, Dropdown, Flex, Select, Input, Tooltip, Form } from 'antd'
import { DownOutlined, DollarOutlined, ExpandAltOutlined, ExpandOutlined, SendOutlined } from '@ant-design/icons'
import { DownOutlined, DollarOutlined, ExpandAltOutlined, ExpandOutlined, SendOutlined, } from '@ant-design/icons'
import EmailEditorPopup from './EmailEditorPopup'
import useStyleStore from '@/stores/StyleStore'
import useAuthStore from '@/stores/AuthStore'
@ -9,11 +9,10 @@ import useConversationStore from '@/stores/ConversationStore'
import { useOrderStore } from '@/stores/OrderStore'
import { EditIcon } from '@/components/Icons'
import { cloneDeep, isEmpty } from '@/utils/commons'
import InputEmoji from './Emoji'
import InputMediaUpload from './MediaUpload'
import { v4 as uuid } from 'uuid'
import { postSendEmail } from '@/actions/EmailActions'
import { sentMsgTypeMapped, } from '@/channel/bubbleMsgUtils';
import ComposerTools from './ComposerTools'
const EmailComposer = ({ ...props }) => {
const { notification } = App.useApp()
@ -26,7 +25,6 @@ const EmailComposer = ({ ...props }) => {
const currentConversation = useConversationStore((state) => state.currentConversation)
// const talkabled = !isEmpty(currentConversation.sn) && websocketOpened;
const { orderDetail, customerDetail } = useOrderStore()
const [openPaymentDrawer] = useOrderStore((state) => [state.openDrawer])
const emailListOption = emailList?.map((ele) => ({ ...ele, label: ele.email, key: ele.email, value: ele.email })) || []
const emailListAddrMapped = emailListOption?.reduce((r, v) => ({ ...r, [v.email]: v }), {});
@ -58,11 +56,30 @@ const EmailComposer = ({ ...props }) => {
setToEmail(currentConversation?.channels?.email || customerDetail?.email || '')
}
const lastFocusedFieldRef = useRef(null);
const textInputRef = useRef(null)
const websocketOpened = useConversationStore((state) => state.websocketOpened)
const talkabled = !isEmpty(currentConversation.sn) && websocketOpened
const [sendLoading, setSendLoading] = useState(false)
const handleFocus = (field) => {
lastFocusedFieldRef.current = field;
}
const addEmoji = (emoji) => {
const _field = lastFocusedFieldRef.current || 'mailcontent';
// if (focusedField) {
const fieldValue = form.getFieldValue(_field) || '';
const updatedValue = `${fieldValue}${emoji}`;
// form.setFieldsValue({ [_field]: updatedValue });
form.setFieldValue(_field, updatedValue)
form.focusField(_field)
// }
}
/**
* 保存成功, 推一个气泡
* 再从异步通知更新消息发送状态
@ -87,17 +104,10 @@ const EmailComposer = ({ ...props }) => {
sentOrReceivedNewMessage(contentToRender.conversationid, contentToRender);
};
const addEmoji = (emoji) => {
form.setFieldValue('mailcontent', form.getFieldValue('mailcontent') + emoji)
// setTextContent((prevValue) => {
// return prevValue + emoji
// })
}
// const [quickValidateHelp, setQuickValidateHelp] = useState('')
const handleSendEmail = async (values) => {
console.log('invoke email message');
// console.log('invoke email message');
const emailAccount = { opi_sn: fromUser, email: pickEmail.key, mat_sn: '' };
emailAccount.opi_sn = fromUser || emailListAddrMapped?.[emailAccount.email]?.opi_sn || '';
@ -177,6 +187,7 @@ const EmailComposer = ({ ...props }) => {
className='rounded-b-none border-b-0 font-bold text-base text-indigo-600'
placeholder='*主题'
disabled={!talkabled}
onFocus={() => handleFocus('subject')}
suffix={
<Tooltip title={'全文编辑'}>
<Button
@ -193,6 +204,7 @@ const EmailComposer = ({ ...props }) => {
<Input.TextArea
allowClear
ref={textInputRef}
onFocus={() => handleFocus('mailcontent')}
size='large'
maxLength={2000}
showCount={true}
@ -204,13 +216,7 @@ const EmailComposer = ({ ...props }) => {
/>
</Form.Item>
<Flex gap={8} className='w-full bg-gray-200 p-1 rounded-b-0' align={'center'} justify={'space-between'}>
<Flex gap={4} className='*:text-primary *:rounded-none items-center'>
<InputEmoji key='emoji' disabled={!talkabled} inputEmoji={addEmoji} />
{/* <InputMediaUpload key={'addNewMedia'} disabled={!textabled} {...{ invokeUploadFileMessage, invokeSendUploadMessage }} /> */}
<Tooltip title='支付链接' >
<Button type='text' onClick={() => openPaymentDrawer()} icon={<DollarOutlined className='text-orange-500' />} size={'middle'} />
</Tooltip>
</Flex>
<ComposerTools key={'et'} channel={'email'} inputEmoji={addEmoji} />
{/* <span>切换邮箱:</span>*/}
<Flex gap={4} align={'center'}>
<div className='text-red-500'>

@ -15,7 +15,10 @@ const InputTemplate = ({ disabled = false, inputEmoji }) => {
<>
<Popover
overlayClassName='p-0'
placement={mobile === false ? 'right' : 'top'}
placement='top'
arrow={false}
align={{offset: [-6, -100] }}
// placement={mobile === false ? 'left' : 'top'}
overlayInnerStyle={{ padding: 0, borderRadius: '8px' }}
forceRender={true}
content={<EmojiPicker skinTonesDisabled={true} emojiStyle='native' onEmojiClick={handlePickEmoji} className='chatwindow-wrapper' />}

@ -21,6 +21,7 @@ import { postUploadFileItem } from '@/actions/CommonActions';
import dayjs from 'dayjs';
import useStyleStore from '@/stores/StyleStore';
import { useOrderStore } from '@/stores/OrderStore'
import ComposerTools from './ComposerTools';
const ButtonStyleClsMapped =
{
@ -29,7 +30,7 @@ const ButtonStyleClsMapped =
'wa': 'bg-whatsapp shadow shadow-whatsapp-300 hover:!bg-whatsapp-400 active:bg-whatsapp-400 focus:bg-whatsapp-400',
};
const InputComposer = ({ channel }) => {
const InputComposer = ({ channel, currentActive }) => {
const [mobile] = useStyleStore((state) => [state.mobile]);
const {userId, whatsAppBusiness} = useAuthStore((state) => state.loginUser);
@ -249,9 +250,9 @@ const InputComposer = ({ channel }) => {
};
useEffect(() => {
focusInput();
if (currentActive) focusInput();
return () => {};
}, [referenceMsg, complexMsg]);
}, [referenceMsg, complexMsg, currentActive]);
return (
<div>
@ -316,21 +317,7 @@ const InputComposer = ({ channel }) => {
autoSize={{ minRows: 2, maxRows: 6 }}
/>
<Flex justify={'space-between'} className=' bg-gray-200 p-1 rounded-b-0'>
<Flex gap={4} className='*:text-primary *:rounded-none items-center'>
{channel==='waba' && <InputTemplate key='templates' disabled={!talkabled} invokeSendMessage={invokeSendMessage} />}
<InputEmoji key='emoji' disabled={!textabled} inputEmoji={addEmoji} />
<InputMediaUpload key={'addNewMedia'} disabled={!textabled} {...{ invokeUploadFileMessage, invokeSendUploadMessage }} />
<Tooltip title={<><div>支付链接</div></>} >
<Button type='text' onClick={() => openPaymentDrawer()} icon={<DollarOutlined className='text-orange-500' />} size={'middle'} />
</Tooltip>
{/* <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>
<ComposerTools key={'wt'} channel={channel} inputEmoji={addEmoji} {...{ invokeUploadFileMessage, invokeSendUploadMessage, invokeSendMessage }} />
<Flex gap={4} align={'center'}>
<div className='text-neutral-400 text-sm'>
{/* <ExpireTimeClock expireTime={currentConversation.conversation_expiretime} /> */}

@ -0,0 +1,15 @@
import { createContext, useEffect, useState } from 'react'
import { useOrderStore } from '@/stores/OrderStore'
import { Tooltip, Button } from 'antd'
const PaymentlinkBtn = ({ ...props }) => {
const [openPaymentDrawer] = useOrderStore((state) => [state.openDrawer])
return (
<Tooltip title='支付链接' >
{/* <Button type='text' onClick={() => openPaymentDrawer()} icon={<DollarOutlined className='text-orange-500' />} size={'middle'} /> */}
<Button type='text' onClick={() => openPaymentDrawer()} size={'middle'} className='px-1' >💲</Button>
</Tooltip>
)
}
export default PaymentlinkBtn

@ -0,0 +1,17 @@
import { createContext, useEffect, useState } from 'react'
import useSnippetStore from '@/stores/SnippetStore'
import { Tooltip, Button } from 'antd'
const SnippestBtn = ({ ...props }) => {
const [openSnippetDrawer] = useSnippetStore((state) => [state.openDrawer])
return (
<Tooltip title='图文集'>
<Button type='text' onClick={() => openSnippetDrawer()} size={'middle'} className='px-1'>
📝
</Button>
</Tooltip>
)
}
export default SnippestBtn

@ -68,8 +68,8 @@ const ReplyWrapper = () => {
const replyTypes = [
// { key: 'waba', label: mobile ? '' : (<WABASwitcher />), icon: <WABIcon />, children: <InputComposer channel={'waba'} /> },
{ key: 'waba', label: mobile ? '' : '商业号', icon: <WABIcon />, children: <InputComposer channel={'waba'} /> },
{ key: 'email', label: mobile ? '' : '邮件', icon: <MailOutlined className='text-indigo-500' />, children: <EmailComposer /> },
{ key: 'waba', label: mobile ? '' : '商业号', icon: <WABIcon />, children: <InputComposer currentActive={activeChannel==='waba'} channel={'waba'} /> },
{ key: 'email', label: mobile ? '' : '邮件', icon: <MailOutlined className='text-indigo-500' />, children: <EmailComposer currentActive={activeChannel==='email'} /> },
// { key: 'whatsapp', label: mobile ? '' : 'WhatsApp', icon: <WhatsAppOutlined className='text-whatsapp' />, children: <InputComposer channel={'whatsapp'} /> },
{ key: 'wa', label: mobile ? '' : 'WhatsApp', icon: <WhatsAppOutlined className='text-whatsapp' />, children: <div className='p-2 py-4 text-center text-whatsapp bg-gray-200 rounded rounded-b-none border-gray-300 border-solid border border-b-0 border-x-0'>正在开发敬请期待</div> },
// TODO WA

Loading…
Cancel
Save