From 5b42dc3393431184293ded2f9d30a11fe5548d52 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 2 Jan 2020 16:55:54 -0500 Subject: Adding show_avatar user setting, and option to send notifications to inbox. - Fixes #254 - Fixes #394 --- .gitignore | 1 + .../down.sql | 20 ++++++ .../up.sql | 22 ++++++ server/src/api/comment.rs | 79 ++++++++++++++++++++-- server/src/api/user.rs | 10 +++ server/src/apub/mod.rs | 2 + server/src/db/comment.rs | 2 + server/src/db/comment_view.rs | 2 + server/src/db/community.rs | 2 + server/src/db/moderator.rs | 4 ++ server/src/db/password_reset_request.rs | 2 + server/src/db/post.rs | 2 + server/src/db/post_view.rs | 2 + server/src/db/user.rs | 10 +++ server/src/db/user_mention.rs | 4 ++ server/src/db/user_view.rs | 4 ++ server/src/schema.rs | 2 + ui/src/components/comment-node.tsx | 3 +- ui/src/components/main.tsx | 3 +- ui/src/components/navbar.tsx | 4 +- ui/src/components/post-listing.tsx | 3 +- ui/src/components/search.tsx | 3 +- ui/src/components/sidebar.tsx | 9 ++- ui/src/components/user.tsx | 56 ++++++++++++++- ui/src/interfaces.ts | 5 ++ ui/src/translations/en.ts | 2 + ui/src/utils.ts | 7 ++ 27 files changed, 249 insertions(+), 16 deletions(-) create mode 100644 server/migrations/2020-01-02-172755_add_show_avatar_and_email_notifications_to_user/down.sql create mode 100644 server/migrations/2020-01-02-172755_add_show_avatar_and_email_notifications_to_user/up.sql diff --git a/.gitignore b/.gitignore index 4cb8939f..16548f36 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ ansible/inventory ansible/passwords/ +docker/lemmy_mine.hjson build/ .idea/ diff --git a/server/migrations/2020-01-02-172755_add_show_avatar_and_email_notifications_to_user/down.sql b/server/migrations/2020-01-02-172755_add_show_avatar_and_email_notifications_to_user/down.sql new file mode 100644 index 00000000..ec061223 --- /dev/null +++ b/server/migrations/2020-01-02-172755_add_show_avatar_and_email_notifications_to_user/down.sql @@ -0,0 +1,20 @@ +-- Drop the columns +drop view user_view; +alter table user_ drop column show_avatars; +alter table user_ drop column send_notifications_to_email; + +-- Rebuild the view +create view user_view as +select id, +name, +avatar, +email, +fedi_name, +admin, +banned, +published, +(select count(*) from post p where p.creator_id = u.id) as number_of_posts, +(select coalesce(sum(score), 0) from post p, post_like pl where u.id = p.creator_id and p.id = pl.post_id) as post_score, +(select count(*) from comment c where c.creator_id = u.id) as number_of_comments, +(select coalesce(sum(score), 0) from comment c, comment_like cl where u.id = c.creator_id and c.id = cl.comment_id) as comment_score +from user_ u; diff --git a/server/migrations/2020-01-02-172755_add_show_avatar_and_email_notifications_to_user/up.sql b/server/migrations/2020-01-02-172755_add_show_avatar_and_email_notifications_to_user/up.sql new file mode 100644 index 00000000..21f0aff4 --- /dev/null +++ b/server/migrations/2020-01-02-172755_add_show_avatar_and_email_notifications_to_user/up.sql @@ -0,0 +1,22 @@ +-- Add columns +alter table user_ add column show_avatars boolean default true not null; +alter table user_ add column send_notifications_to_email boolean default false not null; + +-- Rebuild the user_view +drop view user_view; +create view user_view as +select id, +name, +avatar, +email, +fedi_name, +admin, +banned, +show_avatars, +send_notifications_to_email, +published, +(select count(*) from post p where p.creator_id = u.id) as number_of_posts, +(select coalesce(sum(score), 0) from post p, post_like pl where u.id = p.creator_id and p.id = pl.post_id) as post_score, +(select count(*) from comment c where c.creator_id = u.id) as number_of_comments, +(select coalesce(sum(score), 0) from comment c, comment_like cl where u.id = c.creator_id and c.id = cl.comment_id) as comment_score +from user_ u; diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs index ed658985..5c3417dd 100644 --- a/server/src/api/comment.rs +++ b/server/src/api/comment.rs @@ -1,4 +1,6 @@ use super::*; +use crate::send_email; +use crate::settings::Settings; #[derive(Serialize, Deserialize)] pub struct CreateComment { @@ -56,6 +58,8 @@ impl Perform for Oper { let user_id = claims.id; + let hostname = &format!("https://{}", Settings::get().hostname); + // Check for a community ban let post = Post::read(&conn, data.post_id)?; if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() { @@ -89,17 +93,13 @@ impl Perform for Oper { let extracted_usernames = extract_usernames(&comment_form.content); for username_mention in &extracted_usernames { - let mention_user = User_::read_from_name(&conn, (*username_mention).to_string()); - - if mention_user.is_ok() { - let mention_user_id = mention_user?.id; - + if let Ok(mention_user) = User_::read_from_name(&conn, (*username_mention).to_string()) { // You can't mention yourself // At some point, make it so you can't tag the parent creator either // This can cause two notifications, one for reply and the other for mention - if mention_user_id != user_id { + if mention_user.id != user_id { let user_mention_form = UserMentionForm { - recipient_id: mention_user_id, + recipient_id: mention_user.id, comment_id: inserted_comment.id, read: None, }; @@ -109,11 +109,76 @@ impl Perform for Oper { match UserMention::create(&conn, &user_mention_form) { Ok(_mention) => (), Err(_e) => eprintln!("{}", &_e), + }; + + // Send an email to those users that have notifications on + if mention_user.send_notifications_to_email { + if let Some(mention_email) = mention_user.email { + let subject = &format!( + "{} - Mentioned by {}", + Settings::get().hostname, + claims.username + ); + let html = &format!( + "

User Mention


{} - {}

inbox", + claims.username, comment_form.content, hostname + ); + match send_email(subject, &mention_email, &mention_user.name, html) { + Ok(_o) => _o, + Err(e) => eprintln!("{}", e), + }; + } } } } } + // Send notifs to the parent commenter / poster + match data.parent_id { + Some(parent_id) => { + let parent_comment = Comment::read(&conn, parent_id)?; + let parent_user = User_::read(&conn, parent_comment.creator_id)?; + if parent_user.send_notifications_to_email { + if let Some(comment_reply_email) = parent_user.email { + let subject = &format!( + "{} - Reply from {}", + Settings::get().hostname, + claims.username + ); + let html = &format!( + "

Comment Reply


{} - {}

inbox", + claims.username, comment_form.content, hostname + ); + match send_email(subject, &comment_reply_email, &parent_user.name, html) { + Ok(_o) => _o, + Err(e) => eprintln!("{}", e), + }; + } + } + } + // Its a post + None => { + let parent_user = User_::read(&conn, post.creator_id)?; + if parent_user.send_notifications_to_email { + if let Some(post_reply_email) = parent_user.email { + let subject = &format!( + "{} - Reply from {}", + Settings::get().hostname, + claims.username + ); + let html = &format!( + "

Post Reply


{} - {}

inbox", + claims.username, comment_form.content, hostname + ); + match send_email(subject, &post_reply_email, &parent_user.name, html) { + Ok(_o) => _o, + Err(e) => eprintln!("{}", e), + }; + } + } + } + }; + // You like your own comment by default let like_form = CommentLikeForm { comment_id: inserted_comment.id, diff --git a/server/src/api/user.rs b/server/src/api/user.rs index 912587da..41729eb7 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -32,6 +32,8 @@ pub struct SaveUserSettings { new_password: Option, new_password_verify: Option, old_password: Option, + show_avatars: bool, + send_notifications_to_email: bool, auth: String, } @@ -231,6 +233,8 @@ impl Perform for Oper { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; // Create the user @@ -356,6 +360,8 @@ impl Perform for Oper { default_sort_type: data.default_sort_type, default_listing_type: data.default_listing_type, lang: data.lang.to_owned(), + show_avatars: data.show_avatars, + send_notifications_to_email: data.send_notifications_to_email, }; let updated_user = match User_::update(&conn, user_id, &user_form) { @@ -497,6 +503,8 @@ impl Perform for Oper { default_sort_type: read_user.default_sort_type, default_listing_type: read_user.default_listing_type, lang: read_user.lang, + show_avatars: read_user.show_avatars, + send_notifications_to_email: read_user.send_notifications_to_email, }; match User_::update(&conn, data.user_id, &user_form) { @@ -560,6 +568,8 @@ impl Perform for Oper { default_sort_type: read_user.default_sort_type, default_listing_type: read_user.default_listing_type, lang: read_user.lang, + show_avatars: read_user.show_avatars, + send_notifications_to_email: read_user.send_notifications_to_email, }; match User_::update(&conn, data.user_id, &user_form) { diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs index b1a01b6d..2d2e5ad3 100644 --- a/server/src/apub/mod.rs +++ b/server/src/apub/mod.rs @@ -32,6 +32,8 @@ mod tests { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; let person = user.as_person(); diff --git a/server/src/db/comment.rs b/server/src/db/comment.rs index 64195356..b3198052 100644 --- a/server/src/db/comment.rs +++ b/server/src/db/comment.rs @@ -183,6 +183,8 @@ mod tests { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); diff --git a/server/src/db/comment_view.rs b/server/src/db/comment_view.rs index c56da51d..2942bbe7 100644 --- a/server/src/db/comment_view.rs +++ b/server/src/db/comment_view.rs @@ -381,6 +381,8 @@ mod tests { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); diff --git a/server/src/db/community.rs b/server/src/db/community.rs index b5d05384..09c3ddc4 100644 --- a/server/src/db/community.rs +++ b/server/src/db/community.rs @@ -229,6 +229,8 @@ mod tests { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); diff --git a/server/src/db/moderator.rs b/server/src/db/moderator.rs index 7f1c3499..dc018bd9 100644 --- a/server/src/db/moderator.rs +++ b/server/src/db/moderator.rs @@ -451,6 +451,8 @@ mod tests { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; let inserted_mod = User_::create(&conn, &new_mod).unwrap(); @@ -470,6 +472,8 @@ mod tests { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); diff --git a/server/src/db/password_reset_request.rs b/server/src/db/password_reset_request.rs index b7983f53..1664516b 100644 --- a/server/src/db/password_reset_request.rs +++ b/server/src/db/password_reset_request.rs @@ -101,6 +101,8 @@ mod tests { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); diff --git a/server/src/db/post.rs b/server/src/db/post.rs index da669ea1..084edc9b 100644 --- a/server/src/db/post.rs +++ b/server/src/db/post.rs @@ -196,6 +196,8 @@ mod tests { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); diff --git a/server/src/db/post_view.rs b/server/src/db/post_view.rs index 22e2eb14..f0638eb5 100644 --- a/server/src/db/post_view.rs +++ b/server/src/db/post_view.rs @@ -311,6 +311,8 @@ mod tests { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); diff --git a/server/src/db/user.rs b/server/src/db/user.rs index 82736f8e..b0401297 100644 --- a/server/src/db/user.rs +++ b/server/src/db/user.rs @@ -24,6 +24,8 @@ pub struct User_ { pub default_sort_type: i16, pub default_listing_type: i16, pub lang: String, + pub show_avatars: bool, + pub send_notifications_to_email: bool, } #[derive(Insertable, AsChangeset, Clone)] @@ -43,6 +45,8 @@ pub struct UserForm { pub default_sort_type: i16, pub default_listing_type: i16, pub lang: String, + pub show_avatars: bool, + pub send_notifications_to_email: bool, } impl Crud for User_ { @@ -100,6 +104,7 @@ pub struct Claims { pub default_listing_type: i16, pub lang: String, pub avatar: Option, + pub show_avatars: bool, } impl Claims { @@ -125,6 +130,7 @@ impl User_ { default_listing_type: self.default_listing_type, lang: self.lang.to_owned(), avatar: self.avatar.to_owned(), + show_avatars: self.show_avatars.to_owned(), }; encode( &Header::default(), @@ -187,6 +193,8 @@ mod tests { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -208,6 +216,8 @@ mod tests { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; let read_user = User_::read(&conn, inserted_user.id).unwrap(); diff --git a/server/src/db/user_mention.rs b/server/src/db/user_mention.rs index 5392c87a..67286779 100644 --- a/server/src/db/user_mention.rs +++ b/server/src/db/user_mention.rs @@ -77,6 +77,8 @@ mod tests { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; let inserted_user = User_::create(&conn, &new_user).unwrap(); @@ -96,6 +98,8 @@ mod tests { default_sort_type: SortType::Hot as i16, default_listing_type: ListingType::Subscribed as i16, lang: "browser".into(), + show_avatars: true, + send_notifications_to_email: false, }; let inserted_recipient = User_::create(&conn, &recipient_form).unwrap(); diff --git a/server/src/db/user_view.rs b/server/src/db/user_view.rs index 2d50b40c..e0de2230 100644 --- a/server/src/db/user_view.rs +++ b/server/src/db/user_view.rs @@ -11,6 +11,8 @@ table! { fedi_name -> Varchar, admin -> Bool, banned -> Bool, + show_avatars -> Bool, + send_notifications_to_email -> Bool, published -> Timestamp, number_of_posts -> BigInt, post_score -> BigInt, @@ -31,6 +33,8 @@ pub struct UserView { pub fedi_name: String, pub admin: bool, pub banned: bool, + pub show_avatars: bool, + pub send_notifications_to_email: bool, pub published: chrono::NaiveDateTime, pub number_of_posts: i64, pub post_score: i64, diff --git a/server/src/schema.rs b/server/src/schema.rs index 86834072..61957067 100644 --- a/server/src/schema.rs +++ b/server/src/schema.rs @@ -270,6 +270,8 @@ table! { default_sort_type -> Int2, default_listing_type -> Int2, lang -> Varchar, + show_avatars -> Bool, + send_notifications_to_email -> Bool, } } diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index b3ca682b..64bc7134 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -23,6 +23,7 @@ import { canMod, isMod, pictshareAvatarThumbnail, + showAvatars, } from '../utils'; import * as moment from 'moment'; import { MomentTime } from './moment-time'; @@ -138,7 +139,7 @@ export class CommentNode extends Component { className="text-info" to={`/u/${node.comment.creator_name}`} > - {node.comment.creator_avatar && ( + {node.comment.creator_avatar && showAvatars() && ( { {this.state.site.admins.map(admin => (
  • - {admin.avatar && ( + {admin.avatar && showAvatars() && ( { to={`/u/${UserService.Instance.user.username}`} > - {UserService.Instance.user.avatar && ( + {UserService.Instance.user.avatar && showAvatars() && ( {
  • {i18n.t('by')} - {post.creator_avatar && ( + {post.creator_avatar && showAvatars() && ( { className="text-info" to={`/u/${(i.data as UserView).name}`} > - {(i.data as UserView).avatar && ( + {(i.data as UserView).avatar && showAvatars() && ( { {this.props.moderators.map(mod => (
  • - {mod.avatar && ( + {mod.avatar && showAvatars() && ( { comment_score: null, banned: null, avatar: null, + show_avatars: null, + send_notifications_to_email: null, }, user_id: null, username: null, @@ -99,6 +102,8 @@ export class User extends Component { default_sort_type: null, default_listing_type: null, lang: null, + show_avatars: null, + send_notifications_to_email: null, auth: null, }, userSettingsLoading: null, @@ -207,7 +212,7 @@ export class User extends Component {
    - {this.state.user.avatar && ( + {this.state.user.avatar && showAvatars() && ( {
    )} +
    +
    + + +
    +
    +
    +
    + + +
    +