import {EllipsisHorizontalIcon, PencilIcon, TrashIcon} from '@heroicons/react/24/outline' import {FaceSmileIcon} from '@heroicons/react/24/solid' import {JSONContent} from '@tiptap/react' import clsx from 'clsx' import {ChatMessage} from 'common/chat-message' import {Dispatch, SetStateAction, useEffect, useRef, useState} from 'react' import {toast} from 'react-hot-toast' import DropdownMenu, {DropdownItem} from 'web/components/comments/dropdown-menu' import {useClickOutside} from 'web/hooks/use-click-outside' import {useIsMobile} from 'web/hooks/use-is-mobile' import {useUser} from 'web/hooks/use-user' import {api} from 'web/lib/api' import {useT} from 'web/lib/locale' import {updateReactionUI} from 'web/lib/supabase/chat-messages' import {handleReaction} from 'web/lib/util/message-reactions' const REACTIONS = ['👍', '❤️', '😂', '😮', '😢', '👎'] export function MessageActions(props: { message: { id: number userId: string content: JSONContent isEdited?: boolean reactions?: Record } onRequestEdit?: () => void setMessages?: Dispatch> className?: string // If provided, when this key changes, the emoji picker will open openEmojiPickerKey?: number // If true, hide the trigger menu button and only render the picker anchor hideTrigger?: boolean }) { const {message, onRequestEdit, className, setMessages, openEmojiPickerKey, hideTrigger} = props const [showEmojiPicker, setShowEmojiPicker] = useState(false) const emojiPickerRef = useRef(null) const user = useUser() const isOwner = user?.id === message.userId const isMobile = useIsMobile() const t = useT() useClickOutside(emojiPickerRef, () => { setShowEmojiPicker(false) }) // Open emoji picker when external key changes useEffect(() => { if (openEmojiPickerKey !== undefined) { setShowEmojiPicker(true) } }, [openEmojiPickerKey]) const handleDelete = async () => { if (!confirm(t('messages.delete_confirm', 'Are you sure you want to delete this message?'))) return const messageId = message.id try { await api('delete-message', {messageId}) toast.success(t('messages.deleted', 'Message deleted')) setMessages?.((prevMessages) => { if (!prevMessages) return prevMessages return prevMessages.filter((m) => m.id !== messageId) }) } catch (error) { toast.error(t('messages.delete_failed', 'Failed to delete message')) console.error(error) } } return (
{showEmojiPicker && (
{REACTIONS.map((reaction) => ( ))}
)} {!hideTrigger && ( , onClick: onRequestEdit, }, isOwner && { name: t('messages.action.delete', 'Delete'), icon: , onClick: handleDelete, }, { name: t('messages.action.add_reaction', 'Add Reaction'), icon: , onClick: () => { setShowEmojiPicker(!showEmojiPicker) }, }, ].filter(Boolean) as DropdownItem[] } closeOnClick={true} icon={} menuWidth="w-40" className="text-ink-500 hover:text-ink-700 rounded-full p-1 hover:bg-canvas-25" /> )} {/*{message.isEdited && (*/} {/* {t('messages.edited', 'edited')}*/} {/*)}*/}
) }