From 3859659b13592f7704d156475434d334675cb526 Mon Sep 17 00:00:00 2001 From: Brunobrno Date: Thu, 4 Jun 2026 00:14:34 +0200 Subject: [PATCH] Add emoji picker, reactions user_id & UI tweaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce client-side emoji picker and richer reaction UX, and propagate user IDs for reactions over the WS protocol. Backend: include user_id in reaction events. Frontend: add EmojiPicker component; update useChatSocket types to include user_id; handle "reaction" events in ChatRoomPage to update cached messages (add/remove/switch reactions). Message component: UI overhaul โ€” action menu with slide-out actions, toggle button (โ‹ฎโ†’โœ•), emoji picker trigger, grouped reaction buttons that show counts and indicate "mine" state, and various layout/cleanups. Media: defer video loading by setting preload="none" in ChatMediaGallery, MediaGallery and PostComposer to improve performance. --- backend/social/chat/consumers.py | 4 +- .../src/components/social/EmojiPicker.tsx | 42 ++++ .../social/chat/ChatMediaGallery.tsx | 3 +- .../src/components/social/chat/Message.tsx | 188 +++++++++++------- .../components/social/posts/MediaGallery.tsx | 3 +- .../components/social/posts/PostComposer.tsx | 2 +- frontend/src/hooks/useChatSocket.ts | 2 +- .../src/pages/social/chat/ChatRoomPage.tsx | 27 ++- 8 files changed, 193 insertions(+), 78 deletions(-) create mode 100644 frontend/src/components/social/EmojiPicker.tsx diff --git a/backend/social/chat/consumers.py b/backend/social/chat/consumers.py index 077faf0..be15191 100644 --- a/backend/social/chat/consumers.py +++ b/backend/social/chat/consumers.py @@ -84,7 +84,8 @@ class ChatConsumer(AsyncWebsocketConsumer): "message_id": data["message_id"], "emoji": data["emoji"], "user": user.username, - "action": action, # 'added' | 'removed' | 'switched' + "user_id": user.id, + "action": action, }) elif msg_type == "typing": @@ -158,6 +159,7 @@ class ChatConsumer(AsyncWebsocketConsumer): "message_id": event["message_id"], "emoji": event["emoji"], "user": event["user"], + "user_id": event["user_id"], "action": event["action"], })) diff --git a/frontend/src/components/social/EmojiPicker.tsx b/frontend/src/components/social/EmojiPicker.tsx new file mode 100644 index 0000000..2574682 --- /dev/null +++ b/frontend/src/components/social/EmojiPicker.tsx @@ -0,0 +1,42 @@ +import { useEffect, useRef } from "react"; + +const EMOJIS = ["๐Ÿ‘", "โค๏ธ", "๐Ÿ˜‚", "๐Ÿ˜ฎ", "๐Ÿ˜ข", "๐Ÿ”ฅ", "๐Ÿ‘", "๐ŸŽ‰"]; + +interface Props { + onSelect: (emoji: string) => void; + onClose: () => void; + className?: string; +} + +export default function EmojiPicker({ onSelect, onClose, className }: Props) { + const ref = useRef(null); + + useEffect(() => { + function handleOutside(e: MouseEvent) { + if (ref.current && !ref.current.contains(e.target as Node)) onClose(); + } + document.addEventListener("mousedown", handleOutside); + return () => document.removeEventListener("mousedown", handleOutside); + }, [onClose]); + + return ( +
+ {EMOJIS.map((e) => ( + + ))} +
+ ); +} diff --git a/frontend/src/components/social/chat/ChatMediaGallery.tsx b/frontend/src/components/social/chat/ChatMediaGallery.tsx index 86a5e0f..7ea5371 100644 --- a/frontend/src/components/social/chat/ChatMediaGallery.tsx +++ b/frontend/src/components/social/chat/ChatMediaGallery.tsx @@ -122,6 +122,7 @@ function ChatLightboxContent({ file }: { file: MessageFile }) {