- Add gitignore and prettier configuration files for project scaffolding - Implement room access control service with project member verification - Create user access key management with CRUD operations and activity logging - Add accordion UI component for frontend expandable sections - Implement room AI configuration with list, upsert, and delete operations - Add AI event types for agent join/leave/status change tracking - Create streaming AI processing services for mode and react patterns - Build room AI service with model detection and idempotency handling - Integrate chat service orchestration for AI message processing - Add typing indicators and stream cancellation for AI interactions - Implement mention parsing and context extraction for AI agents
105 lines
3.5 KiB
Rust
105 lines
3.5 KiB
Rust
use crate::error::RoomError;
|
|
use crate::service::RoomService;
|
|
use crate::ws_context::WsUserContext;
|
|
use chrono::Utc;
|
|
use models::rooms::room_message_reaction;
|
|
use queue::ReactionGroup;
|
|
use sea_orm::*;
|
|
use uuid::Uuid;
|
|
|
|
impl RoomService {
|
|
pub async fn message_reaction_add(
|
|
&self,
|
|
message_id: Uuid,
|
|
emoji: String,
|
|
ctx: &WsUserContext,
|
|
) -> Result<super::reaction::MessageReactionsResponse, RoomError> {
|
|
let user_id = ctx.user_id;
|
|
let message = self.find_message_or_404(message_id).await?;
|
|
self.require_room_access(message.room, user_id).await?;
|
|
Self::validate_emoji(&emoji)?;
|
|
|
|
let txn = self.db.begin().await?;
|
|
let existing = room_message_reaction::Entity::find()
|
|
.filter(room_message_reaction::Column::Message.eq(message_id))
|
|
.filter(room_message_reaction::Column::User.eq(user_id))
|
|
.filter(room_message_reaction::Column::Emoji.eq(&emoji))
|
|
.one(&txn)
|
|
.await?;
|
|
|
|
if existing.is_some() {
|
|
txn.commit().await?;
|
|
return self.get_message_reactions(message_id, Some(user_id)).await;
|
|
}
|
|
|
|
let reaction = room_message_reaction::ActiveModel {
|
|
id: Set(Uuid::now_v7()),
|
|
room: Set(message.room),
|
|
message: Set(message_id),
|
|
user: Set(user_id),
|
|
emoji: Set(emoji.clone()),
|
|
created_at: Set(Utc::now()),
|
|
};
|
|
room_message_reaction::Entity::insert(reaction)
|
|
.exec(&txn)
|
|
.await?;
|
|
txn.commit().await?;
|
|
|
|
let reactions = self
|
|
.get_message_reactions(message_id, Some(user_id))
|
|
.await?;
|
|
let groups = reactions
|
|
.reactions
|
|
.iter()
|
|
.map(|g| ReactionGroup {
|
|
emoji: g.emoji.clone(),
|
|
count: g.count as i32,
|
|
reacted_by_me: g.reacted_by_me,
|
|
users: g.users.iter().filter_map(|u| u.parse().ok()).collect(),
|
|
})
|
|
.collect();
|
|
self.queue
|
|
.publish_reaction_event(message.room, message_id, groups)
|
|
.await;
|
|
|
|
self.get_message_reactions(message_id, Some(user_id)).await
|
|
}
|
|
|
|
pub async fn message_reaction_remove(
|
|
&self,
|
|
message_id: Uuid,
|
|
emoji: String,
|
|
ctx: &WsUserContext,
|
|
) -> Result<super::reaction::MessageReactionsResponse, RoomError> {
|
|
let user_id = ctx.user_id;
|
|
let message = self.find_message_or_404(message_id).await?;
|
|
self.require_room_access(message.room, user_id).await?;
|
|
|
|
room_message_reaction::Entity::delete_many()
|
|
.filter(room_message_reaction::Column::Message.eq(message_id))
|
|
.filter(room_message_reaction::Column::User.eq(user_id))
|
|
.filter(room_message_reaction::Column::Emoji.eq(&emoji))
|
|
.exec(&self.db)
|
|
.await?;
|
|
|
|
let reactions = self
|
|
.get_message_reactions(message_id, Some(user_id))
|
|
.await?;
|
|
let groups = reactions
|
|
.reactions
|
|
.iter()
|
|
.map(|g| ReactionGroup {
|
|
emoji: g.emoji.clone(),
|
|
count: g.count as i32,
|
|
reacted_by_me: g.reacted_by_me,
|
|
users: g.users.iter().filter_map(|u| u.parse().ok()).collect(),
|
|
})
|
|
.collect();
|
|
self.queue
|
|
.publish_reaction_event(message.room, message_id, groups)
|
|
.await;
|
|
|
|
self.get_message_reactions(message_id, Some(user_id)).await
|
|
}
|
|
}
|