Testing indicators

This commit is contained in:
2025-12-23 12:24:37 +01:00
parent aa8996d79a
commit fcdf8e2e38
6 changed files with 99 additions and 56 deletions
+30
View File
@@ -0,0 +1,30 @@
use crate::{server::Server, types::message::ServerMessage, utils::client::Client};
use anyhow::Context;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "params", rename_all = "snake_case")]
pub enum Indicator {
Typing { user_id: String, channel_id: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndicatorContext {
pub indicator: Indicator,
pub expires: u16,
}
crate::logger!(LOGGER "Typing Indicator");
pub fn start_typing(server: &Arc<Server>, client: &Client, channel_id: &str) -> crate::Result<()> {
server.broadcast(ServerMessage::Indicator(IndicatorContext {
indicator: Indicator::Typing {
user_id: client.get_uuid().context("Failed to get uuid")?,
channel_id: channel_id.to_string(),
},
expires: 5, // 5 secs
}));
Ok(())
}
+5 -2
View File
@@ -1,5 +1,6 @@
pub mod chunk; pub mod chunk;
pub mod message; pub mod message;
pub mod indicator;
use std::sync::Arc; use std::sync::Arc;
@@ -36,8 +37,10 @@ impl Server {
ClientMessage::LoadChunk { ClientMessage::LoadChunk {
chunk_id, chunk_id,
channel_id, channel_id,
} => { } => chunk::load_chunk(self, client, channel_id, *chunk_id)?,
chunk::load_chunk(self, client, channel_id, *chunk_id)?;
ClientMessage::Typing { channel_id } => {
indicator::start_typing(self, client, channel_id)?
} }
}, },
+6 -1
View File
@@ -44,6 +44,7 @@ pub struct Server {
pub plugins: Mutex<Vec<Plugin>>, pub plugins: Mutex<Vec<Plugin>>,
pub db: utils::database::Database, pub db: utils::database::Database,
pub shutting_down: AtomicBool, pub shutting_down: AtomicBool,
pub indicators: Mutex<Vec<crate::requests::indicator::IndicatorContext>>,
} }
impl Default for ServerConfig { impl Default for ServerConfig {
@@ -79,6 +80,7 @@ impl Server {
clients: Mutex::new(HashSet::new()), clients: Mutex::new(HashSet::new()),
plugins: Mutex::new(Vec::new()), plugins: Mutex::new(Vec::new()),
shutting_down: AtomicBool::new(false), shutting_down: AtomicBool::new(false),
indicators: Mutex::new(Vec::new()),
}) })
} }
@@ -164,7 +166,10 @@ impl Server {
let uuid = self.wrap_err(&client, auth_res)?; let uuid = self.wrap_err(&client, auth_res)?;
self.wrap_err( self.wrap_err(
&client, &client,
client.send(types::message::ServerMessage::Authenticated { uuid }), client.send(types::message::ServerMessage::Authenticated {
uuid,
indicators: self.indicators.lock().unwrap().clone(),
}),
)?; )?;
} }
Some(v) => { Some(v) => {
+11 -6
View File
@@ -55,9 +55,12 @@ pub mod handshake {
pub mod message { pub mod message {
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{ use crate::{
requests::indicator::IndicatorContext,
types::{
Author, Author,
data::{self, Message}, data::{self, Message},
},
}; };
/// Messages sent *from the client* (user’s app) to the server /// Messages sent *from the client* (user’s app) to the server
@@ -85,6 +88,10 @@ pub mod message {
channel_id: String, channel_id: String,
chunk_id: usize, chunk_id: usize,
}, },
Typing {
channel_id: String,
},
} }
/// Messages sent *from the server* to the client /// Messages sent *from the server* to the client
@@ -94,6 +101,7 @@ pub mod message {
/// Successful authentication /// Successful authentication
Authenticated { Authenticated {
uuid: Author, uuid: Author,
indicators: Vec<IndicatorContext>,
}, },
TempMessage { TempMessage {
@@ -120,11 +128,8 @@ pub mod message {
status: String, status: String,
}, },
/// Typing indicator /// Indicator
Typing { Indicator(IndicatorContext),
user_id: Author,
channel_id: String,
},
Shutdown { Shutdown {
message: String, message: String,
+9 -9
View File
@@ -33,13 +33,13 @@ impl Logger {
} }
} }
pub fn extract_panic<T, E: Display, D: Display>(&self, v: Result<T, E>, m: D) -> T { // pub fn extract_panic<T, E: Display, D: Display>(&self, v: Result<T, E>, m: D) -> T {
match v { // match v {
Ok(a) => a, // Ok(a) => a,
Err(e) => { // Err(e) => {
self.error(format!("{m}: {e}")); // self.error(format!("{m}: {e}"));
panic!() // panic!()
} // }
} // }
} // }
} }
+36 -36
View File
@@ -16,29 +16,29 @@ pub fn dir(path: &Path) -> crate::Result<()> {
Ok(()) Ok(())
} }
pub fn read(path: &Path, default_content: &str) -> crate::Result<String> { // pub fn read(path: &Path, default_content: &str) -> crate::Result<String> {
if !path.exists() { // if !path.exists() {
LOGGER.info(format!( // LOGGER.info(format!(
"File {path:?} does not exist, creating it with default contents" // "File {path:?} does not exist, creating it with default contents"
)); // ));
write(path, default_content)?; // write(path, default_content)?;
return Ok(default_content.to_string()); // return Ok(default_content.to_string());
} // }
Ok(fs::read_to_string(path)?) // Ok(fs::read_to_string(path)?)
} // }
pub fn read_bytes<'a>(path: &Path, default_content: Vec<u8>) -> crate::Result<Vec<u8>> { // pub fn read_bytes<'a>(path: &Path, default_content: Vec<u8>) -> crate::Result<Vec<u8>> {
if !path.exists() { // if !path.exists() {
LOGGER.info(format!( // LOGGER.info(format!(
"File {path:?} does not exist, creating it with default contents" // "File {path:?} does not exist, creating it with default contents"
)); // ));
write_bytes(path, &default_content)?; // write_bytes(path, &default_content)?;
return Ok(default_content); // return Ok(default_content);
} // }
Ok(fs::read(path)?) // Ok(fs::read(path)?)
} // }
pub fn read_config<T: Default + Serialize + for<'de> Deserialize<'de>>( pub fn read_config<T: Default + Serialize + for<'de> Deserialize<'de>>(
path: &Path, path: &Path,
@@ -56,23 +56,23 @@ pub fn read_config<T: Default + Serialize + for<'de> Deserialize<'de>>(
Ok(serde_json::from_str::<T>(&read)?) Ok(serde_json::from_str::<T>(&read)?)
} }
pub fn write(path: &Path, content: &str) -> crate::Result<()> { // pub fn write(path: &Path, content: &str) -> crate::Result<()> {
dir(path.parent().ok_or(std::io::Error::new( // dir(path.parent().ok_or(std::io::Error::new(
std::io::ErrorKind::InvalidFilename, // std::io::ErrorKind::InvalidFilename,
"File doesn't have a parent assigned, example: `config/config.json`", // "File doesn't have a parent assigned, example: `config/config.json`",
))?)?; // ))?)?;
fs::write(path, content)?; // fs::write(path, content)?;
Ok(()) // Ok(())
} // }
pub fn write_bytes(path: &Path, content: &[u8]) -> crate::Result<()> { // pub fn write_bytes(path: &Path, content: &[u8]) -> crate::Result<()> {
dir(path.parent().ok_or(std::io::Error::new( // dir(path.parent().ok_or(std::io::Error::new(
std::io::ErrorKind::InvalidFilename, // std::io::ErrorKind::InvalidFilename,
"File doesn't have a parent assigned, example: `config/config.json`", // "File doesn't have a parent assigned, example: `config/config.json`",
))?)?; // ))?)?;
fs::write(path, content)?; // fs::write(path, content)?;
Ok(()) // Ok(())
} // }
pub fn write_config<T: Serialize>(path: &Path, content: &T) -> crate::Result<()> { pub fn write_config<T: Serialize>(path: &Path, content: &T) -> crate::Result<()> {
dir(path.parent().ok_or(std::io::Error::new( dir(path.parent().ok_or(std::io::Error::new(