From a171d691c603cabb16c32acc303ca1b26b7eef2e Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Fri, 17 Apr 2026 22:59:13 +0800 Subject: [PATCH] fix(room): align ReactionGroup types with frontend and guard reaction update handler - Fix ReactionGroup.count: i64 -> i32 and users: Vec -> Vec to match frontend ReactionItem (count: number, users: string[]). Mismatched types caused the WS reaction update to silently fail. Also update ReactionItem in api/ws_types.rs to match. - Add activeRoomIdRef guard in onRoomReactionUpdated to prevent stale room state from processing outdated events after room switch. - Switch from prev.map() to targeted findIndex+spread in onRoomReactionUpdated to avoid unnecessary array recreation. --- libs/api/room/ws_types.rs | 4 ++-- libs/queue/types.rs | 5 +++-- libs/room/src/reaction.rs | 20 ++++++++++++-------- src/contexts/room-context.tsx | 17 ++++++++++------- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/libs/api/room/ws_types.rs b/libs/api/room/ws_types.rs index 98d8fea..020ed80 100644 --- a/libs/api/room/ws_types.rs +++ b/libs/api/room/ws_types.rs @@ -449,9 +449,9 @@ impl From for ReactionListData { #[derive(Debug, Clone, Serialize)] pub struct ReactionItem { pub emoji: String, - pub count: i64, + pub count: i32, pub reacted_by_me: bool, - pub users: Vec, + pub users: Vec, } #[derive(Debug, Clone, Serialize)] diff --git a/libs/queue/types.rs b/libs/queue/types.rs index 3c4cc01..b6d1596 100644 --- a/libs/queue/types.rs +++ b/libs/queue/types.rs @@ -40,9 +40,10 @@ pub struct RoomMessageEvent { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ReactionGroup { pub emoji: String, - pub count: i64, + pub count: i32, pub reacted_by_me: bool, - pub users: Vec, + /// Stored as strings (UUIDs) to match the frontend's `users: string[]` type. + pub users: Vec, } impl From for RoomMessageEvent { diff --git a/libs/room/src/reaction.rs b/libs/room/src/reaction.rs index c08253c..eea5089 100644 --- a/libs/room/src/reaction.rs +++ b/libs/room/src/reaction.rs @@ -11,9 +11,9 @@ use uuid::Uuid; #[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)] pub struct ReactionGroupResponse { pub emoji: String, - pub count: i64, + pub count: i32, pub reacted_by_me: bool, - pub users: Vec, + pub users: Vec, } #[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)] @@ -83,9 +83,9 @@ impl RoomService { .into_iter() .map(|g| ReactionGroup { emoji: g.emoji, - count: g.count, + count: g.count as i32, reacted_by_me: g.reacted_by_me, - users: g.users, + users: g.users.into_iter().map(|u| u.to_string()).collect(), }) .collect(); self.queue @@ -121,9 +121,9 @@ impl RoomService { .into_iter() .map(|g| ReactionGroup { emoji: g.emoji, - count: g.count, + count: g.count as i32, reacted_by_me: g.reacted_by_me, - users: g.users, + users: g.users.into_iter().map(|u| u.to_string()).collect(), }) .collect(); self.queue @@ -258,11 +258,15 @@ impl RoomService { grouped .into_iter() .map(|(emoji, user_reactions)| { - let count = user_reactions.len() as i64; + let count = user_reactions.len() as i32; let reacted_by_me = current_user_id .map(|uid| user_reactions.iter().any(|r| r.user == uid)) .unwrap_or(false); - let users = user_reactions.iter().take(3).map(|r| r.user).collect(); + let users = user_reactions + .iter() + .take(3) + .map(|r| r.user.to_string()) + .collect(); ReactionGroupResponse { emoji, diff --git a/src/contexts/room-context.tsx b/src/contexts/room-context.tsx index 428155b..ccf63a3 100644 --- a/src/contexts/room-context.tsx +++ b/src/contexts/room-context.tsx @@ -517,15 +517,18 @@ export function RoomProvider({ } }, onRoomReactionUpdated: (payload: RoomReactionUpdatedPayload) => { + // Guard: ignore events for rooms that are no longer active. + // Without this, a WS event arriving after room switch could update + // the wrong room's message list (same message ID, different room). + if (!activeRoomIdRef.current) return; + setMessages((prev) => { - const updated = prev.map((m) => - m.id === payload.message_id - ? { ...m, reactions: payload.reactions } - : m, - ); + const existingIdx = prev.findIndex((m) => m.id === payload.message_id); + if (existingIdx === -1) return prev; + const updated = [...prev]; + updated[existingIdx] = { ...updated[existingIdx], reactions: payload.reactions }; // Persist reaction update to IndexedDB - const msg = updated.find((m) => m.id === payload.message_id); - if (msg) saveMessage(msg).catch(() => {}); + saveMessage(updated[existingIdx]).catch(() => {}); return updated; }); },