mirror of https://github.com/msgbyte/tailchat
feat: 增加emoji表情管理
parent
9642f2c436
commit
71ff176a66
@ -0,0 +1,71 @@
|
||||
import { Icon } from '@iconify/react';
|
||||
import { Menu } from 'antd';
|
||||
import React from 'react';
|
||||
import {
|
||||
ChatMessage,
|
||||
deleteMessage,
|
||||
recallMessage,
|
||||
t,
|
||||
useAsyncRequest,
|
||||
useChatBoxContext,
|
||||
useGroupInfoContext,
|
||||
useUserInfo,
|
||||
} from 'tailchat-shared';
|
||||
|
||||
/**
|
||||
* 消息的会话操作
|
||||
*/
|
||||
export function useChatMessageItemAction(
|
||||
payload: ChatMessage
|
||||
): React.ReactElement {
|
||||
const context = useChatBoxContext();
|
||||
const groupInfo = useGroupInfoContext();
|
||||
const userInfo = useUserInfo();
|
||||
|
||||
const [, handleRecallMessage] = useAsyncRequest(() => {
|
||||
return recallMessage(payload._id);
|
||||
}, [payload._id]);
|
||||
|
||||
const [, handleDeleteMessage] = useAsyncRequest(() => {
|
||||
return deleteMessage(payload._id);
|
||||
}, [payload._id]);
|
||||
|
||||
const isGroupOwner = groupInfo && groupInfo.owner === userInfo?._id; //
|
||||
const isMessageAuthor = payload.author === userInfo?._id;
|
||||
|
||||
return (
|
||||
<Menu>
|
||||
{context.hasContext && (
|
||||
<Menu.Item
|
||||
key="reply"
|
||||
icon={<Icon icon="mdi:reply" />}
|
||||
onClick={() => context.setReplyMsg(payload)}
|
||||
>
|
||||
{t('回复')}
|
||||
</Menu.Item>
|
||||
)}
|
||||
|
||||
{(isGroupOwner || isMessageAuthor) && (
|
||||
<Menu.Item
|
||||
key="recall"
|
||||
icon={<Icon icon="mdi:restore" />}
|
||||
onClick={handleRecallMessage}
|
||||
>
|
||||
{t('撤回')}
|
||||
</Menu.Item>
|
||||
)}
|
||||
|
||||
{/* 仅群组管理员可见 */}
|
||||
{isGroupOwner && (
|
||||
<Menu.Item
|
||||
key="delete"
|
||||
danger={true}
|
||||
icon={<Icon icon="mdi:delete-outline" />}
|
||||
onClick={handleDeleteMessage}
|
||||
>
|
||||
{t('删除')}
|
||||
</Menu.Item>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { EmojiPanel } from '@/components/EmojiPanel';
|
||||
import React, { useCallback } from 'react';
|
||||
import type { ChatMessage } from 'tailchat-shared';
|
||||
|
||||
/**
|
||||
* 消息的反应信息操作
|
||||
*/
|
||||
export function useChatMessageReaction(
|
||||
payload: ChatMessage
|
||||
): React.ReactElement {
|
||||
const handleSelect = useCallback((code: string) => {
|
||||
console.log('code', code);
|
||||
}, []);
|
||||
|
||||
return <EmojiPanel onSelect={handleSelect} />;
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { NimblePicker, Data, EmojiData } from 'emoji-mart';
|
||||
import data from 'emoji-mart/data/twitter.json';
|
||||
|
||||
import 'emoji-mart/css/emoji-mart.css';
|
||||
import { isValidStr } from 'tailchat-shared';
|
||||
|
||||
const emojiData: Data = {
|
||||
compressed: true,
|
||||
categories: [
|
||||
{
|
||||
id: 'people',
|
||||
name: 'Smileys & People',
|
||||
emojis: data.categories[0].emojis,
|
||||
},
|
||||
{
|
||||
id: 'nature',
|
||||
name: 'Animals & Nature',
|
||||
emojis: data.categories[1].emojis,
|
||||
},
|
||||
{
|
||||
id: 'foods',
|
||||
name: 'Food & Drink',
|
||||
emojis: data.categories[2].emojis,
|
||||
},
|
||||
{
|
||||
id: 'activity',
|
||||
name: 'Activities',
|
||||
emojis: data.categories[3].emojis,
|
||||
},
|
||||
{
|
||||
id: 'places',
|
||||
name: 'Travel & Places',
|
||||
emojis: data.categories[4].emojis,
|
||||
},
|
||||
{
|
||||
id: 'objects',
|
||||
name: 'Objects',
|
||||
emojis: data.categories[5].emojis,
|
||||
},
|
||||
{
|
||||
id: 'symbols',
|
||||
name: 'Symbols',
|
||||
emojis: data.categories[6].emojis,
|
||||
},
|
||||
{
|
||||
id: 'flags',
|
||||
name: 'Flags',
|
||||
emojis: data.categories[7].emojis,
|
||||
},
|
||||
],
|
||||
emojis: data.emojis,
|
||||
aliases: data.aliases,
|
||||
};
|
||||
|
||||
interface EmojiPickerProps {
|
||||
onSelect: (code: string) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* emoji表情面板
|
||||
*/
|
||||
const EmojiPicker: React.FC<EmojiPickerProps> = React.memo((props) => {
|
||||
const handleSelect = useCallback(
|
||||
(emoji: EmojiData) => {
|
||||
const code = emoji.colons;
|
||||
if (isValidStr(code)) {
|
||||
props.onSelect(code);
|
||||
}
|
||||
},
|
||||
[props.onSelect]
|
||||
);
|
||||
|
||||
return (
|
||||
<NimblePicker set="twitter" data={emojiData} onSelect={handleSelect} />
|
||||
);
|
||||
});
|
||||
EmojiPicker.displayName = 'EmojiPicker';
|
||||
|
||||
export default EmojiPicker;
|
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { Loadable } from '../Loadable';
|
||||
|
||||
const EmojiPicker = Loadable(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: 'emoji-picker' */ /* webpackPrefetch: true */ './Picker'
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* emoji表情面板
|
||||
*/
|
||||
export const EmojiPanel: React.FC<{
|
||||
onSelect: (code: string) => void;
|
||||
}> = React.memo((props) => {
|
||||
return <EmojiPicker onSelect={props.onSelect} />;
|
||||
});
|
||||
EmojiPanel.displayName = 'EmojiPanel';
|
Loading…
Reference in New Issue