-
Notifications
You must be signed in to change notification settings - Fork 163
Expand file tree
/
Copy pathEmojiReactionButton.tsx
More file actions
107 lines (93 loc) · 3.52 KB
/
Copy pathEmojiReactionButton.tsx
File metadata and controls
107 lines (93 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import * as React from 'react';
import { useRoomContext } from '../../context';
import { setupDataMessageHandler } from '@livekit/components-core';
import { DataTopic } from '@livekit/components-core';
import { mergeProps } from '../../utils';
import { useEmojiReactionContext } from '../../context/EmojiReactionContext';
const EMOJI_OPTIONS = ['👍', '👎', '❤️', '😂', '😮', '😢', '👏', '🎉'];
export interface EmojiReactionButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
showIcon?: boolean;
showText?: boolean;
}
export function EmojiReactionButton({
showIcon = true,
showText = false,
...props
}: EmojiReactionButtonProps) {
const room = useRoomContext();
const { addReaction } = useEmojiReactionContext();
const [isOpen, setIsOpen] = React.useState(false);
const buttonRef = React.useRef<HTMLButtonElement>(null);
const popupRef = React.useRef<HTMLDivElement>(null);
const { send, isSendingObservable } = React.useMemo(
() => setupDataMessageHandler(room, DataTopic.REACTIONS),
[room],
);
const [isSending, setIsSending] = React.useState(false);
React.useEffect(() => {
const subscription = isSendingObservable.subscribe(setIsSending);
return () => subscription.unsubscribe();
}, [isSendingObservable]);
React.useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
buttonRef.current &&
popupRef.current &&
!buttonRef.current.contains(event.target as Node) &&
!popupRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
const handleEmojiClick = React.useCallback(
async (emoji: string) => {
if (isSending) return;
try {
const payload = new TextEncoder().encode(JSON.stringify({ emoji }));
await send(payload);
// Add local reaction immediately
addReaction({
emoji,
from: room.localParticipant,
});
// Don't close the popup - let user send multiple reactions
} catch (error) {
console.error('Failed to send emoji reaction:', error);
}
},
[send, isSending, room.localParticipant, addReaction],
);
const htmlProps = mergeProps(
{
className: 'lk-button lk-emoji-reaction-button',
onClick: () => setIsOpen(!isOpen),
disabled: isSending,
},
props,
);
return (
<div className="lk-button-group">
<button ref={buttonRef} {...htmlProps}>
{showIcon && <span>😊</span>}
{showText && 'Reactions'}
</button>
{isOpen && (
<div ref={popupRef} className="lk-emoji-popup">
{EMOJI_OPTIONS.map((emoji) => (
<button
key={emoji}
className="lk-emoji-option"
onClick={() => handleEmojiClick(emoji)}
disabled={isSending}
>
{emoji}
</button>
))}
</div>
)}
</div>
);
}