From c87d490664a488259f36a9deab7dc4c86ec13374 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 15:46:10 +0200 Subject: [PATCH] Refactor notifications --- .../down.sql | 8 +++ .../up.sql | 8 +++ plume-models/src/comments.rs | 6 +- plume-models/src/follows.rs | 7 +- plume-models/src/likes.rs | 8 +-- plume-models/src/mentions.rs | 17 ++--- plume-models/src/notifications.rs | 70 ++++++++++++++++--- plume-models/src/reshares.rs | 14 ++-- plume-models/src/schema.rs | 6 +- po/plume.pot | 15 ++++ src/routes/notifications.rs | 2 +- templates/notifications/index.html.tera | 31 +++++++- 12 files changed, 146 insertions(+), 46 deletions(-) create mode 100644 migrations/2018-07-25-165754_refactor_notifications/down.sql create mode 100644 migrations/2018-07-25-165754_refactor_notifications/up.sql diff --git a/migrations/2018-07-25-165754_refactor_notifications/down.sql b/migrations/2018-07-25-165754_refactor_notifications/down.sql new file mode 100644 index 00000000..53830f4a --- /dev/null +++ b/migrations/2018-07-25-165754_refactor_notifications/down.sql @@ -0,0 +1,8 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE notifications ADD COLUMN title VARCHAR NOT NULL; +ALTER TABLE notifications ADD COLUMN content TEXT; +ALTER TABLE notifications ADD COLUMN link VARCHAR; +ALTER TABLE notifications ADD COLUMN data VARCHAR; + +ALTER TABLE notifications DROP COLUMN kind; +ALTER TABLE notifications DROP COLUMN object_id; diff --git a/migrations/2018-07-25-165754_refactor_notifications/up.sql b/migrations/2018-07-25-165754_refactor_notifications/up.sql new file mode 100644 index 00000000..e3ab6641 --- /dev/null +++ b/migrations/2018-07-25-165754_refactor_notifications/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here +ALTER TABLE notifications DROP COLUMN title; +ALTER TABLE notifications DROP COLUMN content; +ALTER TABLE notifications DROP COLUMN link; +ALTER TABLE notifications DROP COLUMN data; + +ALTER TABLE notifications ADD COLUMN kind VARCHAR NOT NULL DEFAULT 'unknown'; +ALTER TABLE notifications ADD COLUMN object_id INTEGER NOT NULL DEFAULT 0; diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index f1e4b454..39926d5c 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -123,10 +123,8 @@ impl Notify for Comment { fn notify(&self, conn: &PgConnection) { for author in self.get_post(conn).get_authors(conn) { Notification::insert(conn, NewNotification { - title: "{{ data }} commented your article".to_string(), - data: Some(self.get_author(conn).display_name.clone()), - content: Some(self.get_post(conn).title), - link: self.ap_url.clone(), + kind: notification_kind::COMMENT.to_string(), + object_id: self.id, user_id: author.id }); } diff --git a/plume-models/src/follows.rs b/plume-models/src/follows.rs index 05c50a38..57e26fa4 100644 --- a/plume-models/src/follows.rs +++ b/plume-models/src/follows.rs @@ -70,12 +70,9 @@ impl FromActivity for Follow { impl Notify for Follow { fn notify(&self, conn: &PgConnection) { - let follower = User::get(conn, self.follower_id).unwrap(); Notification::insert(conn, NewNotification { - title: "{{ data }} started following you".to_string(), - data: Some(follower.display_name.clone()), - content: None, - link: Some(follower.ap_url), + kind: notification_kind::FOLLOW.to_string(), + object_id: self.id, user_id: self.following_id }); } diff --git a/plume-models/src/likes.rs b/plume-models/src/likes.rs index e8e084d2..651b4d8d 100644 --- a/plume-models/src/likes.rs +++ b/plume-models/src/likes.rs @@ -89,15 +89,11 @@ impl FromActivity for Like { impl Notify for Like { fn notify(&self, conn: &PgConnection) { - let liker = User::get(conn, self.user_id).unwrap(); let post = Post::get(conn, self.post_id).unwrap(); for author in post.get_authors(conn) { - let post = post.clone(); Notification::insert(conn, NewNotification { - title: "{{ data }} liked your article".to_string(), - data: Some(liker.display_name.clone()), - content: Some(post.title), - link: Some(post.ap_url), + kind: notification_kind::LIKE.to_string(), + object_id: self.id, user_id: author.id }); } diff --git a/plume-models/src/mentions.rs b/plume-models/src/mentions.rs index 0d2e586c..c338a5cb 100644 --- a/plume-models/src/mentions.rs +++ b/plume-models/src/mentions.rs @@ -46,6 +46,13 @@ impl Mention { self.comment_id.and_then(|id| Comment::get(conn, id)) } + pub fn get_user(&self, conn: &PgConnection) -> Option { + match self.get_post(conn) { + Some(p) => p.get_authors(conn).into_iter().next(), + None => self.get_comment(conn).map(|c| c.get_author(conn)) + } + } + pub fn build_activity(conn: &PgConnection, ment: String) -> link::Mention { let user = User::find_by_fqn(conn, ment.clone()); let mut mention = link::Mention::default(); @@ -94,16 +101,10 @@ impl Mention { impl Notify for Mention { fn notify(&self, conn: &PgConnection) { - let author = self.get_comment(conn) - .map(|c| c.get_author(conn).display_name.clone()) - .unwrap_or_else(|| self.get_post(conn).unwrap().get_authors(conn)[0].display_name.clone()); - self.get_mentioned(conn).map(|m| { Notification::insert(conn, NewNotification { - title: "{{ data }} mentioned you.".to_string(), - data: Some(author), - content: None, - link: Some(self.get_post(conn).map(|p| p.ap_url).unwrap_or_else(|| self.get_comment(conn).unwrap().ap_url.unwrap_or(String::new()))), + kind: notification_kind::MENTION.to_string(), + object_id: self.id, user_id: m.id }); }); diff --git a/plume-models/src/notifications.rs b/plume-models/src/notifications.rs index 07f0bdb2..33b06c1a 100644 --- a/plume-models/src/notifications.rs +++ b/plume-models/src/notifications.rs @@ -1,28 +1,39 @@ use chrono::NaiveDateTime; use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods}; +use serde_json; +use comments::Comment; +use follows::Follow; +use likes::Like; +use mentions::Mention; +use posts::Post; +use reshares::Reshare; use users::User; use schema::notifications; +pub mod notification_kind { + pub const COMMENT: &'static str = "COMMENT"; + pub const FOLLOW: &'static str = "FOLLOW"; + pub const LIKE: &'static str = "LIKE"; + pub const MENTION: &'static str = "MENTION"; + pub const RESHARE: &'static str = "RESHARE"; +} + #[derive(Queryable, Identifiable, Serialize)] pub struct Notification { pub id: i32, - pub title: String, - pub content: Option, - pub link: Option, pub user_id: i32, pub creation_date: NaiveDateTime, - pub data: Option + pub kind: String, + pub object_id: i32 } #[derive(Insertable)] #[table_name = "notifications"] pub struct NewNotification { - pub title: String, - pub content: Option, - pub link: Option, pub user_id: i32, - pub data: Option + pub kind: String, + pub object_id: i32 } impl Notification { @@ -44,4 +55,47 @@ impl Notification { .load::(conn) .expect("Couldn't load user notifications page") } + + pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { + let mut json = json!(self); + json["object"] = json!(match self.kind.as_ref() { + notification_kind::COMMENT => Comment::get(conn, self.object_id).map(|comment| + json!({ + "post": comment.get_post(conn).to_json(conn), + "user": comment.get_author(conn).to_json(conn), + "id": comment.id + }) + ), + notification_kind::FOLLOW => Follow::get(conn, self.object_id).map(|follow| + json!({ + "follower": User::get(conn, follow.follower_id).map(|u| u.to_json(conn)) + }) + ), + notification_kind::LIKE => Like::get(conn, self.object_id).map(|like| + json!({ + "post": Post::get(conn, like.post_id).map(|p| p.to_json(conn)), + "user": User::get(conn, like.user_id).map(|u| u.to_json(conn)) + }) + ), + notification_kind::MENTION => Mention::get(conn, self.object_id).map(|mention| + json!({ + "user": mention.get_user(conn).map(|u| u.to_json(conn)), + "url": mention.get_post(conn).map(|p| p.to_json(conn)["url"].clone()) + .unwrap_or_else(|| { + let comment = mention.get_comment(conn).expect("No comment nor post for mention"); + let post = comment.get_post(conn).to_json(conn); + json!(format!("{}#comment-{}", post["url"].as_str().unwrap(), comment.id)) + }) + }) + ), + notification_kind::RESHARE => Reshare::get(conn, self.object_id).map(|reshare| + json!({ + "post": reshare.get_post(conn).map(|p| p.to_json(conn)), + "user": reshare.get_user(conn).map(|u| u.to_json(conn)) + }) + ), + _ => Some(json!({})) + }); + json + } } diff --git a/plume-models/src/reshares.rs b/plume-models/src/reshares.rs index 335baf84..2acb0a44 100644 --- a/plume-models/src/reshares.rs +++ b/plume-models/src/reshares.rs @@ -5,7 +5,7 @@ use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods}; use plume_common::activity_pub::{Id, IntoId, inbox::{FromActivity, Notify, Deletable}, PUBLIC_VISIBILTY}; use notifications::*; use posts::Post; -use users::User; +use users::User; use schema::reshares; #[derive(Serialize, Deserialize, Queryable, Identifiable)] @@ -55,6 +55,10 @@ impl Reshare { Post::get(conn, self.post_id) } + pub fn get_user(&self, conn: &PgConnection) -> Option { + User::get(conn, self.user_id) + } + pub fn delete(&self, conn: &PgConnection) -> Undo { diesel::delete(self).execute(conn).unwrap(); @@ -96,15 +100,11 @@ impl FromActivity for Reshare { impl Notify for Reshare { fn notify(&self, conn: &PgConnection) { - let actor = User::get(conn, self.user_id).unwrap(); let post = self.get_post(conn).unwrap(); for author in post.get_authors(conn) { - let post = post.clone(); Notification::insert(conn, NewNotification { - title: "{{ data }} reshared your article".to_string(), - data: Some(actor.display_name.clone()), - content: Some(post.title), - link: Some(post.ap_url), + kind: notification_kind::RESHARE.to_string(), + object_id: self.id, user_id: author.id }); } diff --git a/plume-models/src/schema.rs b/plume-models/src/schema.rs index 42184146..c19136d0 100644 --- a/plume-models/src/schema.rs +++ b/plume-models/src/schema.rs @@ -79,12 +79,10 @@ table! { table! { notifications (id) { id -> Int4, - title -> Varchar, - content -> Nullable, - link -> Nullable, user_id -> Int4, creation_date -> Timestamp, - data -> Nullable, + kind -> Varchar, + object_id -> Int4, } } diff --git a/po/plume.pot b/po/plume.pot index 197db640..31806f63 100644 --- a/po/plume.pot +++ b/po/plume.pot @@ -336,3 +336,18 @@ msgstr "" msgid "Next page" msgstr "" + +msgid "{{ user }} mentioned you." +msgstr "" + +msgid "{{ user }} commented your article." +msgstr "" + +msgid "{{ user }} is now following you." +msgstr "" + +msgid "{{ user }} liked your article." +msgstr "" + +msgstr "{{ user }} reshare your article." +msgid "" diff --git a/src/routes/notifications.rs b/src/routes/notifications.rs index de414944..64def8da 100644 --- a/src/routes/notifications.rs +++ b/src/routes/notifications.rs @@ -9,7 +9,7 @@ use routes::Page; fn paginated_notifications(conn: DbConn, user: User, page: Page) -> Template { Template::render("notifications/index", json!({ "account": user, - "notifications": Notification::page_for_user(&*conn, &user, page.limits()), + "notifications": Notification::page_for_user(&*conn, &user, page.limits()).into_iter().map(|n| n.to_json(&*conn)).collect::>(), "page": page.page, "n_pages": Page::total(Notification::find_for_user(&*conn, &user).len() as i32) })) diff --git a/templates/notifications/index.html.tera b/templates/notifications/index.html.tera index 97034a50..8c4f194e 100644 --- a/templates/notifications/index.html.tera +++ b/templates/notifications/index.html.tera @@ -10,9 +10,34 @@
{% for notification in notifications %}
-

{{ notification.title | _(data=notification.data) }}

- {% if notification.content %} -

{{ notification.content }}

+ {% if notification.kind == "COMMENT" %} +

+ {{ "{{ user }} commented your article." | _(user=notification.object.user.display_name) }} +

+

{{ notification.object.post.title }}

+ + {% elif notification.kind == "FOLLOW" %} +

+ {{ "{{ user }} is now following you." | _(user=notification.object.follower.display_name) }} +

+ + {% elif notification.kind == "LIKE" %} +

+ {{ "{{ user }} liked your article." | _(user=notification.object.user.display_name) }} +

+

{{ notification.object.post.title }}

+ + {% elif notification.kind == "MENTION" %} +

+ {{ "{{ user }} mentioned you." | _(user=notification.object.user.display_name) }} +

+ + {% elif notification.kind == "RESHARE" %} +

+ {{ "{{ user }} reshare your article." | _(user=notification.object.user.display_name) }} +

+

{{ notification.object.post.title }}

+ {% endif %}
{% endfor %}