144 lines
4.2 KiB
Rust
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(())
|
|
}
|
|
}
|