fix(room): align ReactionGroup types with frontend and guard reaction update handler
- Fix ReactionGroup.count: i64 -> i32 and users: Vec<Uuid> -> Vec<String> 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.
This commit is contained in:
parent
047782e585
commit
a171d691c6
@ -449,9 +449,9 @@ impl From<room::MessageReactionsResponse> for ReactionListData {
|
|||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub struct ReactionItem {
|
pub struct ReactionItem {
|
||||||
pub emoji: String,
|
pub emoji: String,
|
||||||
pub count: i64,
|
pub count: i32,
|
||||||
pub reacted_by_me: bool,
|
pub reacted_by_me: bool,
|
||||||
pub users: Vec<Uuid>,
|
pub users: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
|||||||
@ -40,9 +40,10 @@ pub struct RoomMessageEvent {
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ReactionGroup {
|
pub struct ReactionGroup {
|
||||||
pub emoji: String,
|
pub emoji: String,
|
||||||
pub count: i64,
|
pub count: i32,
|
||||||
pub reacted_by_me: bool,
|
pub reacted_by_me: bool,
|
||||||
pub users: Vec<Uuid>,
|
/// Stored as strings (UUIDs) to match the frontend's `users: string[]` type.
|
||||||
|
pub users: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RoomMessageEnvelope> for RoomMessageEvent {
|
impl From<RoomMessageEnvelope> for RoomMessageEvent {
|
||||||
|
|||||||
@ -11,9 +11,9 @@ use uuid::Uuid;
|
|||||||
#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
|
#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
|
||||||
pub struct ReactionGroupResponse {
|
pub struct ReactionGroupResponse {
|
||||||
pub emoji: String,
|
pub emoji: String,
|
||||||
pub count: i64,
|
pub count: i32,
|
||||||
pub reacted_by_me: bool,
|
pub reacted_by_me: bool,
|
||||||
pub users: Vec<Uuid>,
|
pub users: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
|
#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
|
||||||
@ -83,9 +83,9 @@ impl RoomService {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|g| ReactionGroup {
|
.map(|g| ReactionGroup {
|
||||||
emoji: g.emoji,
|
emoji: g.emoji,
|
||||||
count: g.count,
|
count: g.count as i32,
|
||||||
reacted_by_me: g.reacted_by_me,
|
reacted_by_me: g.reacted_by_me,
|
||||||
users: g.users,
|
users: g.users.into_iter().map(|u| u.to_string()).collect(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
self.queue
|
self.queue
|
||||||
@ -121,9 +121,9 @@ impl RoomService {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|g| ReactionGroup {
|
.map(|g| ReactionGroup {
|
||||||
emoji: g.emoji,
|
emoji: g.emoji,
|
||||||
count: g.count,
|
count: g.count as i32,
|
||||||
reacted_by_me: g.reacted_by_me,
|
reacted_by_me: g.reacted_by_me,
|
||||||
users: g.users,
|
users: g.users.into_iter().map(|u| u.to_string()).collect(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
self.queue
|
self.queue
|
||||||
@ -258,11 +258,15 @@ impl RoomService {
|
|||||||
grouped
|
grouped
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(emoji, user_reactions)| {
|
.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
|
let reacted_by_me = current_user_id
|
||||||
.map(|uid| user_reactions.iter().any(|r| r.user == uid))
|
.map(|uid| user_reactions.iter().any(|r| r.user == uid))
|
||||||
.unwrap_or(false);
|
.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 {
|
ReactionGroupResponse {
|
||||||
emoji,
|
emoji,
|
||||||
|
|||||||
@ -517,15 +517,18 @@ export function RoomProvider({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRoomReactionUpdated: (payload: RoomReactionUpdatedPayload) => {
|
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) => {
|
setMessages((prev) => {
|
||||||
const updated = prev.map((m) =>
|
const existingIdx = prev.findIndex((m) => m.id === payload.message_id);
|
||||||
m.id === payload.message_id
|
if (existingIdx === -1) return prev;
|
||||||
? { ...m, reactions: payload.reactions }
|
const updated = [...prev];
|
||||||
: m,
|
updated[existingIdx] = { ...updated[existingIdx], reactions: payload.reactions };
|
||||||
);
|
|
||||||
// Persist reaction update to IndexedDB
|
// Persist reaction update to IndexedDB
|
||||||
const msg = updated.find((m) => m.id === payload.message_id);
|
saveMessage(updated[existingIdx]).catch(() => {});
|
||||||
if (msg) saveMessage(msg).catch(() => {});
|
|
||||||
return updated;
|
return updated;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user