mirror of
https://gitlab.com/comunic/comunicapiv3
synced 2025-01-30 14:03:00 +00:00
460 lines
15 KiB
Rust
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(¬if_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
|
|
} |