Skip to content

Commit

Permalink
feat: 增加内置的文件发送功能
Browse files Browse the repository at this point in the history
  • Loading branch information
moonrailgun committed Feb 5, 2023
1 parent a70c5e4 commit 469f341
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 13 deletions.
2 changes: 1 addition & 1 deletion client/shared/utils/upload-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export async function uploadFile(

return data;
} catch (e) {
showToasts(`${t('上传失败')}: ${t('可能是图片体积过大')}`, 'error');
showToasts(`${t('上传失败')}: ${t('可能是文件体积过大')}`, 'error');
console.error(`${t('上传失败')}: ${_get(e, 'message')}`);
throw e;
}
Expand Down
8 changes: 8 additions & 0 deletions client/web/plugins/com.msgbyte.bbcode/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ regMessageTextDecorators(() => ({

return `[img]${plain}[/img]`;
},
card: (plain, attrs) => {
const h = [
'card',
...Object.entries(attrs).map(([k, v]) => `${k}=${v}`),
].join(' ');

return `[${h}]${plain}[/card]`;
},
mention: (userId, userName) => `[at=${userId}]${userName}[/at]`,
emoji: (emojiCode) => `[emoji]${stripColons(emojiCode)}[/emoji]`,
serialize: (plain: string) => (serialize ? serialize(plain) : plain),
Expand Down
17 changes: 17 additions & 0 deletions client/web/plugins/com.msgbyte.bbcode/src/tags/CardTag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Card } from '@capital/component';
import React from 'react';
import type { TagProps } from '../bbcode/type';

export const CardTag: React.FC<TagProps> = React.memo((props) => {
const { node } = props;
const label = node.content.join('');
const attrs = node.attrs ?? {};

const payload: any = {
label,
...attrs,
};

return <Card type={payload.type} payload={payload} />;
});
CardTag.displayName = 'CardTag';
2 changes: 2 additions & 0 deletions client/web/plugins/com.msgbyte.bbcode/src/tags/__all__.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { BoldTag } from './BoldTag';
import { ItalicTag } from './ItalicTag';
import { UnderlinedTag } from './UnderlinedTag';
import { DeleteTag } from './DeleteTag';
import { CardTag } from './CardTag';

import './styles.less';

Expand All @@ -28,3 +29,4 @@ registerBBCodeTag('at', MentionTag);
registerBBCodeTag('emoji', EmojiTag);
registerBBCodeTag('markdown', MarkdownTag);
registerBBCodeTag('md', MarkdownTag); // alias
registerBBCodeTag('card', CardTag); // alias
45 changes: 45 additions & 0 deletions client/web/src/components/Card/FileCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { downloadUrl } from '@/utils/file-helper';
import React from 'react';
import { Icon } from 'tailchat-design';
import { useMemoizedFn, t } from 'tailchat-shared';
import { IconBtn } from '../IconBtn';
import { CardWrapper } from './Wrapper';

export interface FileCardPayload {
label: string;
url: string;
}

export const FileCard: React.FC<{
payload: FileCardPayload;
}> = React.memo((props) => {
const payload = props.payload ?? {};

const handleDownload = useMemoizedFn(() => {
downloadUrl(payload.url, payload.label);
});

return (
<CardWrapper>
<div className="flex items-center">
<div className="mr-3 overflow-hidden">
<div className="flex text-lg items-center">
<Icon icon="mdi:paperclip" />
<span className="ml-1">{t('文件')}</span>
</div>

<div className="text-sm text-black text-opacity-60 dark:text-white dark:text-opacity-60">
{payload.label}
</div>
</div>

<IconBtn
title={t('下载')}
icon="mdi:cloud-download-outline"
onClick={handleDownload}
/>
</div>
</CardWrapper>
);
});
FileCard.displayName = 'FileCard';
14 changes: 14 additions & 0 deletions client/web/src/components/Card/Wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';

export const CardWrapper: React.FC<React.PropsWithChildren> = React.memo(
(props) => {
return (
<div className="w-3/4">
<div className="border border-black border-opacity-20 rounded-md p-2 bg-black bg-opacity-5 dark:bg-black dark:bg-opacity-10 inline-flex overflow-hidden">
{props.children}
</div>
</div>
);
}
);
CardWrapper.displayName = 'CardWrapper';
17 changes: 17 additions & 0 deletions client/web/src/components/Card/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { t } from 'tailchat-shared';
import { FileCard, FileCardPayload } from './FileCard';
import { CardWrapper } from './Wrapper';

interface Props {
type: 'file';
payload: FileCardPayload;
}
export const Card: React.FC<Props> = React.memo((props) => {
if (props.type === 'file') {
return <FileCard payload={props.payload} />;
}

return <CardWrapper>{t('未知的卡片类型')}</CardWrapper>;
});
Card.displayName = 'Card';
19 changes: 18 additions & 1 deletion client/web/src/components/ChatBox/ChatInputBox/Addon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Dropdown, Menu } from 'antd';
import React, { useState } from 'react';
import { t } from 'tailchat-shared';
import { useChatInputActionContext } from './context';
import { uploadMessageImage } from './utils';
import { uploadMessageFile, uploadMessageImage } from './utils';
import clsx from 'clsx';

export const ChatInputAddon: React.FC = React.memo(() => {
Expand All @@ -31,6 +31,19 @@ export const ChatInputAddon: React.FC = React.memo(() => {
}
};

const handleSendFile = (files: FileList) => {
// 发送文件
const file = files[0];
if (file) {
// 发送图片
uploadMessageFile(file).then(({ name, url }) => {
actionContext.sendMsg(
getMessageTextDecorators().card(name, { type: 'file', url })
);
});
}
};

const menu = (
<Menu>
<FileSelector
Expand All @@ -40,6 +53,10 @@ export const ChatInputAddon: React.FC = React.memo(() => {
<Menu.Item>{t('发送图片')}</Menu.Item>
</FileSelector>

<FileSelector onSelected={handleSendFile}>
<Menu.Item>{t('发送文件')}</Menu.Item>
</FileSelector>

{pluginChatInputActions.map((item, i) => (
<Menu.Item
key={item.label + i}
Expand Down
18 changes: 12 additions & 6 deletions client/web/src/components/ChatBox/ChatInputBox/ChatDropArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,29 @@ 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 { uploadMessageFile, 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 }) => {
const file = files[0];
if (file.type.startsWith('image/')) {
// 发送图片
uploadMessageImage(file).then(({ url, width, height }) => {
actionContext?.sendMsg(
getMessageTextDecorators().image(url, { width, height })
);
});
} else {
// 发送文件
uploadMessageFile(file).then(({ url, name }) => {
actionContext?.sendMsg(
getMessageTextDecorators().card(name, { type: 'file', url })
);
});
}
});

Expand Down
15 changes: 15 additions & 0 deletions client/web/src/components/ChatBox/ChatInputBox/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,18 @@ export function uploadMessageImage(image: File): Promise<{
});
});
}

/**
* 上传文件,并返回地址
*/
export async function uploadMessageFile(file: File): Promise<{
name: string;
url: string;
}> {
const fileInfo = await uploadFile(file);

return {
name: file.name || fileInfo.etag,
url: fileInfo.url,
};
}
1 change: 1 addition & 0 deletions client/web/src/plugin/common/reg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export const [getMessageRender, regMessageRender] = buildRegFn<
const defaultMessageTextDecorators = {
url: (url: string, label?: string) => url,
image: (plain: string, attrs: Record<string, unknown>) => plain,
card: (plain: string, payload: Record<string, unknown>) => plain,
mention: (userId: string, userName: string) => `@${userName}`,
emoji: (emojiCode: string) => emojiCode,
serialize: (plain: string) => plain,
Expand Down
1 change: 1 addition & 0 deletions client/web/src/plugin/component/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@ export { UserAvatar } from '@/components/UserAvatar';
export { UserName } from '@/components/UserName';
export { Markdown } from '@/components/Markdown';
export { Webview, WebviewKeepAlive } from '@/components/Webview';
export { Card } from '@/components/Card';
10 changes: 5 additions & 5 deletions client/web/src/utils/file-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,21 @@ export async function blobUrlToFile(
}

/**
* 下载Bloburl
* 通过url下载文件
*/
export async function downloadBlobUrl(blobUrl: string, fileName: string) {
export function downloadUrl(url: string, fileName: string) {
const a = document.createElement('a');
a.href = blobUrl;
a.href = url;
a.download = fileName; // 这里填保存成的文件名
a.click();
}

/**
* 下载Blob文件
*/
export async function downloadBlob(blob: Blob, fileName: string) {
export function downloadBlob(blob: Blob, fileName: string) {
const url = String(URL.createObjectURL(blob));
downloadBlobUrl(url, fileName);
downloadUrl(url, fileName);
URL.revokeObjectURL(url);
}

Expand Down

0 comments on commit 469f341

Please sign in to comment.