@@ -4,36 +4,70 @@ import {
44 Dropdown ,
55 DropdownMenu ,
66 DropdownItem ,
7- DropdownTrigger
7+ DropdownTrigger ,
8+ Tooltip ,
9+ Avatar ,
10+ DropdownSection
811} from '@heroui/react'
912import { CornerDownRight , Trash2 , Edit } from 'lucide-react'
10- import type { Key } from 'react'
13+ import { useMemo , type Key } from 'react'
14+ // import { formatDate } from '~/utils/time'
15+ import type { ChatMessageReaction } from '~/types/api/chat'
1116
1217interface Props {
1318 isMenuOpen : boolean
1419 onClose : ( ) => void
1520 anchorPoint : { x : number ; y : number }
1621 onReaction : ( emoji : string ) => void
22+ reactionArray : ChatMessageReaction [ ]
1723 onDelete ?: ( ) => void
1824 onReply ?: ( ) => void
1925 onEdit ?: ( ) => void
2026 isOwner ?: boolean
2127}
2228
23- const commonReactions = [ '🥰' , '👍' , '❤️' , '🤨' , '🙄' ]
29+ const commonReactions = [
30+ '🥰' ,
31+ '👍' ,
32+ '❤️' ,
33+ '🤨' ,
34+ '🙄' ,
35+ '😎' ,
36+ '😱' ,
37+ '😭' ,
38+ '🔥' ,
39+ '🎉'
40+ ]
2441
2542export const ChatMessageContextMenu = ( {
2643 isMenuOpen,
2744 onClose,
2845 anchorPoint,
2946 onReaction,
47+ reactionArray,
3048 onDelete,
3149 onReply,
3250 onEdit,
3351 isOwner = false
3452} : Props ) => {
3553 if ( ! isMenuOpen ) return null
3654
55+ const groupedReactions = useMemo ( ( ) => {
56+ if ( ! reactionArray || reactionArray . length === 0 ) {
57+ return { }
58+ }
59+ return reactionArray . reduce (
60+ ( acc , reaction ) => {
61+ if ( ! acc [ reaction . emoji ] ) {
62+ acc [ reaction . emoji ] = [ ]
63+ }
64+ acc [ reaction . emoji ] . push ( reaction )
65+ return acc
66+ } ,
67+ { } as Record < string , ChatMessageReaction [ ] >
68+ )
69+ } , [ reactionArray ] )
70+
3771 const handleMenuAction = ( key : Key ) => {
3872 switch ( key ) {
3973 case 'reply' :
@@ -65,42 +99,76 @@ export const ChatMessageContextMenu = ({
6599 textValue = "Reactions"
66100 isReadOnly
67101 key = "reactions"
68- className = "gap-1 cursor-default data-[hover=true]:bg-background"
69- >
70- { commonReactions . map ( ( emoji ) => (
71- < span
72- key = { emoji }
73- className = "cursor-pointer p-1 rounded-full text-lg hover:bg-default-200"
74- onClick = { ( ) => handleReactionClick ( emoji ) }
75- >
76- { emoji }
77- </ span >
78- ) ) }
79- </ DropdownItem >
80-
81- < DropdownItem
82- key = "reply"
83- startContent = { < CornerDownRight className = "size-4" /> }
102+ className = "gap-1 px-0 cursor-default data-[hover=true]:bg-background"
84103 >
85- 回复
104+ < div className = "grid grid-cols-5" >
105+ { commonReactions . map ( ( emoji ) => (
106+ < span
107+ key = { emoji }
108+ className = "cursor-pointer p-1 flex justify-center items-center rounded-full text-lg hover:bg-default-200"
109+ onClick = { ( ) => handleReactionClick ( emoji ) }
110+ >
111+ { emoji }
112+ </ span >
113+ ) ) }
114+ </ div >
86115 </ DropdownItem >
87116
88- { isOwner ? (
89- < DropdownItem key = "edit" startContent = { < Edit className = "size-4" /> } >
90- 编辑
91- </ DropdownItem >
92- ) : null }
93-
94- { isOwner ? (
117+ < DropdownSection showDivider title = "消息操作" >
95118 < DropdownItem
96- key = "delete"
97- className = "text-danger"
98- color = "danger"
99- startContent = { < Trash2 className = "size-4" /> }
119+ key = "reply"
120+ startContent = { < CornerDownRight className = "size-4" /> }
100121 >
101- 删除
122+ 回复
102123 </ DropdownItem >
103- ) : null }
124+
125+ { isOwner ? (
126+ < DropdownItem
127+ key = "edit"
128+ startContent = { < Edit className = "size-4" /> }
129+ >
130+ 编辑
131+ </ DropdownItem >
132+ ) : null }
133+
134+ { isOwner ? (
135+ < DropdownItem
136+ key = "delete"
137+ className = "text-danger"
138+ color = "danger"
139+ startContent = { < Trash2 className = "size-4" /> }
140+ >
141+ 删除
142+ </ DropdownItem >
143+ ) : null }
144+ </ DropdownSection >
145+
146+ < DropdownSection title = "回应表情" >
147+ < >
148+ { Object . entries ( groupedReactions ) . map ( ( [ emoji , reactions ] ) => (
149+ < DropdownItem
150+ key = { `reaction-detail-${ emoji } ` }
151+ isReadOnly
152+ startContent = { < span className = "text-xs" > { emoji } </ span > }
153+ endContent = {
154+ < span className = "text-xs text-default-600" >
155+ { reactions . length }
156+ </ span >
157+ }
158+ className = "cursor-default data-[hover=true]:bg-background"
159+ textValue = { `${ emoji } ${ reactions . length } ` }
160+ >
161+ < span className = "text-xs" > { reactions [ 0 ] . user . name } </ span >
162+ { reactions . length > 1 && (
163+ < span className = "text-xs text-default-500" >
164+ { ' ' }
165+ 等 { reactions . length - 1 } 人
166+ </ span >
167+ ) }
168+ </ DropdownItem >
169+ ) ) }
170+ </ >
171+ </ DropdownSection >
104172 </ DropdownMenu >
105173 </ Dropdown >
106174 </ div >
0 commit comments