feat: 增加拖拽文件发送图片的功能

pull/64/head
moonrailgun 2 years ago
parent fdb1830e92
commit 338af097ca

@ -284,6 +284,7 @@
"ke071c620": "Allow members to manage users, such as banning, removing users, etc.",
"ke17b2c87": "Do not upload pictures that violate local laws and regulations",
"ke187440d": "Panel type cannot be empty",
"ke3d797fd": "Drop files to send into current converse",
"ke59ffe49": "Muted, there are {{remain}} left",
"kea977d95": "The following users are offline",
"kec46a57f": "Add members",

@ -284,6 +284,7 @@
"ke071c620": "允许成员管理用户,如禁言、移除用户等操作",
"ke17b2c87": "请勿上传违反当地法律法规的图片",
"ke187440d": "面板类型不能为空",
"ke3d797fd": "拖放文件以发送到当前会话",
"ke59ffe49": "禁言中, 还剩 {{remain}}",
"kea977d95": "以下用户已离线",
"kec46a57f": "添加成员",

@ -49,6 +49,8 @@
"qs": "^6.11.0",
"rc-tree": "^5.7.2",
"react": "18.2.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "18.2.0",
"react-easy-crop": "^3.5.3",
"react-helmet": "^6.1.0",

@ -23,6 +23,8 @@ import { pluginRootRoute } from './plugin/common';
import { PortalHost as FallbackPortalHost } from './components/Portal';
import isElectron from 'is-electron';
import { AppRouterApi } from './components/AppRouterApi';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
const AppRouter: any = isElectron() ? HashRouter : BrowserRouter;
@ -55,9 +57,11 @@ const AppProvider: React.FC<PropsWithChildren> = React.memo((props) => {
<Suspense fallback={<LoadingSpinner />}>
<AppRouter>
<TcProvider>
<AntdProvider getPopupContainer={getPopupContainer}>
{props.children}
</AntdProvider>
<DndProvider backend={HTML5Backend}>
<AntdProvider getPopupContainer={getPopupContainer}>
{props.children}
</AntdProvider>
</DndProvider>
</TcProvider>
</AppRouter>
</Suspense>

@ -0,0 +1,62 @@
import React from 'react';
import { t, useMemoizedFn } from 'tailchat-shared';
import { DropTargetMonitor, useDrop } from 'react-dnd';
import { NativeTypes } from 'react-dnd-html5-backend';
import { useChatInputActionContext } from './context';
import { uploadMessageImage } from './utils';
import { getMessageTextDecorators } from '@/plugin/common';
import { Icon } from 'tailchat-design';
export const ChatDropArea: React.FC = React.memo(() => {
const actionContext = useChatInputActionContext();
const handleDrop = useMemoizedFn((files: File[]) => {
const images = files.filter((f) => f.type.startsWith('image/'));
if (images.length > 0) {
// 目前只取一张
const img = images[0];
uploadMessageImage(img).then(({ url, width, height }) => {
actionContext?.sendMsg(
getMessageTextDecorators().image(url, { width, height })
);
});
}
});
const [collectedProps, ref] = useDrop({
accept: [NativeTypes.FILE],
drop(item: { files: any[] }) {
handleDrop(item.files);
},
canDrop(item: any) {
return true;
},
collect: (monitor: DropTargetMonitor) => {
return {
isOver: monitor.isOver(),
canDrop: monitor.canDrop(),
};
},
});
if (!collectedProps.canDrop) {
return;
}
return (
<div
ref={ref}
className={
'absolute inset-0 bg-white bg-opacity-50 dark:bg-black dark:bg-opacity-50 p-4'
}
>
<div className="h-full w-full border-dashed border-8 flex flex-col justify-center items-center">
<div>
<Icon icon="mdi:cloud-upload" fontSize={128} />
</div>
<div className="text-xl font-bold">{t('拖放文件以发送到当前会话')}</div>
</div>
</div>
);
});
ChatDropArea.displayName = 'ChatDropArea';

@ -14,6 +14,7 @@ import {
} from 'tailchat-shared';
import { ChatInputEmotion } from './Emotion';
import _uniq from 'lodash/uniq';
import { ChatDropArea } from './ChatDropArea';
interface ChatInputBoxProps {
onSendMsg: (msg: string, meta?: SendMessagePayloadMeta) => void;
@ -111,6 +112,8 @@ export const ChatInputBox: React.FC<ChatInputBoxProps> = React.memo((props) => {
<ChatInputEmotion />
<ChatInputAddon />
</div>
<ChatDropArea />
</div>
</div>
</ChatInputActionContext.Provider>

@ -392,6 +392,8 @@ importers:
qs: ^6.11.0
rc-tree: ^5.7.2
react: 18.2.0
react-dnd: ^16.0.1
react-dnd-html5-backend: ^16.0.1
react-dom: 18.2.0
react-easy-crop: ^3.5.3
react-helmet: ^6.1.0
@ -461,6 +463,8 @@ importers:
qs: 6.11.0
rc-tree: 5.7.2_biqbaboplfbrettd7655fr4n2y
react: 18.2.0
react-dnd: 16.0.1_44xqbblv4eynrtx3g5sxpcjfbe
react-dnd-html5-backend: 16.0.1
react-dom: 18.2.0_react@18.2.0
react-easy-crop: 3.5.3_biqbaboplfbrettd7655fr4n2y
react-helmet: 6.1.0_react@18.2.0
@ -6921,6 +6925,18 @@ packages:
tslib: 2.4.1
dev: false
/@react-dnd/asap/5.0.2:
resolution: {integrity: sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==}
dev: false
/@react-dnd/invariant/4.0.2:
resolution: {integrity: sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==}
dev: false
/@react-dnd/shallowequal/4.0.2:
resolution: {integrity: sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==}
dev: false
/@reduxjs/toolkit/1.8.5_kkwg4cbsojnjnupd3btipussee:
resolution: {integrity: sha512-f4D5EXO7A7Xq35T0zRbWq5kJQyXzzscnHKmjnu2+37B3rwHU6mX9PYlbfXdnxcY6P/7zfmjhgan0Z+yuOfeBmA==}
peerDependencies:
@ -16987,6 +17003,14 @@ packages:
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
dev: false
/dnd-core/16.0.1:
resolution: {integrity: sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==}
dependencies:
'@react-dnd/asap': 5.0.2
'@react-dnd/invariant': 4.0.2
redux: 4.2.0
dev: false
/dns-equal/1.0.0:
resolution: {integrity: sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==}
@ -29542,6 +29566,37 @@ packages:
- utf-8-validate
dev: false
/react-dnd-html5-backend/16.0.1:
resolution: {integrity: sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==}
dependencies:
dnd-core: 16.0.1
dev: false
/react-dnd/16.0.1_44xqbblv4eynrtx3g5sxpcjfbe:
resolution: {integrity: sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==}
peerDependencies:
'@types/hoist-non-react-statics': '>= 3.3.1'
'@types/node': '>= 12'
'@types/react': '>= 16'
react: '>= 16.14'
peerDependenciesMeta:
'@types/hoist-non-react-statics':
optional: true
'@types/node':
optional: true
'@types/react':
optional: true
dependencies:
'@react-dnd/invariant': 4.0.2
'@react-dnd/shallowequal': 4.0.2
'@types/node': 15.14.9
'@types/react': 18.0.20
dnd-core: 16.0.1
fast-deep-equal: 3.1.3
hoist-non-react-statics: 3.3.2
react: 18.2.0
dev: false
/react-docgen-typescript/2.2.2_typescript@4.7.4:
resolution: {integrity: sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==}
peerDependencies:

Loading…
Cancel
Save