Refactor notifications
This commit is contained in:
parent
44172b67d5
commit
c87d490664
|
@ -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;
|
|
@ -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;
|
|
@ -123,10 +123,8 @@ impl Notify<PgConnection> for Comment {
|
||||||
fn notify(&self, conn: &PgConnection) {
|
fn notify(&self, conn: &PgConnection) {
|
||||||
for author in self.get_post(conn).get_authors(conn) {
|
for author in self.get_post(conn).get_authors(conn) {
|
||||||
Notification::insert(conn, NewNotification {
|
Notification::insert(conn, NewNotification {
|
||||||
title: "{{ data }} commented your article".to_string(),
|
kind: notification_kind::COMMENT.to_string(),
|
||||||
data: Some(self.get_author(conn).display_name.clone()),
|
object_id: self.id,
|
||||||
content: Some(self.get_post(conn).title),
|
|
||||||
link: self.ap_url.clone(),
|
|
||||||
user_id: author.id
|
user_id: author.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,12 +70,9 @@ impl FromActivity<FollowAct, PgConnection> for Follow {
|
||||||
|
|
||||||
impl Notify<PgConnection> for Follow {
|
impl Notify<PgConnection> for Follow {
|
||||||
fn notify(&self, conn: &PgConnection) {
|
fn notify(&self, conn: &PgConnection) {
|
||||||
let follower = User::get(conn, self.follower_id).unwrap();
|
|
||||||
Notification::insert(conn, NewNotification {
|
Notification::insert(conn, NewNotification {
|
||||||
title: "{{ data }} started following you".to_string(),
|
kind: notification_kind::FOLLOW.to_string(),
|
||||||
data: Some(follower.display_name.clone()),
|
object_id: self.id,
|
||||||
content: None,
|
|
||||||
link: Some(follower.ap_url),
|
|
||||||
user_id: self.following_id
|
user_id: self.following_id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,15 +89,11 @@ impl FromActivity<activity::Like, PgConnection> for Like {
|
||||||
|
|
||||||
impl Notify<PgConnection> for Like {
|
impl Notify<PgConnection> for Like {
|
||||||
fn notify(&self, conn: &PgConnection) {
|
fn notify(&self, conn: &PgConnection) {
|
||||||
let liker = User::get(conn, self.user_id).unwrap();
|
|
||||||
let post = Post::get(conn, self.post_id).unwrap();
|
let post = Post::get(conn, self.post_id).unwrap();
|
||||||
for author in post.get_authors(conn) {
|
for author in post.get_authors(conn) {
|
||||||
let post = post.clone();
|
|
||||||
Notification::insert(conn, NewNotification {
|
Notification::insert(conn, NewNotification {
|
||||||
title: "{{ data }} liked your article".to_string(),
|
kind: notification_kind::LIKE.to_string(),
|
||||||
data: Some(liker.display_name.clone()),
|
object_id: self.id,
|
||||||
content: Some(post.title),
|
|
||||||
link: Some(post.ap_url),
|
|
||||||
user_id: author.id
|
user_id: author.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,13 @@ impl Mention {
|
||||||
self.comment_id.and_then(|id| Comment::get(conn, id))
|
self.comment_id.and_then(|id| Comment::get(conn, id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_user(&self, conn: &PgConnection) -> Option<User> {
|
||||||
|
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 {
|
pub fn build_activity(conn: &PgConnection, ment: String) -> link::Mention {
|
||||||
let user = User::find_by_fqn(conn, ment.clone());
|
let user = User::find_by_fqn(conn, ment.clone());
|
||||||
let mut mention = link::Mention::default();
|
let mut mention = link::Mention::default();
|
||||||
|
@ -94,16 +101,10 @@ impl Mention {
|
||||||
|
|
||||||
impl Notify<PgConnection> for Mention {
|
impl Notify<PgConnection> for Mention {
|
||||||
fn notify(&self, conn: &PgConnection) {
|
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| {
|
self.get_mentioned(conn).map(|m| {
|
||||||
Notification::insert(conn, NewNotification {
|
Notification::insert(conn, NewNotification {
|
||||||
title: "{{ data }} mentioned you.".to_string(),
|
kind: notification_kind::MENTION.to_string(),
|
||||||
data: Some(author),
|
object_id: self.id,
|
||||||
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()))),
|
|
||||||
user_id: m.id
|
user_id: m.id
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,28 +1,39 @@
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods};
|
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 users::User;
|
||||||
use schema::notifications;
|
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)]
|
#[derive(Queryable, Identifiable, Serialize)]
|
||||||
pub struct Notification {
|
pub struct Notification {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub title: String,
|
|
||||||
pub content: Option<String>,
|
|
||||||
pub link: Option<String>,
|
|
||||||
pub user_id: i32,
|
pub user_id: i32,
|
||||||
pub creation_date: NaiveDateTime,
|
pub creation_date: NaiveDateTime,
|
||||||
pub data: Option<String>
|
pub kind: String,
|
||||||
|
pub object_id: i32
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(Insertable)]
|
||||||
#[table_name = "notifications"]
|
#[table_name = "notifications"]
|
||||||
pub struct NewNotification {
|
pub struct NewNotification {
|
||||||
pub title: String,
|
|
||||||
pub content: Option<String>,
|
|
||||||
pub link: Option<String>,
|
|
||||||
pub user_id: i32,
|
pub user_id: i32,
|
||||||
pub data: Option<String>
|
pub kind: String,
|
||||||
|
pub object_id: i32
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Notification {
|
impl Notification {
|
||||||
|
@ -44,4 +55,47 @@ impl Notification {
|
||||||
.load::<Notification>(conn)
|
.load::<Notification>(conn)
|
||||||
.expect("Couldn't load user notifications page")
|
.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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 plume_common::activity_pub::{Id, IntoId, inbox::{FromActivity, Notify, Deletable}, PUBLIC_VISIBILTY};
|
||||||
use notifications::*;
|
use notifications::*;
|
||||||
use posts::Post;
|
use posts::Post;
|
||||||
use users::User;
|
use users::User;
|
||||||
use schema::reshares;
|
use schema::reshares;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Queryable, Identifiable)]
|
#[derive(Serialize, Deserialize, Queryable, Identifiable)]
|
||||||
|
@ -55,6 +55,10 @@ impl Reshare {
|
||||||
Post::get(conn, self.post_id)
|
Post::get(conn, self.post_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_user(&self, conn: &PgConnection) -> Option<User> {
|
||||||
|
User::get(conn, self.user_id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn delete(&self, conn: &PgConnection) -> Undo {
|
pub fn delete(&self, conn: &PgConnection) -> Undo {
|
||||||
diesel::delete(self).execute(conn).unwrap();
|
diesel::delete(self).execute(conn).unwrap();
|
||||||
|
|
||||||
|
@ -96,15 +100,11 @@ impl FromActivity<Announce, PgConnection> for Reshare {
|
||||||
|
|
||||||
impl Notify<PgConnection> for Reshare {
|
impl Notify<PgConnection> for Reshare {
|
||||||
fn notify(&self, conn: &PgConnection) {
|
fn notify(&self, conn: &PgConnection) {
|
||||||
let actor = User::get(conn, self.user_id).unwrap();
|
|
||||||
let post = self.get_post(conn).unwrap();
|
let post = self.get_post(conn).unwrap();
|
||||||
for author in post.get_authors(conn) {
|
for author in post.get_authors(conn) {
|
||||||
let post = post.clone();
|
|
||||||
Notification::insert(conn, NewNotification {
|
Notification::insert(conn, NewNotification {
|
||||||
title: "{{ data }} reshared your article".to_string(),
|
kind: notification_kind::RESHARE.to_string(),
|
||||||
data: Some(actor.display_name.clone()),
|
object_id: self.id,
|
||||||
content: Some(post.title),
|
|
||||||
link: Some(post.ap_url),
|
|
||||||
user_id: author.id
|
user_id: author.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,12 +79,10 @@ table! {
|
||||||
table! {
|
table! {
|
||||||
notifications (id) {
|
notifications (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
title -> Varchar,
|
|
||||||
content -> Nullable<Text>,
|
|
||||||
link -> Nullable<Varchar>,
|
|
||||||
user_id -> Int4,
|
user_id -> Int4,
|
||||||
creation_date -> Timestamp,
|
creation_date -> Timestamp,
|
||||||
data -> Nullable<Varchar>,
|
kind -> Varchar,
|
||||||
|
object_id -> Int4,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
po/plume.pot
15
po/plume.pot
|
@ -336,3 +336,18 @@ msgstr ""
|
||||||
|
|
||||||
msgid "Next page"
|
msgid "Next page"
|
||||||
msgstr ""
|
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 ""
|
||||||
|
|
|
@ -9,7 +9,7 @@ use routes::Page;
|
||||||
fn paginated_notifications(conn: DbConn, user: User, page: Page) -> Template {
|
fn paginated_notifications(conn: DbConn, user: User, page: Page) -> Template {
|
||||||
Template::render("notifications/index", json!({
|
Template::render("notifications/index", json!({
|
||||||
"account": user,
|
"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::<Vec<_>>(),
|
||||||
"page": page.page,
|
"page": page.page,
|
||||||
"n_pages": Page::total(Notification::find_for_user(&*conn, &user).len() as i32)
|
"n_pages": Page::total(Notification::find_for_user(&*conn, &user).len() as i32)
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -10,9 +10,34 @@
|
||||||
<div class="list">
|
<div class="list">
|
||||||
{% for notification in notifications %}
|
{% for notification in notifications %}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3><a href="{% if notification.link %}{{ notification.link }}/{% else %}#{% endif %}">{{ notification.title | _(data=notification.data) }}</a></h3>
|
{% if notification.kind == "COMMENT" %}
|
||||||
{% if notification.content %}
|
<h3><a href="{{ notification.object.post.url }}#comment-{{ notification.object.id }}">
|
||||||
<p>{{ notification.content }}</p>
|
{{ "{{ user }} commented your article." | _(user=notification.object.user.display_name) }}
|
||||||
|
</a></h3>
|
||||||
|
<p><a href="{{ notification.object.post.url }}">{{ notification.object.post.title }}</a></p>
|
||||||
|
|
||||||
|
{% elif notification.kind == "FOLLOW" %}
|
||||||
|
<h3><a href="/@/{{ notification.object.follower.fqn }}/">
|
||||||
|
{{ "{{ user }} is now following you." | _(user=notification.object.follower.display_name) }}
|
||||||
|
</a></h3>
|
||||||
|
|
||||||
|
{% elif notification.kind == "LIKE" %}
|
||||||
|
<h3>
|
||||||
|
{{ "{{ user }} liked your article." | _(user=notification.object.user.display_name) }}
|
||||||
|
</h3>
|
||||||
|
<p><a href="{{ notification.object.post.url }}">{{ notification.object.post.title }}</a></p>
|
||||||
|
|
||||||
|
{% elif notification.kind == "MENTION" %}
|
||||||
|
<h3><a href="{{ notification.object.url }}">
|
||||||
|
{{ "{{ user }} mentioned you." | _(user=notification.object.user.display_name) }}
|
||||||
|
</a></h3>
|
||||||
|
|
||||||
|
{% elif notification.kind == "RESHARE" %}
|
||||||
|
<h3>
|
||||||
|
{{ "{{ user }} reshare your article." | _(user=notification.object.user.display_name) }}
|
||||||
|
</h3>
|
||||||
|
<p><a href="{{ notification.object.post.url }}">{{ notification.object.post.title }}</a></p>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
Loading…
Reference in New Issue