1
0
mirror of https://gitlab.com/comunic/comunicapiv3 synced 2025-01-30 14:03:00 +00:00
comunicapiv3/src/helpers/conversations_helper.rs

453 lines
15 KiB
Rust

//! # Conversations helper
//!
//! @author Pierre Hubert
use crate::constants::database_tables_names::{CONV_LIST_TABLE, CONV_MESSAGES_TABLE, CONV_USERS_TABLE};
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::unread_conversation::UnreadConversation;
use crate::data::user::UserID;
use crate::helpers::database;
use crate::helpers::database::InsertQuery;
use crate::utils::date_utils::time;
use crate::utils::user_data_utils::user_data_path;
/// Create a new conversation. This method returns the ID of the created conversation
pub fn create(conv: &NewConversation) -> ResultBoxError<u64> {
// Create the conversation in the main table
let conv_id = InsertQuery::new(CONV_LIST_TABLE)
.add_user_id("user_id", &conv.owner_id)
.add_str("name", conv.name.clone().unwrap_or(String::new()).as_str())
.add_u64("last_active", time())
.add_u64("creation_time", time())
.add_legacy_bool("can_everyone_add_members", conv.can_everyone_add_members)
.insert()?.ok_or(ExecError::new("missing result conv id!"))?;
// Add the members to the conversation
for member in &conv.members {
// Check following status of the member
let mut follow = true;
if member.eq(&conv.owner_id) {
follow = conv.owner_following;
}
add_member(conv_id, member, follow)?;
}
Ok(conv_id)
}
/// Add a member to a conversation
pub fn add_member(conv_id: u64, user_id: &UserID, following: bool) -> ResultBoxError<()> {
InsertQuery::new(CONV_USERS_TABLE)
.add_u64("conv_id", conv_id)
.add_user_id("user_id", user_id)
.add_u64("time_add", time())
.add_legacy_bool("following", following)
.add_legacy_bool("saw_last_message", true)
.insert()?;
Ok(())
}
/// Remove a member from a conversation
pub fn remove_member(conv_id: u64, user_id: &UserID) -> ResultBoxError<()> {
database::DeleteQuery::new(CONV_USERS_TABLE)
.cond_u64("conv_id", conv_id)
.cond_user_id("user_id", user_id)
.exec()
}
/// Get the list of conversations of a specific user
pub fn get_list_user(user_id: &UserID) -> ResultBoxError<Vec<Conversation>> {
database::QueryInfo::new(CONV_LIST_TABLE)
.alias("l")
// Join with conversation members table
.join(CONV_USERS_TABLE, "u", "l.id = u.conv_id")
// Specify selected fields
.add_field("*")
.add_field("l.id as id")
.add_field("l.user_id as owner_id")
// Filter query
.cond_user_id("u.user_id", user_id)
// Sort results
.set_order("l.last_active DESC")
// Execute query
.exec(db_to_conversation_info)
}
/// Get information about a single conversation
pub fn get_single(conv_id: u64, user_id: &UserID) -> ResultBoxError<Conversation> {
// Tables
database::QueryInfo::new(CONV_LIST_TABLE)
.alias("l")
.join(CONV_USERS_TABLE, "u", "l.id = u.conv_id")
// Fields
.add_field("*")
.add_field("l.id as id")
.add_field("l.user_id as owner_id")
// Conditions
.cond_u64("l.id", conv_id)
.cond_user_id("u.user_id", user_id)
.query_row(db_to_conversation_info)
}
/// Get the list of members of a conversation
pub fn get_list_members(conv_id: u64) -> ResultBoxError<Vec<UserID>> {
database::QueryInfo::new(CONV_USERS_TABLE)
.cond_u64("conv_id", conv_id)
.add_field("user_id")
.exec(|res| res.get_user_id("user_id"))
}
/// Check if a user belongs to a conversation or not
pub fn does_user_belongs_to(user_id: &UserID, conv_id: u64) -> ResultBoxError<bool> {
Ok(database::QueryInfo::new(CONV_USERS_TABLE)
.cond_u64("conv_id", conv_id)
.cond_user_id("user_id", user_id)
.exec_count()? > 0)
}
/// Check out wheter a user is the moderator of a conversation or not
pub fn is_user_moderator(user_id: &UserID, conv_id: u64) -> ResultBoxError<bool> {
Ok(database::QueryInfo::new(CONV_LIST_TABLE)
.cond_u64("id", conv_id)
.cond_user_id("user_id", user_id)
.exec_count()? > 0)
}
/// Check out whether all the members of a conversation can add members to it or not
pub fn can_everyone_add_members(conv_id: u64) -> ResultBoxError<bool> {
database::QueryInfo::new(CONV_LIST_TABLE)
.cond_u64("id", conv_id)
.add_field("can_everyone_add_members")
.query_row(|f| f.get_legacy_bool("can_everyone_add_members"))
}
/// Set whether a user is following a conversation or not
pub fn set_following(user_id: &UserID, conv_id: u64, following: bool) -> ResultBoxError<()> {
database::UpdateInfo::new(CONV_USERS_TABLE)
.cond_u64("conv_id", conv_id)
.cond_user_id("user_id", user_id)
.set_legacy_bool("following", following)
.exec()
}
/// Set a new list of members for a given conversation
pub fn set_members(conv_id: u64, new_list: &Vec<UserID>, can_delete: bool) -> ResultBoxError<()> {
let curr_list = get_list_members(conv_id)?;
// Add new members
for member in new_list {
if curr_list.contains(member) {
continue;
}
add_member(conv_id, member, true)?;
}
// Remove a member
if can_delete {
for member in curr_list {
if new_list.contains(&member) {
continue;
}
remove_member(conv_id, &member)?;
}
}
Ok(())
}
/// Set a new name to the conversation
pub fn set_name(conv_id: u64, name: Option<String>) -> ResultBoxError<()> {
database::UpdateInfo::new(CONV_LIST_TABLE)
.cond_u64("id", conv_id)
.set_opt_str("name", name)
.exec()
}
/// Specify whether any member of this conversation can invite other users to join it
pub fn set_can_everyone_add_members(conv_id: u64, allow: bool) -> ResultBoxError<()> {
database::UpdateInfo::new(CONV_LIST_TABLE)
.cond_u64("id", conv_id)
.set_legacy_bool("can_everyone_add_members", allow)
.exec()
}
/// Search for private conversation between two users
pub fn find_private(user_1: &UserID, user_2: &UserID) -> ResultBoxError<Vec<u64>> {
database::QueryInfo::new(CONV_USERS_TABLE)
.alias("t1")
// Join
.join(CONV_USERS_TABLE, "t2", "t1.conv_id = t2.conv_id")
// Conditions
.cond_user_id("t1.user_id", user_1)
.cond_user_id("t2.user_id", user_2)
.set_custom_where(format!("(SELECT COUNT(*) FROM {} WHERE conv_id = t1.conv_id) = 2", CONV_USERS_TABLE).as_ref())
.add_field("t1.conv_id AS conv_id")
.exec(|f| f.get_u64("conv_id"))
}
/// Get the last messages posted in a conversation
pub fn get_last_messages(conv_id: u64, number_of_messages: u64) -> ResultBoxError<Vec<ConversationMessage>> {
database::QueryInfo::new(CONV_MESSAGES_TABLE)
.cond_u64("conv_id", conv_id)
.set_limit(number_of_messages)
.set_order("id DESC")
.exec(db_to_conversation_message)
.map(|mut l| {
l.sort_by(|a, b| a.id.partial_cmp(&b.id).unwrap());
l
})
}
/// Get the new messages of a conversation
pub fn get_new_messages(conv_id: u64, last_message_id: u64) -> ResultBoxError<Vec<ConversationMessage>> {
database::QueryInfo::new(CONV_MESSAGES_TABLE)
.cond_u64("conv_id", conv_id)
.set_custom_where("id > ?")
.add_custom_where_argument_u64(last_message_id)
.set_order("id")
.exec(db_to_conversation_message)
}
/// Get older messages of a conversation
///
/// `conv_id` contains the ID of the target conversation
/// `start_id` contains the ID from wich the research start
/// `limit` Maximum number of messages to get
pub fn get_older_messages(conv_id: u64, start_id: u64, limit: u64) -> ResultBoxError<Vec<ConversationMessage>> {
database::QueryInfo::new(CONV_MESSAGES_TABLE)
.cond_u64("conv_id", conv_id)
.set_custom_where("ID <= ?")
.add_custom_where_argument_u64(start_id)
.set_order("id DESC")
.set_limit(limit)
.exec(db_to_conversation_message)
.map(|mut l| {
l.sort_by(|a, b| a.id.partial_cmp(&b.id).unwrap());
l
})
}
/// Get all the messages of a single user for a conversation
pub fn get_user_messages_for_conversations(conv_id: u64, user_id: &UserID) -> ResultBoxError<Vec<ConversationMessage>> {
database::QueryInfo::new(CONV_MESSAGES_TABLE)
.cond_u64("conv_id", conv_id)
.cond_user_id("user_id", user_id)
.exec(db_to_conversation_message)
}
/// Get the entire list of messages of a given conversation
pub fn get_all_messages(conv_id: u64) -> ResultBoxError<Vec<ConversationMessage>> {
database::QueryInfo::new(CONV_MESSAGES_TABLE)
.cond_u64("conv_id", conv_id)
.exec(db_to_conversation_message)
}
/// Get a single message specified by its ID
pub fn get_single_message(msg_id: u64) -> ResultBoxError<ConversationMessage> {
database::QueryInfo::new(CONV_MESSAGES_TABLE)
.cond_u64("id", msg_id)
.query_row(db_to_conversation_message)
}
/// Send a new conversation message
pub fn send_message(msg: &NewConversationMessage) -> 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.id())
.set_legacy_bool("saw_last_message", false)
.exec()?;
Ok(())
}
/// Update message content
pub fn update_message_content(msg_id: u64, new_content: &str) -> ResultBoxError<()> {
database::UpdateInfo::new(CONV_MESSAGES_TABLE)
.cond_u64("id", msg_id)
.set_str("message", new_content)
.exec()
}
/// Remove a message from a conversation
pub fn delete_message(msg: &ConversationMessage) -> ResultBoxError<()> {
// Delete associated image (if any)
if let Some(img) = &msg.image_path {
let path = user_data_path(img.as_ref());
if path.exists() {
std::fs::remove_file(path)?;
}
}
database::DeleteQuery::new(CONV_MESSAGES_TABLE)
.cond_u64("ID", msg.id)
.exec()?;
Ok(())
}
/// Delete a message with a specific ID
pub fn delete_message_by_id(id: u64) -> ResultBoxError<()> {
delete_message(&get_single_message(id)?)
}
/// Count the number of unread conversation for a specified user
pub fn count_unread_for_user(user_id: &UserID) -> ResultBoxError<usize> {
database::QueryInfo::new(CONV_USERS_TABLE)
.cond_user_id("user_id", user_id)
.cond_legacy_bool("saw_last_message", false)
.cond_legacy_bool("following", true)
.exec_count()
}
/// Get the list of unread conversations of a user
pub fn get_list_unread(user_id: &UserID) -> ResultBoxError<Vec<UnreadConversation>> {
database::QueryInfo::new(CONV_USERS_TABLE)
.alias("users")
.join(CONV_LIST_TABLE, "list", "users.conv_id = list.id")
.join(CONV_MESSAGES_TABLE, "messages", "messages.conv_id = users.conv_id")
.cond_user_id("users.user_id", user_id)
.cond_legacy_bool("users.following", true)
.cond_legacy_bool("users.saw_last_message", false)
.set_custom_where("list.last_active = messages.time_insert")
.set_order("list.last_active DESC")
.exec(|res| Ok(UnreadConversation {
id: res.get_u64("conv_id")?,
name: res.get_optional_str("name")?,
last_active: res.get_u64("last_active")?,
user_id: res.get_user_id("user_id")?,
message: res.get_str("message")?,
}))
}
/// 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)
.cond_u64("conv_id", conv_id)
.cond_user_id("user_id", user_id)
.cond_legacy_bool("saw_last_message", false)
.set_legacy_bool("saw_last_message", true)
.exec()
}
/// Remove a user from a conversation
pub fn remove_user_from_conversation(user_id: &UserID, conv_id: u64) -> ResultBoxError<()> {
if is_user_moderator(user_id, conv_id)? {
delete_conversation(conv_id)
} else {
delete_member(user_id, conv_id)
}
}
/// Remove permanently a conversation
pub fn delete_conversation(conv_id: u64) -> ResultBoxError<()> {
// Delete all the messages of the conversations
for message in get_all_messages(conv_id)? {
delete_message(&message)?;
}
// Delete all the members of the conversation
database::DeleteQuery::new(CONV_USERS_TABLE)
.cond_u64("conv_id", conv_id)
.exec()?;
// Delete the conversation entry itself
database::DeleteQuery::new(CONV_LIST_TABLE)
.cond_u64("id", conv_id)
.exec()?;
Ok(())
}
/// Delete a conversation membership
pub fn delete_member(user_id: &UserID, conv_id: u64) -> ResultBoxError<()> {
for msg in get_user_messages_for_conversations(conv_id, user_id)? {
delete_message(&msg)?;
}
// Delete membership
remove_member(conv_id, user_id)?;
Ok(())
}
/// Check out whether a user is the owner of a message or not
pub fn is_message_owner(user_id: &UserID, message_id: u64) -> ResultBoxError<bool> {
database::QueryInfo::new(CONV_MESSAGES_TABLE)
.cond_u64("id", message_id)
.cond_user_id("user_id", user_id)
.exec_count()
.map(|r| r > 0)
}
/// Turn a database entry into a ConversationInfo object
fn db_to_conversation_info(row: &database::RowResult) -> ResultBoxError<Conversation> {
let conv_id = row.get_u64("id")?;
Ok(Conversation {
id: conv_id,
owner_id: row.get_user_id("owner_id")?,
name: row.get_optional_str("name")?,
members: get_list_members(conv_id)?,
can_everyone_add_members: row.get_legacy_bool("can_everyone_add_members")?,
last_active: row.get_u64("last_active")?,
time_create: row.get_u64("time_add")?,
following: row.get_legacy_bool("following")?,
saw_last_message: row.get_legacy_bool("saw_last_message")?,
})
}
/// Turn a database entry into a ConversationMessgae object
fn db_to_conversation_message(row: &database::RowResult) -> ResultBoxError<ConversationMessage> {
Ok(ConversationMessage {
id: row.get_u64("id")?,
time_sent: row.get_u64("time_insert")?,
conv_id: row.get_u64("conv_id")?,
user_id: row.get_user_id("user_id")?,
message: row.get_optional_str("message")?,
image_path: row.get_optional_str("image_path")?,
})
}