diff --git a/src/api_data/mod.rs b/src/api_data/mod.rs index ed81ad2..a40a87a 100644 --- a/src/api_data/mod.rs +++ b/src/api_data/mod.rs @@ -39,4 +39,5 @@ pub mod res_create_post; pub mod posts_targets_api; pub mod res_create_comment; pub mod res_number_unread_notifications; -pub mod res_count_all_unreads; \ No newline at end of file +pub mod res_count_all_unreads; +pub mod notification_api; \ No newline at end of file diff --git a/src/api_data/notification_api.rs b/src/api_data/notification_api.rs new file mode 100644 index 0000000..29cb6e3 --- /dev/null +++ b/src/api_data/notification_api.rs @@ -0,0 +1,59 @@ +//! # API Notification object +//! +//! @author Pierre Hubert + +use serde::{Serialize, Serializer}; +use serde::ser::SerializeMap; + +use crate::data::notification::Notification; + +struct TypeContainer { + t: String, +} + +impl Serialize for TypeContainer { + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where + S: Serializer { + let mut map = serializer.serialize_map(Some(1))?; + map.serialize_entry("type", &self.t)?; + map.end() + } +} + +#[derive(Serialize)] +pub struct NotificationAPI { + id: u64, + time_create: u64, + seen: bool, + from_user_id: u64, + dest_user_id: u64, + on_elem_id: u64, + on_elem_type: String, + #[serde(flatten)] + type_container: TypeContainer, + event_visibility: String, + from_container_id: Option, + from_container_type: Option, +} + +impl NotificationAPI { + pub fn new(n: &Notification) -> NotificationAPI { + NotificationAPI { + id: n.id, + time_create: n.time_create, + seen: n.seen, + from_user_id: n.from_user_id.id(), + dest_user_id: n.dest_user_id.id(), + on_elem_id: n.on_elem_id, + on_elem_type: n.on_elem_type.to_api(), + type_container: TypeContainer { t: n.kind.to_api() }, + event_visibility: n.visibility.to_api(), + from_container_id: n.container_id, + from_container_type: n.container_type.as_ref().map(|f| f.to_api()), + } + } + + pub fn for_list(l: &Vec) -> Vec { + l.iter().map(Self::new).collect() + } +} \ No newline at end of file diff --git a/src/controllers/notifications_controller.rs b/src/controllers/notifications_controller.rs index 79fd007..b020825 100644 --- a/src/controllers/notifications_controller.rs +++ b/src/controllers/notifications_controller.rs @@ -2,6 +2,7 @@ //! //! @author Pierre Hubert +use crate::api_data::notification_api::NotificationAPI; use crate::api_data::res_count_all_unreads::ResCountAllUnread; use crate::api_data::res_number_unread_notifications::ResNumberUnreadNotifications; use crate::controllers::routes::RequestResult; @@ -24,4 +25,11 @@ pub fn count_all_news(r: &mut HttpRequestHandler) -> RequestResult { }; r.set_response(ResCountAllUnread::new(notifications, conversations as u64, friends_requests)) +} + +/// Get the list of unread notifications +pub fn get_list_unread(r: &mut HttpRequestHandler) -> RequestResult { + let list = notifications_helper::get_list_unread(r.user_id_ref()?)?; + + r.set_response(NotificationAPI::for_list(&list)) } \ No newline at end of file diff --git a/src/controllers/routes.rs b/src/controllers/routes.rs index 60bb809..3123acb 100644 --- a/src/controllers/routes.rs +++ b/src/controllers/routes.rs @@ -249,6 +249,9 @@ pub fn get_routes() -> Vec { Route::post("/notifications/count_all_news", Box::new(notifications_controller::count_all_news)), + Route::post("/notifications/get_list_unread", Box::new(notifications_controller::get_list_unread)), + + // Movies controller Route::post("/movies/get_list", Box::new(movies_controller::get_list)), diff --git a/src/data/mod.rs b/src/data/mod.rs index 8b40982..9020438 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -23,4 +23,5 @@ pub mod post; pub mod movie; pub mod survey; pub mod comment; -pub mod new_survey; \ No newline at end of file +pub mod new_survey; +pub mod notification; \ No newline at end of file diff --git a/src/data/notification.rs b/src/data/notification.rs new file mode 100644 index 0000000..97c615f --- /dev/null +++ b/src/data/notification.rs @@ -0,0 +1,154 @@ +//! # Notification +//! +//! @author Pierre Hubert + +use crate::data::user::UserID; + +#[allow(non_camel_case_types)] +pub enum NotifElemType { + UNKNOWN, + USER_PAGE, + GROUP_PAGE, + CONVERSATION, + CONVERSATION_MESSAGE, + POST, + COMMENT, + FRIENDSHIP_REQUEST, + GROUP_MEMBERSHIP, +} + +impl NotifElemType { + pub fn from_db(d: &str) -> NotifElemType { + match d { + "user_page" => NotifElemType::USER_PAGE, + "group_page" => NotifElemType::GROUP_PAGE, + "conversation" => NotifElemType::CONVERSATION, + "conversation_message" => NotifElemType::CONVERSATION_MESSAGE, + "post" => NotifElemType::POST, + "comment" => NotifElemType::COMMENT, + "friend_request" => NotifElemType::FRIENDSHIP_REQUEST, + "group_membership" => NotifElemType::GROUP_MEMBERSHIP, + _ => NotifElemType::UNKNOWN, + } + } + + pub fn to_db(&self) -> String { + match self { + NotifElemType::USER_PAGE => "user_page", + NotifElemType::GROUP_PAGE => "group_page", + NotifElemType::CONVERSATION => "conversation", + NotifElemType::CONVERSATION_MESSAGE => "conversation_message", + NotifElemType::POST => "post", + NotifElemType::COMMENT => "comment", + NotifElemType::FRIENDSHIP_REQUEST => "friend_request", + NotifElemType::GROUP_MEMBERSHIP => "group_membership", + NotifElemType::UNKNOWN => "", + }.to_string() + } + + pub fn to_api(&self) -> String { + self.to_db() + } +} + + +#[allow(non_camel_case_types)] +pub enum NotifEventType { + UNKNOWN, + COMMENT_CREATED, + SENT_FRIEND_REQUEST, + ACCEPTED_FRIEND_REQUEST, + REJECTED_FRIEND_REQUEST, + ELEM_CREATED, + ELEM_UPDATED, + SENT_GROUP_MEMBERSHIP_INVITATION, + ACCEPTED_GROUP_MEMBERSHIP_INVITATION, + REJECTED_GROUP_MEMBERSHIP_INVITATION, + SENT_GROUP_MEMBERSHIP_REQUEST, + ACCEPTED_GROUP_MEMBERSHIP_REQUEST, + REJECTED_GROUP_MEMBERSHIP_REQUEST, +} + +impl NotifEventType { + pub fn from_db(d: &str) -> NotifEventType { + match d { + "comment_created" => NotifEventType::COMMENT_CREATED, + "sent_friend_request" => NotifEventType::SENT_FRIEND_REQUEST, + "accepted_friend_request" => NotifEventType::ACCEPTED_FRIEND_REQUEST, + "rejected_friend_request" => NotifEventType::REJECTED_FRIEND_REQUEST, + "elem_created" => NotifEventType::ELEM_CREATED, + "elem_updated" => NotifEventType::ELEM_UPDATED, + "sent_group_membership_invitation" => NotifEventType::SENT_GROUP_MEMBERSHIP_INVITATION, + "accepted_group_membership_invitation" => NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_INVITATION, + "rejected_group_membership_invitation" => NotifEventType::REJECTED_GROUP_MEMBERSHIP_INVITATION, + "sent_group_membership_request" => NotifEventType::SENT_GROUP_MEMBERSHIP_REQUEST, + "accepted_group_membership_request" => NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_REQUEST, + "rejected_group_membership_request" => NotifEventType::REJECTED_GROUP_MEMBERSHIP_REQUEST, + _ => NotifEventType::UNKNOWN, + } + } + + pub fn to_db(&self) -> String { + match self { + NotifEventType::COMMENT_CREATED => "comment_created", + NotifEventType::SENT_FRIEND_REQUEST => "sent_friend_request", + NotifEventType::ACCEPTED_FRIEND_REQUEST => "accepted_friend_request", + NotifEventType::REJECTED_FRIEND_REQUEST => "rejected_friend_request", + NotifEventType::ELEM_CREATED => "elem_created", + NotifEventType::ELEM_UPDATED => "elem_updated", + NotifEventType::SENT_GROUP_MEMBERSHIP_INVITATION => "sent_group_membership_invitation", + NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_INVITATION => "accepted_group_membership_invitation", + NotifEventType::REJECTED_GROUP_MEMBERSHIP_INVITATION => "rejected_group_membership_invitation", + NotifEventType::SENT_GROUP_MEMBERSHIP_REQUEST => "sent_group_membership_request", + NotifEventType::ACCEPTED_GROUP_MEMBERSHIP_REQUEST => "accepted_group_membership_request", + NotifEventType::REJECTED_GROUP_MEMBERSHIP_REQUEST => "rejected_group_membership_request", + NotifEventType::UNKNOWN => "" + }.to_string() + } + + pub fn to_api(&self) -> String { + self.to_db() + } +} + + +#[allow(non_camel_case_types)] +pub enum NotifEventVisibility { + EVENT_PRIVATE, + EVENT_PUBLIC, +} + +impl NotifEventVisibility { + pub fn from_db(d: &str) -> NotifEventVisibility { + match d { + "event_public" => NotifEventVisibility::EVENT_PUBLIC, + "event_private" => NotifEventVisibility::EVENT_PRIVATE, + _ => NotifEventVisibility::EVENT_PRIVATE, + } + } + + pub fn to_db(&self) -> String { + match self { + NotifEventVisibility::EVENT_PUBLIC => "event_public", + NotifEventVisibility::EVENT_PRIVATE => "event_private", + }.to_string() + } + + pub fn to_api(&self) -> String { + self.to_db() + } +} + +pub struct Notification { + pub id: u64, + pub time_create: u64, + pub seen: bool, + pub from_user_id: UserID, + pub dest_user_id: UserID, + pub on_elem_id: u64, + pub on_elem_type: NotifElemType, + pub kind: NotifEventType, + pub visibility: NotifEventVisibility, + pub container_id: Option, + pub container_type: Option, +} \ No newline at end of file diff --git a/src/helpers/database.rs b/src/helpers/database.rs index aa6174a..02e5eb5 100644 --- a/src/helpers/database.rs +++ b/src/helpers/database.rs @@ -304,6 +304,14 @@ impl<'a> RowResult<'a> { } } + /// Get an optional unsigned number => Set to None if value is null / empty + pub fn get_optional_u64(&self, name: &str) -> ResultBoxError> { + match self.is_null(name)? { + true => Ok(None), + false => Ok(Some(self.get_u64(name)?)) + } + } + pub fn get_u32(&self, name: &str) -> ResultBoxError { let value = self.row.get_opt(self.find_col(name)?); diff --git a/src/helpers/notifications_helper.rs b/src/helpers/notifications_helper.rs index da07f64..936a66b 100644 --- a/src/helpers/notifications_helper.rs +++ b/src/helpers/notifications_helper.rs @@ -4,6 +4,7 @@ use crate::constants::database_tables_names::NOTIFICATIONS_TABLE; use crate::data::error::ResultBoxError; +use crate::data::notification::{NotifElemType, NotifEventType, NotifEventVisibility, Notification}; use crate::data::user::UserID; use crate::helpers::database; @@ -14,4 +15,31 @@ pub fn count_unread(user_id: &UserID) -> ResultBoxError { .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> { + 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) +} + +/// Turn a database row into a notification object +fn db_to_notif(row: &database::RowResult) -> ResultBoxError { + 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)), + }) } \ No newline at end of file