179 lines
5.7 KiB
Rust
179 lines
5.7 KiB
Rust
use chrono::Utc;
|
|
use uuid::Uuid;
|
|
|
|
use crate::event::{RoomInfo, UserInfo, member};
|
|
use crate::{ChannelBus, ChannelResult};
|
|
|
|
use super::WsHandler;
|
|
use super::WsOutEvent;
|
|
|
|
impl WsHandler {
|
|
pub(super) async fn subscribe(
|
|
bus: &ChannelBus,
|
|
user_id: Uuid,
|
|
_room: Uuid,
|
|
) -> ChannelResult<Option<WsOutEvent>> {
|
|
bus.refresh_user(user_id).await?;
|
|
Ok(None)
|
|
}
|
|
|
|
pub(super) async fn unsubscribe(
|
|
_bus: &ChannelBus,
|
|
_user_id: Uuid,
|
|
_room: Uuid,
|
|
) -> ChannelResult<Option<WsOutEvent>> {
|
|
Ok(None)
|
|
}
|
|
|
|
pub(super) async fn typing(
|
|
bus: &ChannelBus,
|
|
room: Uuid,
|
|
user_id: Uuid,
|
|
action: &str,
|
|
) -> ChannelResult<Option<WsOutEvent>> {
|
|
Self::ensure_room_access(bus, user_id, room).await?;
|
|
|
|
let key = (room, user_id);
|
|
|
|
if action == "start" {
|
|
let ty_room = bus
|
|
.lookup_room(room)
|
|
.await
|
|
.unwrap_or_else(|_| RoomInfo::unknown(room));
|
|
let ty_user = bus
|
|
.lookup_user(user_id)
|
|
.await
|
|
.unwrap_or_else(|_| UserInfo::unknown(user_id));
|
|
let already_typing = bus.inner.typing_states.contains_key(&key);
|
|
if let Some((_, (_, _, old_cancel))) =
|
|
bus.inner.typing_states.remove(&key)
|
|
{
|
|
old_cancel.cancel();
|
|
}
|
|
|
|
let cancel = tokio_util::sync::CancellationToken::new();
|
|
let cancel_clone = cancel.clone();
|
|
let bus_clone = bus.clone();
|
|
let user_clone = ty_user.clone();
|
|
let room_clone = ty_room.clone();
|
|
bus.inner
|
|
.typing_states
|
|
.insert(key, (ty_user.clone(), ty_room.clone(), cancel));
|
|
tokio::spawn(async move {
|
|
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
|
|
if cancel_clone.is_cancelled() {
|
|
return;
|
|
}
|
|
bus_clone
|
|
.inner
|
|
.typing_states
|
|
.remove(&(room_clone.id, user_clone.id));
|
|
let room_id = room_clone.id;
|
|
let stop_data = member::TypingStopService {
|
|
room: room_clone,
|
|
user: user_clone,
|
|
sender_type: "user".to_string(),
|
|
stopped_at: Utc::now(),
|
|
};
|
|
let _ = bus_clone
|
|
.publish_room_event(room_id, "typing.stop", &stop_data)
|
|
.await;
|
|
});
|
|
if !already_typing {
|
|
let data = member::TypingStartService {
|
|
room: ty_room,
|
|
user: ty_user,
|
|
sender_type: "user".to_string(),
|
|
started_at: Utc::now(),
|
|
};
|
|
bus.publish_room_event(room, "typing.start", &data).await?;
|
|
return Ok(Some(WsOutEvent::TypingStart {
|
|
room: data.room.clone(),
|
|
data,
|
|
}));
|
|
}
|
|
Ok(None)
|
|
} else {
|
|
if let Some((_, (_, _, cancel))) =
|
|
bus.inner.typing_states.remove(&key)
|
|
{
|
|
cancel.cancel();
|
|
}
|
|
|
|
let ty_room = bus
|
|
.lookup_room(room)
|
|
.await
|
|
.unwrap_or_else(|_| RoomInfo::unknown(room));
|
|
let ty_user = bus
|
|
.lookup_user(user_id)
|
|
.await
|
|
.unwrap_or_else(|_| UserInfo::unknown(user_id));
|
|
|
|
let data = member::TypingStopService {
|
|
room: ty_room,
|
|
user: ty_user,
|
|
sender_type: "user".to_string(),
|
|
stopped_at: Utc::now(),
|
|
};
|
|
bus.publish_room_event(room, "typing.stop", &data).await?;
|
|
Ok(Some(WsOutEvent::TypingStop {
|
|
room: data.room.clone(),
|
|
data,
|
|
}))
|
|
}
|
|
}
|
|
|
|
pub(super) async fn read_receipt(
|
|
bus: &ChannelBus,
|
|
user_id: Uuid,
|
|
room: Uuid,
|
|
last_read_seq: i64,
|
|
) -> ChannelResult<Option<WsOutEvent>> {
|
|
Self::ensure_room_access(bus, user_id, room).await?;
|
|
bus.inner
|
|
.reconnect
|
|
.save_client_state(user_id, room, last_read_seq)
|
|
.await?;
|
|
db::sqlx::query(
|
|
"INSERT INTO user_room_state (\"user\", room, last_read_seq, last_read_at, updated_at) \
|
|
VALUES ($1, $2, $3, now(), now()) \
|
|
ON CONFLICT (\"user\", room) DO UPDATE \
|
|
SET last_read_seq = GREATEST(user_room_state.last_read_seq, $3), \
|
|
last_read_at = now(), updated_at = now()",
|
|
)
|
|
.bind(user_id)
|
|
.bind(room)
|
|
.bind(last_read_seq)
|
|
.execute(bus.inner.db.writer())
|
|
.await?;
|
|
let rr_room = bus
|
|
.lookup_room(room)
|
|
.await
|
|
.unwrap_or_else(|_| RoomInfo::unknown(room));
|
|
let rr_user = bus
|
|
.lookup_user(user_id)
|
|
.await
|
|
.unwrap_or_else(|_| UserInfo::unknown(user_id));
|
|
let data = member::ReadReceiptService {
|
|
room: rr_room.clone(),
|
|
user: rr_user,
|
|
last_read_seq,
|
|
updated_at: Utc::now(),
|
|
};
|
|
bus.publish_room_event(room, "member.read_receipt", &data)
|
|
.await?;
|
|
Ok(Some(WsOutEvent::ReadReceipt {
|
|
room: rr_room,
|
|
data,
|
|
}))
|
|
}
|
|
|
|
pub(super) async fn csrf_token(
|
|
bus: &ChannelBus,
|
|
user_id: Uuid,
|
|
) -> ChannelResult<Option<WsOutEvent>> {
|
|
let token = bus.inner.csrf.generate_token(user_id).await?;
|
|
Ok(Some(WsOutEvent::CsrfToken { token }))
|
|
}
|
|
}
|