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

460 lines
15 KiB
Rust

//! # Notifications helper
//!
//! @author Pierre Hubert
use std::collections::HashMap;
use crate::constants::database_tables_names::NOTIFICATIONS_TABLE;
use crate::data::error::{ExecError, Res, ResultBoxError};
use crate::data::group_id::GroupID;
use crate::data::notification::{NotifElemType, NotifEventType, NotifEventVisibility, Notification, PartialNotification};
use crate::data::post::{PostID, PostPageKind, PostVisibilityLevel};
use crate::data::user::{User, UserID};
use crate::helpers::{database, events_helper, groups_helper, posts_helper};
use crate::helpers::events_helper::Event;
use crate::helpers::friends_helper::GetFriendsQuery;
use crate::utils::date_utils;
use crate::utils::date_utils::time;
/// Create post notification
pub fn create_post_notification(from_user: &UserID, post_id: u64, action: NotifEventType) -> ResultBoxError {
let mut n = PartialNotification::new()
.set_from_user_id(from_user)
.set_on_elem_id(post_id)
.set_on_elem_type(NotifElemType::POST)
.set_type(action);
push(&mut n)
}
/// Create & push friend notification
pub fn create_friends_notification(from_user: &UserID, dest_user: &UserID, action: NotifEventType) -> ResultBoxError {
let mut n = PartialNotification::new()
.set_from_user_id(from_user)
.set_dest_user_id(dest_user)
.set_on_elem_id(from_user.id())
.set_on_elem_type(NotifElemType::FRIENDSHIP_REQUEST)
.set_type(action);
push(&mut n)
}
/// Create & push a group membership notification
pub fn create_group_membership_notification(user_id: &UserID, moderator_id: Option<&UserID>, group_id: &GroupID, kind: NotifEventType) -> ResultBoxError {
// Delete related group membership notifications
delete_all_related_to_group_membership_notifications(user_id, group_id)?;
let mut n = PartialNotification::new()
.set_on_elem_id(group_id.id())
.set_on_elem_type(NotifElemType::GROUP_MEMBERSHIP)
.set_type(kind);
match moderator_id {
// The notification must be sent to all the moderators of the group
None => {
n = n.set_from_user_id(user_id);
}
// We specify both the source and the destination of the notification
// not to broadcast the notification to all the group members
Some(moderator_id) => {
n = n.set_from_user_id(moderator_id)
.set_dest_user_id(user_id);
}
}
push(&mut n)
}
/// Push a new notification
fn push(n: &mut PartialNotification) -> ResultBoxError
{
if n.time_create.is_none()
{
n.time_create = Some(date_utils::time());
}
// Determine the visibility level of the notification
if matches!(n.on_elem_type, Some(NotifElemType::POST)) {
let post = posts_helper::get_single(n.on_elem_id.unwrap())?;
// Determine post container
match &post.target_page {
PostPageKind::PAGE_KIND_USER(user_id) => {
n.container_type = Some(NotifElemType::USER_PAGE);
n.container_id = Some(user_id.id());
}
PostPageKind::PAGE_KIND_GROUP(group_id) => {
n.container_type = Some(NotifElemType::GROUP_PAGE);
n.container_id = Some(group_id.id());
}
};
// Check the scope of the notification
// Private post (on user page)
if matches!(post.visibility, PostVisibilityLevel::VISIBILITY_USER) {
// Check if the post belongs to current user => no notification needed
if &post.user_id == post.user_page_id().unwrap() {
return Ok(());
}
// If the person who posted that is not the owner of the page
if post.user_page_id().unwrap() != n.from_user_id.as_ref().unwrap() {
n.dest_user_id = Some(post.user_page_id().unwrap().clone());
}
// If the user is the owner of the page, but the post does not belongs to him
else {
n.dest_user_id = Some(post.user_id);
}
return push_private(n);
}
// Posts on user page
else if post.is_on_user_page() {
let mut friend_req = GetFriendsQuery::new(n.from_user_id.as_ref().unwrap())
.set_only_followers(true);
// If the person who created the notification is not on his page
if n.from_user_id.as_ref().unwrap() != post.user_page_id().unwrap() {
friend_req = friend_req.set_only_common_with(post.user_page_id().unwrap())
}
let friends: Vec<UserID> = friend_req
.exec()?
.into_iter()
.map(|f| f.friend_id)
.collect();
return push_public(n, friends);
}
// Posts on group pages
else if post.is_on_group_page() {
return push_group_members(n, post.group_id().unwrap());
}
// Unsupported scenario
else {
unimplemented!();
}
}
// Friendship notification
else if matches!(n.on_elem_type, Some(NotifElemType::FRIENDSHIP_REQUEST)) {
n.container_id = None;
n.container_type = None;
return push_private(n);
}
// Groups membership notifications
else if matches!(n.on_elem_type, Some(NotifElemType::GROUP_MEMBERSHIP)) {
// Complete the notification
n.container_type = None;
n.container_id = None;
// Check whether the notification has to be pushed to a single user
// or to all the moderators of the group
return if let Some(_) = n.dest_user_id
{
// Push the notification in private way (if it has a destination,
// generally the target user of the membership request)
push_private(n)
} else {
push_group_moderators(n, &GroupID::new(n.on_elem_id.unwrap()))
};
} else {
unimplemented!();
}
}
/// Push a notification to group members
fn push_group_members(n: &mut PartialNotification, group_id: &GroupID) -> ResultBoxError {
let mut list = groups_helper::get_list_followers(group_id)?;
list = list.into_iter().filter(|f| f != n.from_user_id.as_ref().unwrap()).collect();
push_public(n, list)
}
/// Push a notification to all the moderators & administrators of a group
fn push_group_moderators(n: &mut PartialNotification, group_id: &GroupID) -> ResultBoxError {
let list = groups_helper::get_list_members(group_id)?;
let list: Vec<UserID> = list
.into_iter()
.filter(|e| e.is_moderator())
.map(|f| f.user_id)
.collect();
push_public(n, list)
}
/// Push a public notification
fn push_public(n: &mut PartialNotification, users: Vec<UserID>) -> ResultBoxError {
n.visibility = Some(NotifEventVisibility::EVENT_PUBLIC);
for user_id in users {
n.dest_user_id = Some(user_id);
if !similar_exists(n)? {
create(n)?;
}
}
Ok(())
}
/// Push a private notification (to 1 user)
fn push_private(n: &mut PartialNotification) -> ResultBoxError {
n.visibility = Some(NotifEventVisibility::EVENT_PRIVATE);
if !similar_exists(n)? {
create(n)?;
}
Ok(())
}
/// Create a new notification
fn create(n: &PartialNotification) -> ResultBoxError {
if n.dest_user_id.is_none() || n.from_user_id.is_none()
{
return Err(ExecError::boxed_new("Trying to send a notification without a source or a destination!"));
}
database::InsertQuery::new(NOTIFICATIONS_TABLE)
.add_values(notif_to_db(n, true))
.insert_drop_result()?;
// Send a notification (updated_number_conversations)
events_helper::propagate_event(&Event::UpdatedNotificationsNumber(&vec![n.dest_user_id.clone().unwrap()]))?;
Ok(())
}
/// Delete notifications
pub fn delete(notification: &PartialNotification) -> ResultBoxError {
let conditions = notif_to_db(notification, false);
// Get the list of affected users
let users = database::QueryInfo::new(NOTIFICATIONS_TABLE)
.add_conditions(&conditions)
.add_field("dest_user_id")
.exec(|r| r.get_user_id("dest_user_id"))?;
// Delete the notifications
database::DeleteQuery::new(NOTIFICATIONS_TABLE)
.add_conditions(conditions)
.exec()?;
// Send a notification (updated_number_conversations)
events_helper::propagate_event(&Event::UpdatedNotificationsNumber(&users))?;
Ok(())
}
/// Delete all the notifications of a given user
pub fn delete_all_user(user_id: &UserID) -> ResultBoxError {
delete(&PartialNotification::new().set_dest_user_id(user_id))
}
/// Delete all the notifications related with a user
pub fn delete_all_related_with_user(user_id: &UserID) -> ResultBoxError {
// Delete all the notifications targeting the user
delete_all_user(user_id)?;
// Delete all the notifications created by the user
delete(&PartialNotification::new().set_from_user_id(user_id))?;
Ok(())
}
/// Delete all the old notifications of a user
pub fn clean_old_user_notifications(user: &User) -> Res {
let lifetime = user.delete_notifications_after.unwrap_or(0);
if lifetime < 1 {
return Ok(());
}
database::DeleteQuery::new(NOTIFICATIONS_TABLE)
.cond_user_id("dest_user_id", &user.id)
.set_custom_where("time_create < ?")
.add_custom_where_arg_u64(time() - lifetime)
.exec()
}
/// Delete all the notifications related with a group
pub fn delete_all_related_with_group(group_id: &GroupID) -> ResultBoxError {
delete(&PartialNotification::new()
.set_on_elem_type(NotifElemType::GROUP_MEMBERSHIP)
.set_on_elem_id(group_id.id())
)?;
delete(&PartialNotification::new()
.set_on_elem_type(NotifElemType::GROUP_PAGE)
.set_on_elem_id(group_id.id())
)
}
/// Delete all the notifications related to a group membership
pub fn delete_all_related_to_group_membership_notifications(user_id: &UserID, group_id: &GroupID) -> ResultBoxError {
let mut n = PartialNotification::new()
.set_on_elem_type(NotifElemType::GROUP_MEMBERSHIP)
.set_on_elem_id(group_id.id());
n.dest_user_id = Some(user_id.clone());
n.from_user_id = None;
delete(&n)?;
n.dest_user_id = None;
n.from_user_id = Some(user_id.clone());
delete(&n)?;
Ok(())
}
/// Delete all the notifications about a post targeting a specified user
pub fn delete_all_post_notifications_targeting_user(user_id: &UserID, post_id: PostID) -> ResultBoxError {
let n = PartialNotification::new()
.set_dest_user_id(user_id)
.set_on_elem_type(NotifElemType::POST)
.set_on_elem_id(post_id);
delete(&n)
}
/// Delete all the notifications related with a post
pub fn delete_all_related_with_post(post_id: PostID) -> ResultBoxError {
let n = PartialNotification::new()
.set_on_elem_type(NotifElemType::POST)
.set_on_elem_id(post_id);
delete(&n)
}
/// Delete all the notifications related with a friendship request
pub fn delete_all_related_with_friendship_request(user_one: &UserID, user_two: &UserID) -> ResultBoxError {
let mut n = PartialNotification::new()
.set_on_elem_type(NotifElemType::FRIENDSHIP_REQUEST);
n.from_user_id = Some(user_one.clone());
n.dest_user_id = Some(user_two.clone());
delete(&n)?;
n.from_user_id = Some(user_two.clone());
n.dest_user_id = Some(user_one.clone());
delete(&n)
}
/// Check out whether a similar notification exists for given specifications
pub fn similar_exists(n: &PartialNotification) -> ResultBoxError<bool> {
database::QueryInfo::new(NOTIFICATIONS_TABLE)
.add_conditions(&notif_to_db(n, false))
.exec_count()
.map(|f| f > 0)
}
/// Count the number of unread notifications
pub fn count_unread(user_id: &UserID) -> ResultBoxError<u64> {
database::QueryInfo::new(NOTIFICATIONS_TABLE)
.cond_user_id("dest_user_id", user_id)
.cond_legacy_bool("seen", false)
.exec_count()
.map(|c| c as u64)
}
/// Get the list of notifications of the user
pub fn get_list_unread(user_id: &UserID) -> ResultBoxError<Vec<Notification>> {
database::QueryInfo::new(NOTIFICATIONS_TABLE)
.cond_user_id("dest_user_id", user_id)
.cond_legacy_bool("seen", false)
.set_order("id DESC")
.exec(db_to_notif)
}
/// Get information about a single notification
pub fn get_single(notif_id: u64) -> ResultBoxError<Notification> {
database::QueryInfo::new(NOTIFICATIONS_TABLE)
.cond_u64("id", notif_id)
.query_row(db_to_notif)
}
/// Turn a database row into a notification object
fn db_to_notif(row: &database::RowResult) -> ResultBoxError<Notification> {
Ok(Notification {
id: row.get_u64("id")?,
time_create: row.get_u64("time_create")?,
seen: row.get_legacy_bool("seen")?,
from_user_id: row.get_user_id("from_user_id")?,
dest_user_id: row.get_user_id("dest_user_id")?,
on_elem_id: row.get_u64("on_elem_id")?,
on_elem_type: NotifElemType::from_db(&row.get_str("on_elem_type")?),
kind: NotifEventType::from_db(&row.get_str("type")?),
visibility: NotifEventVisibility::from_db(&row.get_str("visibility")?),
container_id: row.get_optional_u64("from_container_id")?,
container_type: row.get_optional_str("from_container_type")?
.map(|s| NotifElemType::from_db(&s)),
})
}
/// Turn a notification into a database entry
fn notif_to_db(n: &PartialNotification, complete_information: bool) -> HashMap<String, mysql::Value> {
let mut map = HashMap::new();
if let Some(id) = n.id {
map.insert("id".to_string(), mysql::Value::UInt(id));
}
if let Some(seen) = n.seen {
map.insert("seen".to_string(), mysql::Value::Int(match seen {
true => 1,
false => 0,
}));
}
if let Some(from_user_id) = &n.from_user_id {
map.insert("from_user_id".to_string(), mysql::Value::UInt(from_user_id.id()));
}
if let Some(dest_user_id) = &n.dest_user_id {
map.insert("dest_user_id".to_string(), mysql::Value::UInt(dest_user_id.id()));
}
if let Some(kind) = &n.kind {
map.insert("type".to_string(), mysql::Value::from(kind.to_db()));
}
if let Some(on_elem_id) = n.on_elem_id {
map.insert("on_elem_id".to_string(), mysql::Value::from(on_elem_id));
}
if let Some(on_elem_type) = &n.on_elem_type {
map.insert("on_elem_type".to_string(), mysql::Value::from(on_elem_type.to_db()));
}
if complete_information {
if let Some(from_container_id) = n.container_id {
map.insert("from_container_id".to_string(), mysql::Value::from(from_container_id));
}
if let Some(from_container_type) = &n.container_type {
map.insert("from_container_type".to_string(), mysql::Value::from(from_container_type.to_db()));
}
if let Some(time_create) = n.time_create {
map.insert("time_create".to_string(), mysql::Value::from(time_create));
}
if let Some(visibility) = &n.visibility {
map.insert("visibility".to_string(), mysql::Value::from(visibility.to_db()));
}
}
map
}