Add Slack-style emoji reactions to comments and issue descriptions with full-stack support: database tables, REST API endpoints, real-time WebSocket sync, optimistic UI updates, and inbox notifications. - New `comment_reaction` and `issue_reaction` tables with migrations - POST/DELETE endpoints for adding/removing reactions on both comments and issue descriptions - Real-time WS events (reaction:added/removed, issue_reaction:added/removed) - Shared ReactionBar component with quick emoji picker and full emoji-mart picker (lazy-loaded) - Optimistic add/remove with rollback on failure - Inbox notifications for comment author and issue creator when reacted to - Reactions included in timeline, comment list, and issue detail responses
42 lines
1,015 B
TypeScript
42 lines
1,015 B
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef, useCallback } from "react";
|
|
import data from "@emoji-mart/data";
|
|
import { Picker } from "emoji-mart";
|
|
|
|
interface EmojiPickerProps {
|
|
onSelect: (emoji: string) => void;
|
|
}
|
|
|
|
export function EmojiPicker({ onSelect }: EmojiPickerProps) {
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
const onSelectRef = useRef(onSelect);
|
|
onSelectRef.current = onSelect;
|
|
|
|
const handleSelect = useCallback((emoji: { native: string }) => {
|
|
onSelectRef.current(emoji.native);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const container = containerRef.current;
|
|
if (!container) return;
|
|
|
|
const picker = new Picker({
|
|
data,
|
|
onEmojiSelect: handleSelect,
|
|
theme: "auto",
|
|
set: "native",
|
|
previewPosition: "none",
|
|
skinTonePosition: "search",
|
|
maxFrequentRows: 2,
|
|
});
|
|
|
|
container.appendChild(picker as unknown as Node);
|
|
|
|
return () => {
|
|
container.replaceChildren();
|
|
};
|
|
}, [handleSelect]);
|
|
|
|
return <div ref={containerRef} />;
|
|
}
|