//! # Push notifications helper //! //! @author Pierre Hubert use crate::controllers::user_ws_controller; use crate::data::conversation::ConvID; use crate::data::conversation_message::ConversationMessage; use crate::data::error::Res; use crate::data::push_notification::PushNotification; use crate::data::user::UserID; use crate::data::user_token::{PushNotificationToken, UserAccessToken}; use crate::helpers::{account_helper, conversations_helper, firebase_notifications_helper, independent_push_notifications_service_helper, user_helper}; use crate::helpers::events_helper::Event; /// Un-register for previous push notifications service pub fn un_register_from_previous_service(client: &UserAccessToken) -> Res { // This method must not fail in case of error if let PushNotificationToken::INDEPENDENT(old_token) = &client.push_notifications_token { if let Err(e) = independent_push_notifications_service_helper::remove_token(old_token) { eprintln!("Failed to un-register from independent push notifications service! {}", e); } } Ok(()) } /// Split tokens in categories : (Independent, Firebase) fn split_tokens(targets: Vec) -> (Vec, Vec) { let mut independent = vec![]; let mut firebase = vec![]; for target in targets { match target.push_notifications_token { PushNotificationToken::INDEPENDENT(token) => { independent.push(token) } PushNotificationToken::FIREBASE(_) => { firebase.push(target); } _ => {} } } (independent, firebase) } /// Push a notification to specific tokens fn push_notification(n: &PushNotification, targets: Vec) -> Res { let (independents, firebase) = split_tokens(targets); // Push independent notifications if !independents.is_empty() { independent_push_notifications_service_helper::push_notifications(n, independents)?; } // Push Firebase notifications if !firebase.is_empty() { firebase_notifications_helper::push_notifications(n, firebase)?; } Ok(()) } /// Cancel a notification for specific tokens (optional) fn cancel_notification(id: String, targets: Vec) -> Res { let (independents, _) = split_tokens(targets); if !independents.is_empty() { independent_push_notifications_service_helper::cancel_notifications(id, independents)?; } Ok(()) } /// Push a notification to specific users, only if they are not connected fn push_notification_to_users(n: &PushNotification, users: Vec) -> Res { let dest_users: Vec = users.into_iter() .filter(|u| !user_ws_controller::is_user_connected(u)) .collect(); let mut dest_tokens = vec![]; for user in dest_users { for token in account_helper::get_all_login_tokens(&user)? { dest_tokens.push(token); } } push_notification(n, dest_tokens) } /// Cancel a notification for one or more users fn cancel_notification_for_users(id: String, users: Vec) -> Res { let mut dest_tokens = vec![]; for user in users { for token in account_helper::get_all_login_tokens(&user)? { dest_tokens.push(token); } } cancel_notification(id, dest_tokens) } /// Create a conversation notification pub fn create_conversation_notification(msg: &ConversationMessage) -> Res { let user_id = match &msg.user_id { Some(id) => id, None => { // Do no create notifications for server messages return Ok(()); } }; let user = user_helper::find_user_by_id(user_id)?; let conv = conversations_helper::get_single(msg.conv_id)?; let notif_title = match conv.name { None => user.full_name(), Some(name) => format!("{} ({})", user.full_name(), name) }; let notif_message = match &msg.message { None => "Shared file".to_string(), Some(msg) => msg.to_string() }; let notif = PushNotification { id: format!("conv-{}", msg.conv_id.id()), title: notif_title, body: notif_message, image: None, timeout: None, }; let list: Vec = conversations_helper::get_list_members(msg.conv_id)? .into_iter() .filter(|m| m.following && m.user_id != user_id) .map(|m| m.user_id) .collect(); // Select only users who allowed push notifications let mut dest_list = vec![]; for el in list { if user_helper::find_user_by_id(&el)?.allow_notif_conv { dest_list.push(el); } } push_notification_to_users(¬if, dest_list) } /// Dismiss a conversation notification pub fn cancel_conversation_notification(conv_id: ConvID, users: Option>) -> Res { let notif_id = format!("conv-{}", conv_id.id()); let list = match users { Some(users) => users, None => conversations_helper::get_list_members(conv_id)? .into_iter().map(|m| m.user_id).collect() }; cancel_notification_for_users(notif_id, list) } /// Handle event. This method NEVER returns Err pub fn handle_event(e: &Event) -> Res { if let Err(e) = handle_event_internal(e) { eprintln!("Failed to create push notifications associated with event! {}", e); } Ok(()) } fn handle_event_internal(e: &Event) -> Res { match e { Event::NewConversationMessage(msg) => { create_conversation_notification(msg)?; } Event::SeenLastConversationMessage(user_id, conv_id) => { cancel_conversation_notification(*conv_id, Some(vec![user_id.as_owned()]))?; } _ => {} } Ok(()) }