273 řádky
6.6 KiB
Rust
273 řádky
6.6 KiB
Rust
use actix::{Actor, Context, Handler, Message, Recipient};
|
|
use askama::Template;
|
|
use redis::Connection;
|
|
use serde_json::json;
|
|
use std::collections::HashMap;
|
|
use uuid::Uuid;
|
|
|
|
use crate::{
|
|
db::models::{Board, Post},
|
|
web::tcx::TemplateCtx,
|
|
PostTemplate,
|
|
};
|
|
|
|
#[derive(Message)]
|
|
#[rtype(result = "()")]
|
|
pub enum SessionMessage {
|
|
Data(String),
|
|
Stop,
|
|
}
|
|
|
|
#[derive(Message)]
|
|
#[rtype(result = "()")]
|
|
pub struct ConnectMessage {
|
|
pub uuid: Uuid,
|
|
pub thread: (String, i64),
|
|
pub tcx: TemplateCtx,
|
|
pub recv: Recipient<SessionMessage>,
|
|
}
|
|
|
|
#[derive(Message)]
|
|
#[rtype(result = "()")]
|
|
pub struct DisconnectMessage {
|
|
pub uuid: Uuid,
|
|
pub thread: (String, i64),
|
|
}
|
|
|
|
#[derive(Message)]
|
|
#[rtype(result = "()")]
|
|
pub struct PostCreatedMessage {
|
|
pub post: Post,
|
|
}
|
|
|
|
#[derive(Message)]
|
|
#[rtype(result = "()")]
|
|
pub struct TargetedPostCreatedMessage {
|
|
pub uuid: Uuid,
|
|
pub post: Post,
|
|
}
|
|
|
|
#[derive(Message)]
|
|
#[rtype(result = "()")]
|
|
pub struct PostUpdatedMessage {
|
|
pub post: Post,
|
|
}
|
|
|
|
#[derive(Message)]
|
|
#[rtype(result = "()")]
|
|
pub struct PostRemovedMessage {
|
|
pub post: Post,
|
|
}
|
|
|
|
pub struct LiveHub {
|
|
pub cache: Connection,
|
|
pub recv_by_uuid: HashMap<Uuid, (TemplateCtx, Recipient<SessionMessage>)>,
|
|
pub recv_by_thread: HashMap<(String, i64), Vec<Uuid>>,
|
|
}
|
|
|
|
impl LiveHub {
|
|
pub fn new(cache: Connection) -> Self {
|
|
Self {
|
|
cache,
|
|
recv_by_uuid: HashMap::new(),
|
|
recv_by_thread: HashMap::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Actor for LiveHub {
|
|
type Context = Context<Self>;
|
|
}
|
|
|
|
impl Handler<ConnectMessage> for LiveHub {
|
|
type Result = ();
|
|
|
|
fn handle(&mut self, msg: ConnectMessage, _: &mut Self::Context) -> Self::Result {
|
|
self.recv_by_uuid.insert(msg.uuid, (msg.tcx, msg.recv));
|
|
|
|
match self.recv_by_thread.get_mut(&msg.thread) {
|
|
Some(vec) => {
|
|
vec.push(msg.uuid);
|
|
}
|
|
None => {
|
|
self.recv_by_thread.insert(msg.thread, vec![msg.uuid]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Handler<DisconnectMessage> for LiveHub {
|
|
type Result = ();
|
|
|
|
fn handle(&mut self, msg: DisconnectMessage, _: &mut Self::Context) -> Self::Result {
|
|
self.recv_by_uuid.remove(&msg.uuid);
|
|
|
|
let recv_by_thread = match self.recv_by_thread.get_mut(&msg.thread) {
|
|
Some(recv_by_thread) => recv_by_thread,
|
|
None => return,
|
|
};
|
|
|
|
*recv_by_thread = recv_by_thread
|
|
.iter()
|
|
.filter(|uuid| **uuid != msg.uuid)
|
|
.map(Uuid::clone)
|
|
.collect();
|
|
|
|
if recv_by_thread.is_empty() {
|
|
self.recv_by_thread.remove(&msg.thread);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Handler<PostCreatedMessage> for LiveHub {
|
|
type Result = ();
|
|
|
|
fn handle(&mut self, msg: PostCreatedMessage, _: &mut Self::Context) -> Self::Result {
|
|
let post = msg.post;
|
|
|
|
let uuids = self
|
|
.recv_by_thread
|
|
.get(&(post.board.clone(), post.thread.unwrap_or(post.id)));
|
|
|
|
let uuids = match uuids {
|
|
Some(uuids) => uuids,
|
|
None => return,
|
|
};
|
|
|
|
let Ok(Some(board)) = Board::read_sync(&mut self.cache, post.board.clone()) else {
|
|
return;
|
|
};
|
|
|
|
for uuid in uuids {
|
|
let Some((tcx, recv)) = self.recv_by_uuid.get_mut(uuid) else {
|
|
continue;
|
|
};
|
|
|
|
tcx.update_yous(&mut self.cache).ok();
|
|
|
|
let tcx = &tcx;
|
|
let board = &board;
|
|
let post = &post;
|
|
let id = post.id;
|
|
|
|
let html = PostTemplate { tcx, board, post }
|
|
.render()
|
|
.unwrap_or_default();
|
|
|
|
recv.do_send(SessionMessage::Data(
|
|
json!({ "type": "created", "id": id, "html": html }).to_string(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Handler<TargetedPostCreatedMessage> for LiveHub {
|
|
type Result = ();
|
|
|
|
fn handle(&mut self, msg: TargetedPostCreatedMessage, _: &mut Self::Context) -> Self::Result {
|
|
let post = msg.post;
|
|
|
|
let Ok(Some(board)) = Board::read_sync(&mut self.cache, post.board.clone()) else {
|
|
return;
|
|
};
|
|
|
|
let Some((tcx, recv)) = self.recv_by_uuid.get(&msg.uuid) else {
|
|
return;
|
|
};
|
|
|
|
let id = post.id;
|
|
let tcx = &tcx;
|
|
let board = &board;
|
|
let post = &post;
|
|
|
|
let html = PostTemplate { tcx, board, post }
|
|
.render()
|
|
.unwrap_or_default();
|
|
|
|
recv.do_send(SessionMessage::Data(
|
|
json!({ "type": "created", "id": id, "html": html }).to_string(),
|
|
));
|
|
}
|
|
}
|
|
|
|
impl Handler<PostUpdatedMessage> for LiveHub {
|
|
type Result = ();
|
|
|
|
fn handle(&mut self, msg: PostUpdatedMessage, _: &mut Self::Context) -> Self::Result {
|
|
let post = msg.post;
|
|
|
|
let uuids = self
|
|
.recv_by_thread
|
|
.get(&(post.board.clone(), post.thread.unwrap_or(post.id)));
|
|
|
|
let uuids = match uuids {
|
|
Some(uuids) => uuids,
|
|
None => return,
|
|
};
|
|
|
|
let Ok(Some(board)) = Board::read_sync(&mut self.cache, post.board.clone()) else {
|
|
return;
|
|
};
|
|
|
|
for uuid in uuids {
|
|
let Some((tcx, recv)) = self.recv_by_uuid.get_mut(uuid) else {
|
|
continue;
|
|
};
|
|
|
|
tcx.update_yous(&mut self.cache).ok();
|
|
|
|
let id = post.id;
|
|
let tcx = &tcx;
|
|
let board = &board;
|
|
let post = &post;
|
|
|
|
let html = PostTemplate { tcx, post, board }
|
|
.render()
|
|
.unwrap_or_default();
|
|
|
|
recv.do_send(SessionMessage::Data(
|
|
json!({ "type": "updated", "id": id, "html": html }).to_string(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Handler<PostRemovedMessage> for LiveHub {
|
|
type Result = ();
|
|
|
|
fn handle(&mut self, msg: PostRemovedMessage, _: &mut Self::Context) -> Self::Result {
|
|
let post = msg.post;
|
|
|
|
let uuids = self
|
|
.recv_by_thread
|
|
.get(&(post.board.clone(), post.thread.unwrap_or(post.id)));
|
|
|
|
let uuids = match uuids {
|
|
Some(uuids) => uuids,
|
|
None => return,
|
|
};
|
|
|
|
if post.thread.is_none() {
|
|
for uuid in uuids {
|
|
let Some((_, recv)) = self.recv_by_uuid.get(uuid) else {
|
|
continue;
|
|
};
|
|
|
|
recv.do_send(SessionMessage::Stop);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
for uuid in uuids {
|
|
let Some((_, recv)) = self.recv_by_uuid.get(uuid) else {
|
|
continue;
|
|
};
|
|
|
|
recv.do_send(SessionMessage::Data(
|
|
json!({ "type": "removed", "id": post.id }).to_string(),
|
|
));
|
|
}
|
|
}
|
|
}
|