use chrono::Utc; use uuid::Uuid; use crate::event::{RoomInfo, UserInfo, reaction}; use crate::{ChannelBus, ChannelError, ChannelResult}; use super::WsHandler; use super::WsOutEvent; impl WsHandler { pub(super) async fn reaction_add( bus: &ChannelBus, user_id: Uuid, room: Uuid, message: Uuid, emoji: String, ) -> ChannelResult> { Self::ensure_room_access(bus, user_id, room).await?; Self::ensure_message_in_room(bus, room, message).await?; if emoji.is_empty() || emoji.len() > 100 { return Err(ChannelError::Validation("invalid emoji".into())); } let seq = bus.inner.seq.seq(room).await?; let result = db::sqlx::query( "INSERT INTO room_reaction (message, \"user\", seq, reaction, created_at) \ VALUES ($1, $2, $3, $4, now()) \ ON CONFLICT DO NOTHING", ) .bind(message) .bind(user_id) .bind(seq) .bind(&emoji) .execute(bus.inner.db.writer()) .await?; if result.rows_affected() == 0 { return Ok(None); } let user = bus .lookup_user(user_id) .await .unwrap_or_else(|_| UserInfo::unknown(user_id)); let rct_room = bus .lookup_room(room) .await .unwrap_or_else(|_| RoomInfo::unknown(room)); let data = reaction::ReactionAddedService { id: Uuid::now_v7(), room: rct_room, message, user, emoji, created_at: Utc::now(), }; bus.publish_room_event(room, "reaction.added", &data) .await?; Ok(Some(WsOutEvent::ReactionAdded { room: data.room.clone(), data, })) } pub(super) async fn reaction_remove( bus: &ChannelBus, user_id: Uuid, room: Uuid, message: Uuid, emoji: String, ) -> ChannelResult> { Self::ensure_room_access(bus, user_id, room).await?; Self::ensure_message_in_room(bus, room, message).await?; let result = db::sqlx::query( "DELETE FROM room_reaction WHERE message = $1 AND \"user\" = $2 AND reaction = $3", ) .bind(message) .bind(user_id) .bind(&emoji) .execute(bus.inner.db.writer()) .await?; if result.rows_affected() == 0 { return Ok(None); } let user = bus .lookup_user(user_id) .await .unwrap_or_else(|_| UserInfo::unknown(user_id)); let rct_room = bus .lookup_room(room) .await .unwrap_or_else(|_| RoomInfo::unknown(room)); let data = reaction::ReactionRemovedService { id: Uuid::now_v7(), room: rct_room, message, user, emoji, removed_at: Utc::now(), }; bus.publish_room_event(room, "reaction.removed", &data) .await?; Ok(Some(WsOutEvent::ReactionRemoved { room: data.room.clone(), data, })) } }