gitdataai/libs/transport/unread.rs

144 lines
4.2 KiB
Rust

use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnreadCount {
pub room_id: Uuid,
pub user_id: Uuid,
pub count: i64,
pub last_read_seq: i64,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
#[allow(dead_code)]
#[derive(Clone)]
pub struct UnreadManager {
db: db::database::AppDatabase,
cache: db::cache::AppCache,
}
impl UnreadManager {
pub fn new(db: db::database::AppDatabase, cache: db::cache::AppCache) -> Self {
Self { db, cache }
}
pub async fn mark_read(
&self,
user_id: Uuid,
room_id: Uuid,
seq: i64,
) -> Result<(), crate::error::AppTransportError> {
use models::rooms::room_user_state;
use sea_orm::*;
let state = room_user_state::Entity::find_by_id((room_id, user_id))
.one(&self.db)
.await
.map_err(|_| crate::error::AppTransportError::Internal)?;
if let Some(existing) = state {
let mut active: room_user_state::ActiveModel = existing.into();
active.last_read_seq = Set(Some(seq));
active
.update(&self.db)
.await
.map_err(|_| crate::error::AppTransportError::Internal)?;
} else {
// Lazy create
room_user_state::ActiveModel {
room: Set(room_id),
user: Set(user_id),
last_read_seq: Set(Some(seq)),
do_not_disturb: Set(false),
dnd_start_hour: Set(None),
dnd_end_hour: Set(None),
joined_at: Set(Some(chrono::Utc::now())),
}
.insert(&self.db)
.await
.map_err(|_| crate::error::AppTransportError::Internal)?;
}
self.invalidate_cache(user_id, room_id).await?;
Ok(())
}
pub async fn get_unread_count(
&self,
user_id: Uuid,
room_id: Uuid,
) -> Result<i64, crate::error::AppTransportError> {
use models::rooms::{room_message, room_user_state};
use sea_orm::*;
let state = room_user_state::Entity::find_by_id((room_id, user_id))
.one(&self.db)
.await
.map_err(|_| crate::error::AppTransportError::Internal)?;
let last_read_seq = state.and_then(|s| s.last_read_seq).unwrap_or(0);
let count = room_message::Entity::find()
.filter(room_message::Column::Room.eq(room_id))
.filter(room_message::Column::Seq.gt(last_read_seq))
.count(&self.db)
.await
.map_err(|_| crate::error::AppTransportError::Internal)?;
Ok(count as i64)
}
pub async fn get_all_unread_counts(
&self,
user_id: Uuid,
) -> Result<Vec<UnreadCount>, crate::error::AppTransportError> {
use models::rooms::{room_message, room_user_state};
use sea_orm::*;
let states = room_user_state::Entity::find()
.filter(room_user_state::Column::User.eq(user_id))
.all(&self.db)
.await
.map_err(|_| crate::error::AppTransportError::Internal)?;
if states.is_empty() {
return Ok(Vec::new());
}
let now = chrono::Utc::now();
let mut result = Vec::new();
for state in states {
let last_read_seq = state.last_read_seq.unwrap_or(0);
let count = room_message::Entity::find()
.filter(room_message::Column::Room.eq(state.room))
.filter(room_message::Column::Seq.gt(last_read_seq))
.count(&self.db)
.await
.map_err(|_| crate::error::AppTransportError::Internal)?;
if count > 0 {
result.push(UnreadCount {
room_id: state.room,
user_id,
count: count as i64,
last_read_seq,
updated_at: now,
});
}
}
Ok(result)
}
async fn invalidate_cache(
&self,
_user_id: Uuid,
_room_id: Uuid,
) -> Result<(), crate::error::AppTransportError> {
Ok(())
}
}