import { useMemo } from 'react'; import { IconCopy, IconRollback, IconChat, IconRollfront, IconDelete, Toast, useUIKit, } from '@tencentcloud/uikit-base-component-react'; import { MessageType, MessageStatus, MessageActionStoreRaw } from 'tuikit-atomicx-react/chat'; import { useChatUIState } from '../context/useChatUIState'; import { copyTextToClipboard, transformTextWithEmojiKeyToName } from '../utils'; import { isCallMessage } from '../utils/call'; import classes from '../components/MessageList/Message/MessageActionDropdown/MessageActionDropdown.module.scss'; import type { MessageAction } from '../types/message'; import type { MessageInfo, TextMessagePayload } from 'tuikit-atomicx-react/chat'; const REVOKE_WINDOW_MS = 60 * 2 * 1000; // ms const DEFAULT_ACTIONS: Record = { copy: { key: 'copy', label: 'Copy', visible: (message: MessageInfo) => message.messageType === MessageType.Text, icon: , }, recall: { key: 'recall', label: 'Recall', visible: (message: MessageInfo) => !isCallMessage(message) && message.isSentBySelf && message.status === MessageStatus.SendSuccess && Date.now() - (message.timestamp?.getTime() ?? 0) < REVOKE_WINDOW_MS, icon: , }, quote: { key: 'quote', label: 'Quote', visible: (message: MessageInfo) => !isCallMessage(message) && message.status === MessageStatus.SendSuccess, icon: , }, forward: { key: 'forward', label: 'Forward', visible: (message: MessageInfo) => !isCallMessage(message) && message.status === MessageStatus.SendSuccess, icon: , }, delete: { key: 'delete', label: 'Delete', visible: (message: MessageInfo) => message.status === MessageStatus.SendSuccess, icon: , className: classes.danger, }, }; const DEFAULT_ACTION_ORDER: string[] = ['copy', 'recall', 'quote', 'forward', 'delete']; export function useMessageActions(propsActionList?: any[]): MessageAction[] { const { setQuotedMessage, openForwardModal, focusInput } = useChatUIState(); const { t } = useUIKit(); const defaultActionHandlers: Record void> = { copy: (message) => { const text = (message.messagePayload as TextMessagePayload)?.text || ''; copyTextToClipboard(transformTextWithEmojiKeyToName(text)) .then(() => Toast.success({ message: t('MessageList.copy_success') })) .catch(() => Toast.error({ message: t('MessageList.copy_failed') })); }, recall: (message) => { const actionStore = MessageActionStoreRaw.create(message); actionStore.revoke() .then(() => { Toast.success({ message: t('MessageList.recall_success') }); }) .catch((err: any) => { Toast.error({ message: err.code === 20016 ? t('MessageList.recall_time_limit_exceeded') : t('MessageList.recall_failed'), }); }) .finally(() => actionStore.destroy()); }, quote: (message) => { setQuotedMessage(message); focusInput(); }, forward: (message) => { openForwardModal([message]); }, delete: (message) => { const actionStore = MessageActionStoreRaw.create(message); actionStore.delete() .then(() => { Toast.success({ message: t('MessageList.delete_success') }); }) .catch(() => { Toast.error({ message: t('MessageList.delete_failed') }); }) .finally(() => actionStore.destroy()); }, }; // resolve props list const resolvedActions = (propsActionList || DEFAULT_ACTION_ORDER).map((propsAction): MessageAction => { // if props is string use default config if (typeof propsAction === 'string') { const defaultConfig = DEFAULT_ACTIONS[propsAction]; return { ...defaultConfig, onClick: defaultActionHandlers[propsAction], }; } // if props is object override default config const defaultActionConfig = DEFAULT_ACTIONS[propsAction.key] || {}; return { ...defaultActionConfig, ...propsAction, onClick: propsAction.onClick || defaultActionHandlers[propsAction.key], }; }); return useMemo(() => resolvedActions, [resolvedActions]); }