diff --git a/src/controllers/conversations_controller.rs b/src/controllers/conversations_controller.rs index 8806aea..0136bbb 100644 --- a/src/controllers/conversations_controller.rs +++ b/src/controllers/conversations_controller.rs @@ -14,6 +14,7 @@ use crate::data::http_request_handler::HttpRequestHandler; use crate::data::new_conversation::NewConversation; use crate::helpers::{conversations_helper, user_helper}; use crate::utils::string_utils::remove_html_nodes; +use crate::data::new_conversation_message::NewConversationMessage; /// Create a new conversation pub fn create(r: &mut HttpRequestHandler) -> RequestResult { @@ -233,13 +234,25 @@ pub fn refresh_single(r: &mut HttpRequestHandler) -> RequestResult { /// Send a new message pub fn send_message(r: &mut HttpRequestHandler) -> RequestResult { - let mut image_path = None; + let conv_id = r.post_conv_id("conversationID")?; + let message = r.post_string_without_html("message", 0, false)?; - if r.has_file("image") { - image_path = Some(r.save_post_image("image", "conversations", 1200, 1200)?); + // Get image + let image_path = match r.has_file("image") { + false => None, + true => Some(r.save_post_image("image", "conversations", 1200, 1200)?) + }; + + if image_path == None && message.len() < 3 { + r.bad_request("Message is empty!".to_string())?; } - println!("image: {:?}", image_path); + conversations_helper::send_message(&NewConversationMessage { + user_id: r.user_id()?, + conv_id, + message, + image_path, + })?; - r.success("implement me") + r.success("Conversation message was successfully sent!") } \ No newline at end of file diff --git a/src/data/http_request_handler.rs b/src/data/http_request_handler.rs index 4bf8e60..3f11c4a 100644 --- a/src/data/http_request_handler.rs +++ b/src/data/http_request_handler.rs @@ -16,6 +16,7 @@ use crate::data::config::conf; use crate::data::error::{ExecError, ResultBoxError}; use crate::data::user::UserID; use crate::helpers::{account_helper, api_helper, conversations_helper, user_helper}; +use crate::utils::string_utils::remove_html_nodes; use crate::utils::user_data_utils::{generate_new_user_data_file_name, prepare_file_creation, user_data_path}; use crate::utils::virtual_directories_utils::check_virtual_directory; @@ -423,6 +424,11 @@ impl HttpRequestHandler { Ok(dir) } + /// Get a string included in the request, with HTML codes removed + pub fn post_string_without_html(&mut self, name: &str, min_length: usize, required: bool) -> ResultBoxError { + Ok(remove_html_nodes(self.post_string_opt(name, min_length, required)?.as_str())) + } + /// Get & return the ID of the conversation included in the POST request pub fn post_conv_id(&mut self, name: &str) -> ResultBoxError { let conv_id = self.post_u64(name)?; diff --git a/src/data/mod.rs b/src/data/mod.rs index 3ef9e4f..6e03249 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -9,4 +9,5 @@ pub mod user_token; pub mod custom_emoji; pub mod new_conversation; pub mod conversation; -pub mod conversation_message; \ No newline at end of file +pub mod conversation_message; +pub mod new_conversation_message; \ No newline at end of file diff --git a/src/data/new_conversation_message.rs b/src/data/new_conversation_message.rs new file mode 100644 index 0000000..c300de5 --- /dev/null +++ b/src/data/new_conversation_message.rs @@ -0,0 +1,13 @@ +//! # New conversation message +//! +//! @author Pierre Hubert + +use crate::data::user::UserID; + +/// Information about a new conversation message +pub struct NewConversationMessage { + pub user_id: UserID, + pub conv_id: u64, + pub message: String, + pub image_path: Option +} \ No newline at end of file diff --git a/src/helpers/conversations_helper.rs b/src/helpers/conversations_helper.rs index bb89e2c..c64d2ac 100644 --- a/src/helpers/conversations_helper.rs +++ b/src/helpers/conversations_helper.rs @@ -7,6 +7,7 @@ use crate::data::conversation::Conversation; use crate::data::conversation_message::ConversationMessage; use crate::data::error::{ExecError, ResultBoxError}; use crate::data::new_conversation::NewConversation; +use crate::data::new_conversation_message::NewConversationMessage; use crate::data::user::UserID; use crate::helpers::database; use crate::helpers::database::InsertQuery; @@ -223,6 +224,39 @@ pub fn get_new_messages(conv_id: u64, last_message_id: u64) -> ResultBoxError ResultBoxError<()> { + let t = time(); + + // Insert the message in the database + database::InsertQuery::new(CONV_MESSAGES_TABLE) + .add_u64("conv_id", msg.conv_id) + .add_user_id("user_id", msg.user_id) + .add_u64("time_insert", t) + .add_str("message", msg.message.as_str()) + .add_opt_str("image_path", msg.image_path.as_ref()) + .insert()?; + + // Update the last activity of the conversation + database::UpdateInfo::new(CONV_LIST_TABLE) + .cond_u64("id", msg.conv_id) + .set_u64("last_active", t) + .exec()?; + + // Mark all the users of the conversation as unread + database::UpdateInfo::new(CONV_USERS_TABLE) + .cond_u64("conv_id", msg.conv_id) + .cond_legacy_bool("saw_last_message", true) + + .custom_where("user_id != ?") + .add_custom_where_arg_u64(msg.user_id as u64) + + .set_legacy_bool("saw_last_message", false) + .exec()?; + + Ok(()) +} + /// Indicate that a user has seen the last messages of a conversation pub fn mark_user_seen(conv_id: u64, user_id: UserID) -> ResultBoxError<()> { database::UpdateInfo::new(CONV_USERS_TABLE) diff --git a/src/helpers/database.rs b/src/helpers/database.rs index 921e76a..7314570 100644 --- a/src/helpers/database.rs +++ b/src/helpers/database.rs @@ -445,6 +445,12 @@ impl InsertQuery { self } + /// Add an optional string. If None, an empty string will be inserted + pub fn add_opt_str(mut self, key: &str, value: Option<&String>) -> InsertQuery { + self.values.insert(key.to_string(), Value::from(value.unwrap_or(&String::new()))); + self + } + /// Add an integer pub fn add_i64(mut self, key: &str, value: i64) -> InsertQuery { self.values.insert(key.to_string(), Value::from(value)); @@ -583,6 +589,8 @@ pub fn delete(query: DeleteQuery) -> ResultBoxError<()> { pub struct UpdateInfo { table: String, cond: HashMap, + custom_where: Option, + custom_where_args: Vec, set: HashMap, } @@ -592,6 +600,8 @@ impl UpdateInfo { UpdateInfo { table: table.to_string(), cond: HashMap::new(), + custom_where: None, + custom_where_args: vec![], set: HashMap::new(), } } @@ -619,6 +629,18 @@ impl UpdateInfo { self } + /// Add custom WHERE clause + pub fn custom_where(mut self, query: &str) -> UpdateInfo { + self.custom_where = Some(query.to_string()); + self + } + + /// Add custom WHERE argument + pub fn add_custom_where_arg_u64(mut self, arg: u64) -> UpdateInfo { + self.custom_where_args.push(mysql::Value::UInt(arg)); + self + } + /// Set an new optional string /// /// None => Empty string @@ -639,6 +661,12 @@ impl UpdateInfo { self } + /// Set a new unsigned number + pub fn set_u64(mut self, name: &str, val: u64) -> UpdateInfo { + self.set.insert(name.to_string(), Value::UInt(val)); + self + } + /// Execute the update pub fn exec(self) -> ResultBoxError<()> { update(self) @@ -668,13 +696,20 @@ pub fn update(u: UpdateInfo) -> ResultBoxError<()> { u.set.values().into_iter().for_each(|f| values.push(f)); // Prepare conditions - let conditions = u.cond.keys() + let mut conditions = u.cond.keys() .into_iter() .map(|f| format!("{} = ?", f)) .collect::>() .join(" AND "); u.cond.values().into_iter().for_each(|f| values.push(f)); + // Additional conditions + if let Some(custom_where) = u.custom_where { + conditions = format!("{} AND ({})", conditions, custom_where); + + u.custom_where_args.iter().for_each(|f| values.push(f)) + } + query_sql = format!("{} {} WHERE {}", query_sql, updates, conditions); get_connection()?.exec_drop(