nekrochan/src/live_hub.rs

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(),
));
}
}
}