From a4428528e30b18eb85596edf9c26bc8b6b7d11ee Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 29 Dec 2019 15:39:48 -0500 Subject: Adding user avatars / icons. Requires pictshare. - Fixes #188 --- README.md | 18 +- .../2019-12-29-164820_add_avatar/down.sql | 224 ++++++++++++++++++++ .../migrations/2019-12-29-164820_add_avatar/up.sql | 234 +++++++++++++++++++++ server/src/api/user.rs | 13 +- server/src/apub/mod.rs | 2 +- server/src/db/comment.rs | 1 + server/src/db/comment_view.rs | 7 + server/src/db/community.rs | 1 + server/src/db/community_view.rs | 8 + server/src/db/moderator.rs | 2 + server/src/db/password_reset_request.rs | 1 + server/src/db/post.rs | 1 + server/src/db/post_view.rs | 5 + server/src/db/site_view.rs | 2 + server/src/db/user.rs | 8 +- server/src/db/user_mention.rs | 2 + server/src/db/user_mention_view.rs | 4 +- server/src/db/user_view.rs | 2 + server/src/schema.rs | 2 +- ui/.eslintignore | 2 + ui/fuse.js | 26 +-- ui/src/components/comment-node.tsx | 18 +- ui/src/components/main.tsx | 14 +- ui/src/components/navbar.tsx | 16 +- ui/src/components/post-listing.tsx | 11 +- ui/src/components/search.tsx | 15 +- ui/src/components/sidebar.tsx | 12 +- ui/src/components/user.tsx | 82 +++++++- ui/src/interfaces.ts | 7 + ui/src/translations/en.ts | 1 + ui/src/utils.ts | 7 + ui/translation_report.ts | 20 +- 32 files changed, 721 insertions(+), 47 deletions(-) create mode 100644 server/migrations/2019-12-29-164820_add_avatar/down.sql create mode 100644 server/migrations/2019-12-29-164820_add_avatar/up.sql create mode 100644 ui/.eslintignore diff --git a/README.md b/README.md index e3f85eb6..5407ac0b 100644 --- a/README.md +++ b/README.md @@ -257,15 +257,15 @@ If you'd like to add translations, take a look a look at the [English translatio lang | done | missing --- | --- | --- -de | 100% | -eo | 86% | number_of_communities,preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,theme,are_you_sure,yes,no -es | 95% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default -fr | 95% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default -it | 96% | archive_link,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default -nl | 88% | preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,theme -ru | 82% | cross_posts,cross_post,number_of_communities,preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,recent_comments,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no -sv | 95% | archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default -zh | 80% | cross_posts,cross_post,users,number_of_communities,preview,upload_image,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,recent_comments,nsfw,show_nsfw,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no +de | 97% | avatar,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw +eo | 84% | number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,are_you_sure,yes,no +es | 92% | avatar,archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw +fr | 92% | avatar,archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw +it | 93% | avatar,archive_link,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw +nl | 86% | preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme +ru | 80% | cross_posts,cross_post,number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no +sv | 92% | avatar,archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw +zh | 78% | cross_posts,cross_post,users,number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no If you'd like to update this report, run: diff --git a/server/migrations/2019-12-29-164820_add_avatar/down.sql b/server/migrations/2019-12-29-164820_add_avatar/down.sql new file mode 100644 index 00000000..74b4146d --- /dev/null +++ b/server/migrations/2019-12-29-164820_add_avatar/down.sql @@ -0,0 +1,224 @@ +-- the views +drop view user_mention_view; +drop view reply_view; +drop view comment_view; +drop view user_view; + +-- user +create view user_view as +select id, +name, +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; + +-- post +-- Recreate the view +drop view post_view; +create view post_view as +with all_post as +( + select + p.*, + (select u.banned from user_ u where p.creator_id = u.id) as banned, + (select cb.id::bool from community_user_ban cb where p.creator_id = cb.user_id and p.community_id = cb.community_id) as banned_from_community, + (select name from user_ where p.creator_id = user_.id) as creator_name, + (select name from community where p.community_id = community.id) as community_name, + (select removed from community c where p.community_id = c.id) as community_removed, + (select deleted from community c where p.community_id = c.id) as community_deleted, + (select nsfw from community c where p.community_id = c.id) as community_nsfw, + (select count(*) from comment where comment.post_id = p.id) as number_of_comments, + coalesce(sum(pl.score), 0) as score, + count (case when pl.score = 1 then 1 else null end) as upvotes, + count (case when pl.score = -1 then 1 else null end) as downvotes, + hot_rank(coalesce(sum(pl.score) , 0), p.published) as hot_rank + from post p + left join post_like pl on p.id = pl.post_id + group by p.id +) + +select +ap.*, +u.id as user_id, +coalesce(pl.score, 0) as my_vote, +(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed, +(select pr.id::bool from post_read pr where u.id = pr.user_id and pr.post_id = ap.id) as read, +(select ps.id::bool from post_saved ps where u.id = ps.user_id and ps.post_id = ap.id) as saved +from user_ u +cross join all_post ap +left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id + +union all + +select +ap.*, +null as user_id, +null as my_vote, +null as subscribed, +null as read, +null as saved +from all_post ap +; + +-- community + +drop view community_view; +create view community_view as +with all_community as +( + select *, + (select name from user_ u where c.creator_id = u.id) as creator_name, + (select name from category ct where c.category_id = ct.id) as category_name, + (select count(*) from community_follower cf where cf.community_id = c.id) as number_of_subscribers, + (select count(*) from post p where p.community_id = c.id) as number_of_posts, + (select count(*) from comment co, post p where c.id = p.community_id and p.id = co.post_id) as number_of_comments, + hot_rank((select count(*) from community_follower cf where cf.community_id = c.id), c.published) as hot_rank + from community c +) + +select +ac.*, +u.id as user_id, +(select cf.id::boolean from community_follower cf where u.id = cf.user_id and ac.id = cf.community_id) as subscribed +from user_ u +cross join all_community ac + +union all + +select +ac.*, +null as user_id, +null as subscribed +from all_community ac +; + +-- Reply and comment view +create view comment_view as +with all_comment as +( + select + c.*, + (select community_id from post p where p.id = c.post_id), + (select u.banned from user_ u where c.creator_id = u.id) as banned, + (select cb.id::bool from community_user_ban cb, post p where c.creator_id = cb.user_id and p.id = c.post_id and p.community_id = cb.community_id) as banned_from_community, + (select name from user_ where c.creator_id = user_.id) as creator_name, + coalesce(sum(cl.score), 0) as score, + count (case when cl.score = 1 then 1 else null end) as upvotes, + count (case when cl.score = -1 then 1 else null end) as downvotes + from comment c + left join comment_like cl on c.id = cl.comment_id + group by c.id +) + +select +ac.*, +u.id as user_id, +coalesce(cl.score, 0) as my_vote, +(select cs.id::bool from comment_saved cs where u.id = cs.user_id and cs.comment_id = ac.id) as saved +from user_ u +cross join all_comment ac +left join comment_like cl on u.id = cl.user_id and ac.id = cl.comment_id + +union all + +select + ac.*, + null as user_id, + null as my_vote, + null as saved +from all_comment ac +; + +create view reply_view as +with closereply as ( + select + c2.id, + c2.creator_id as sender_id, + c.creator_id as recipient_id + from comment c + inner join comment c2 on c.id = c2.parent_id + where c2.creator_id != c.creator_id + -- Do union where post is null + union + select + c.id, + c.creator_id as sender_id, + p.creator_id as recipient_id + from comment c, post p + where c.post_id = p.id and c.parent_id is null and c.creator_id != p.creator_id +) +select cv.*, +closereply.recipient_id +from comment_view cv, closereply +where closereply.id = cv.id +; + +-- user mention +create view user_mention_view as +select + c.id, + um.id as user_mention_id, + c.creator_id, + c.post_id, + c.parent_id, + c.content, + c.removed, + um.read, + c.published, + c.updated, + c.deleted, + c.community_id, + c.banned, + c.banned_from_community, + c.creator_name, + c.score, + c.upvotes, + c.downvotes, + c.user_id, + c.my_vote, + c.saved, + um.recipient_id +from user_mention um, comment_view c +where um.comment_id = c.id; + +-- community tables +drop view community_moderator_view; +drop view community_follower_view; +drop view community_user_ban_view; +drop view site_view; + +create view community_moderator_view as +select *, +(select name from user_ u where cm.user_id = u.id) as user_name, +(select name from community c where cm.community_id = c.id) as community_name +from community_moderator cm; + +create view community_follower_view as +select *, +(select name from user_ u where cf.user_id = u.id) as user_name, +(select name from community c where cf.community_id = c.id) as community_name +from community_follower cf; + +create view community_user_ban_view as +select *, +(select name from user_ u where cm.user_id = u.id) as user_name, +(select name from community c where cm.community_id = c.id) as community_name +from community_user_ban cm; + +create view site_view as +select *, +(select name from user_ u where s.creator_id = u.id) as creator_name, +(select count(*) from user_) as number_of_users, +(select count(*) from post) as number_of_posts, +(select count(*) from comment) as number_of_comments, +(select count(*) from community) as number_of_communities +from site s; + +alter table user_ rename column avatar to icon; +alter table user_ alter column icon type bytea using icon::bytea; diff --git a/server/migrations/2019-12-29-164820_add_avatar/up.sql b/server/migrations/2019-12-29-164820_add_avatar/up.sql new file mode 100644 index 00000000..f9265154 --- /dev/null +++ b/server/migrations/2019-12-29-164820_add_avatar/up.sql @@ -0,0 +1,234 @@ +-- Rename to avatar +alter table user_ rename column icon to avatar; +alter table user_ alter column avatar type text; + +-- Rebuild nearly all the views, to include the creator avatars + +-- user +drop view user_view; +create view user_view as +select id, +name, +avatar, +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; + +-- post +-- Recreate the view +drop view post_view; +create view post_view as +with all_post as +( + select + p.*, + (select u.banned from user_ u where p.creator_id = u.id) as banned, + (select cb.id::bool from community_user_ban cb where p.creator_id = cb.user_id and p.community_id = cb.community_id) as banned_from_community, + (select name from user_ where p.creator_id = user_.id) as creator_name, + (select avatar from user_ where p.creator_id = user_.id) as creator_avatar, + (select name from community where p.community_id = community.id) as community_name, + (select removed from community c where p.community_id = c.id) as community_removed, + (select deleted from community c where p.community_id = c.id) as community_deleted, + (select nsfw from community c where p.community_id = c.id) as community_nsfw, + (select count(*) from comment where comment.post_id = p.id) as number_of_comments, + coalesce(sum(pl.score), 0) as score, + count (case when pl.score = 1 then 1 else null end) as upvotes, + count (case when pl.score = -1 then 1 else null end) as downvotes, + hot_rank(coalesce(sum(pl.score) , 0), p.published) as hot_rank + from post p + left join post_like pl on p.id = pl.post_id + group by p.id +) + +select +ap.*, +u.id as user_id, +coalesce(pl.score, 0) as my_vote, +(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed, +(select pr.id::bool from post_read pr where u.id = pr.user_id and pr.post_id = ap.id) as read, +(select ps.id::bool from post_saved ps where u.id = ps.user_id and ps.post_id = ap.id) as saved +from user_ u +cross join all_post ap +left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id + +union all + +select +ap.*, +null as user_id, +null as my_vote, +null as subscribed, +null as read, +null as saved +from all_post ap +; + + +-- community +drop view community_view; +create view community_view as +with all_community as +( + select *, + (select name from user_ u where c.creator_id = u.id) as creator_name, + (select avatar from user_ u where c.creator_id = u.id) as creator_avatar, + (select name from category ct where c.category_id = ct.id) as category_name, + (select count(*) from community_follower cf where cf.community_id = c.id) as number_of_subscribers, + (select count(*) from post p where p.community_id = c.id) as number_of_posts, + (select count(*) from comment co, post p where c.id = p.community_id and p.id = co.post_id) as number_of_comments, + hot_rank((select count(*) from community_follower cf where cf.community_id = c.id), c.published) as hot_rank + from community c +) + +select +ac.*, +u.id as user_id, +(select cf.id::boolean from community_follower cf where u.id = cf.user_id and ac.id = cf.community_id) as subscribed +from user_ u +cross join all_community ac + +union all + +select +ac.*, +null as user_id, +null as subscribed +from all_community ac +; + +-- reply and comment view +drop view reply_view; +drop view user_mention_view; +drop view comment_view; +create view comment_view as +with all_comment as +( + select + c.*, + (select community_id from post p where p.id = c.post_id), + (select u.banned from user_ u where c.creator_id = u.id) as banned, + (select cb.id::bool from community_user_ban cb, post p where c.creator_id = cb.user_id and p.id = c.post_id and p.community_id = cb.community_id) as banned_from_community, + (select name from user_ where c.creator_id = user_.id) as creator_name, + (select avatar from user_ where c.creator_id = user_.id) as creator_avatar, + coalesce(sum(cl.score), 0) as score, + count (case when cl.score = 1 then 1 else null end) as upvotes, + count (case when cl.score = -1 then 1 else null end) as downvotes + from comment c + left join comment_like cl on c.id = cl.comment_id + group by c.id +) + +select +ac.*, +u.id as user_id, +coalesce(cl.score, 0) as my_vote, +(select cs.id::bool from comment_saved cs where u.id = cs.user_id and cs.comment_id = ac.id) as saved +from user_ u +cross join all_comment ac +left join comment_like cl on u.id = cl.user_id and ac.id = cl.comment_id + +union all + +select + ac.*, + null as user_id, + null as my_vote, + null as saved +from all_comment ac +; + +create view reply_view as +with closereply as ( + select + c2.id, + c2.creator_id as sender_id, + c.creator_id as recipient_id + from comment c + inner join comment c2 on c.id = c2.parent_id + where c2.creator_id != c.creator_id + -- Do union where post is null + union + select + c.id, + c.creator_id as sender_id, + p.creator_id as recipient_id + from comment c, post p + where c.post_id = p.id and c.parent_id is null and c.creator_id != p.creator_id +) +select cv.*, +closereply.recipient_id +from comment_view cv, closereply +where closereply.id = cv.id +; + +-- user mention +create view user_mention_view as +select + c.id, + um.id as user_mention_id, + c.creator_id, + c.post_id, + c.parent_id, + c.content, + c.removed, + um.read, + c.published, + c.updated, + c.deleted, + c.community_id, + c.banned, + c.banned_from_community, + c.creator_name, + c.creator_avatar, + c.score, + c.upvotes, + c.downvotes, + c.user_id, + c.my_vote, + c.saved, + um.recipient_id +from user_mention um, comment_view c +where um.comment_id = c.id; + +-- community views +drop view community_moderator_view; +drop view community_follower_view; +drop view community_user_ban_view; +drop view site_view; + +create view community_moderator_view as +select *, +(select name from user_ u where cm.user_id = u.id) as user_name, +(select avatar from user_ u where cm.user_id = u.id), +(select name from community c where cm.community_id = c.id) as community_name +from community_moderator cm; + +create view community_follower_view as +select *, +(select name from user_ u where cf.user_id = u.id) as user_name, +(select avatar from user_ u where cf.user_id = u.id), +(select name from community c where cf.community_id = c.id) as community_name +from community_follower cf; + +create view community_user_ban_view as +select *, +(select name from user_ u where cm.user_id = u.id) as user_name, +(select avatar from user_ u where cm.user_id = u.id), +(select name from community c where cm.community_id = c.id) as community_name +from community_user_ban cm; + +create view site_view as +select *, +(select name from user_ u where s.creator_id = u.id) as creator_name, +(select avatar from user_ u where s.creator_id = u.id) as creator_avatar, +(select count(*) from user_) as number_of_users, +(select count(*) from post) as number_of_posts, +(select count(*) from comment) as number_of_comments, +(select count(*) from community) as number_of_communities +from site s; diff --git a/server/src/api/user.rs b/server/src/api/user.rs index a04ba4b2..e8ad20aa 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -27,6 +27,7 @@ pub struct SaveUserSettings { default_sort_type: i16, default_listing_type: i16, lang: String, + avatar: Option, auth: String, } @@ -220,6 +221,7 @@ impl Perform for Oper { name: data.username.to_owned(), fedi_name: Settings::get().hostname.to_owned(), email: data.email.to_owned(), + avatar: None, password_encrypted: data.password.to_owned(), preferred_username: None, updated: None, @@ -314,6 +316,7 @@ impl Perform for Oper { name: read_user.name, fedi_name: read_user.fedi_name, email: read_user.email, + avatar: data.avatar.to_owned(), password_encrypted: read_user.password_encrypted, preferred_username: read_user.preferred_username, updated: Some(naive_now()), @@ -372,7 +375,12 @@ impl Perform for Oper { data.username.to_owned().unwrap_or("admin".to_string()), ) { Ok(user) => user.id, - Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_that_username_or_email"))? + Err(_e) => { + return Err(APIError::err( + &self.op, + "couldnt_find_that_username_or_email", + ))? + } } } }; @@ -449,6 +457,7 @@ impl Perform for Oper { name: read_user.name, fedi_name: read_user.fedi_name, email: read_user.email, + avatar: read_user.avatar, password_encrypted: read_user.password_encrypted, preferred_username: read_user.preferred_username, updated: Some(naive_now()), @@ -511,6 +520,7 @@ impl Perform for Oper { name: read_user.name, fedi_name: read_user.fedi_name, email: read_user.email, + avatar: read_user.avatar, password_encrypted: read_user.password_encrypted, preferred_username: read_user.preferred_username, updated: Some(naive_now()), @@ -848,6 +858,7 @@ impl Perform for Oper { name: read_user.name, fedi_name: read_user.fedi_name, email: read_user.email, + avatar: read_user.avatar, password_encrypted: data.password.to_owned(), preferred_username: read_user.preferred_username, updated: Some(naive_now()), diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs index 9b861f45..b1a01b6d 100644 --- a/server/src/apub/mod.rs +++ b/server/src/apub/mod.rs @@ -22,7 +22,7 @@ mod tests { preferred_username: None, password_encrypted: "here".into(), email: None, - icon: None, + avatar: None, published: naive_now(), admin: false, banned: false, diff --git a/server/src/db/comment.rs b/server/src/db/comment.rs index b7bd562d..64195356 100644 --- a/server/src/db/comment.rs +++ b/server/src/db/comment.rs @@ -174,6 +174,7 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + avatar: None, admin: false, banned: false, updated: None, diff --git a/server/src/db/comment_view.rs b/server/src/db/comment_view.rs index 5c321e2e..c56da51d 100644 --- a/server/src/db/comment_view.rs +++ b/server/src/db/comment_view.rs @@ -18,6 +18,7 @@ table! { banned -> Bool, banned_from_community -> Bool, creator_name -> Varchar, + creator_avatar -> Nullable, score -> BigInt, upvotes -> BigInt, downvotes -> BigInt, @@ -46,6 +47,7 @@ pub struct CommentView { pub banned: bool, pub banned_from_community: bool, pub creator_name: String, + pub creator_avatar: Option, pub score: i64, pub upvotes: i64, pub downvotes: i64, @@ -226,6 +228,7 @@ table! { banned -> Bool, banned_from_community -> Bool, creator_name -> Varchar, + creator_avatar -> Nullable, score -> BigInt, upvotes -> BigInt, downvotes -> BigInt, @@ -255,6 +258,7 @@ pub struct ReplyView { pub banned: bool, pub banned_from_community: bool, pub creator_name: String, + pub creator_avatar: Option, pub score: i64, pub upvotes: i64, pub downvotes: i64, @@ -368,6 +372,7 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + avatar: None, admin: false, banned: false, updated: None, @@ -447,6 +452,7 @@ mod tests { published: inserted_comment.published, updated: None, creator_name: inserted_user.name.to_owned(), + creator_avatar: None, score: 1, downvotes: 0, upvotes: 1, @@ -470,6 +476,7 @@ mod tests { published: inserted_comment.published, updated: None, creator_name: inserted_user.name.to_owned(), + creator_avatar: None, score: 1, downvotes: 0, upvotes: 1, diff --git a/server/src/db/community.rs b/server/src/db/community.rs index e8bf5bbc..b5d05384 100644 --- a/server/src/db/community.rs +++ b/server/src/db/community.rs @@ -220,6 +220,7 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + avatar: None, admin: false, banned: false, updated: None, diff --git a/server/src/db/community_view.rs b/server/src/db/community_view.rs index 157c4d91..e57fd759 100644 --- a/server/src/db/community_view.rs +++ b/server/src/db/community_view.rs @@ -16,6 +16,7 @@ table! { deleted -> Bool, nsfw -> Bool, creator_name -> Varchar, + creator_avatar -> Nullable, category_name -> Varchar, number_of_subscribers -> BigInt, number_of_posts -> BigInt, @@ -33,6 +34,7 @@ table! { user_id -> Int4, published -> Timestamp, user_name -> Varchar, + avatar -> Nullable, community_name -> Varchar, } } @@ -44,6 +46,7 @@ table! { user_id -> Int4, published -> Timestamp, user_name -> Varchar, + avatar -> Nullable, community_name -> Varchar, } } @@ -55,6 +58,7 @@ table! { user_id -> Int4, published -> Timestamp, user_name -> Varchar, + avatar -> Nullable, community_name -> Varchar, } } @@ -76,6 +80,7 @@ pub struct CommunityView { pub deleted: bool, pub nsfw: bool, pub creator_name: String, + pub creator_avatar: Option, pub category_name: String, pub number_of_subscribers: i64, pub number_of_posts: i64, @@ -224,6 +229,7 @@ pub struct CommunityModeratorView { pub user_id: i32, pub published: chrono::NaiveDateTime, pub user_name: String, + pub avatar: Option, pub community_name: String, } @@ -253,6 +259,7 @@ pub struct CommunityFollowerView { pub user_id: i32, pub published: chrono::NaiveDateTime, pub user_name: String, + pub avatar: Option, pub community_name: String, } @@ -282,6 +289,7 @@ pub struct CommunityUserBanView { pub user_id: i32, pub published: chrono::NaiveDateTime, pub user_name: String, + pub avatar: Option, pub community_name: String, } diff --git a/server/src/db/moderator.rs b/server/src/db/moderator.rs index 22547ca4..7f1c3499 100644 --- a/server/src/db/moderator.rs +++ b/server/src/db/moderator.rs @@ -442,6 +442,7 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + avatar: None, admin: false, banned: false, updated: None, @@ -460,6 +461,7 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + avatar: None, admin: false, banned: false, updated: None, diff --git a/server/src/db/password_reset_request.rs b/server/src/db/password_reset_request.rs index 91e27c57..b7983f53 100644 --- a/server/src/db/password_reset_request.rs +++ b/server/src/db/password_reset_request.rs @@ -92,6 +92,7 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + avatar: None, admin: false, banned: false, updated: None, diff --git a/server/src/db/post.rs b/server/src/db/post.rs index 96ae31db..da669ea1 100644 --- a/server/src/db/post.rs +++ b/server/src/db/post.rs @@ -187,6 +187,7 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + avatar: None, admin: false, banned: false, updated: None, diff --git a/server/src/db/post_view.rs b/server/src/db/post_view.rs index 615b9b0d..d05eecca 100644 --- a/server/src/db/post_view.rs +++ b/server/src/db/post_view.rs @@ -21,6 +21,7 @@ table! { banned_from_community -> Bool, stickied -> Bool, creator_name -> Varchar, + creator_avatar -> Nullable, community_name -> Varchar, community_removed -> Bool, community_deleted -> Bool, @@ -59,6 +60,7 @@ pub struct PostView { pub banned_from_community: bool, pub stickied: bool, pub creator_name: String, + pub creator_avatar: Option, pub community_name: String, pub community_removed: bool, pub community_deleted: bool, @@ -303,6 +305,7 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + avatar: None, updated: None, admin: false, banned: false, @@ -377,6 +380,7 @@ mod tests { body: None, creator_id: inserted_user.id, creator_name: user_name.to_owned(), + creator_avatar: None, banned: false, banned_from_community: false, community_id: inserted_community.id, @@ -414,6 +418,7 @@ mod tests { stickied: false, creator_id: inserted_user.id, creator_name: user_name.to_owned(), + creator_avatar: None, banned: false, banned_from_community: false, community_id: inserted_community.id, diff --git a/server/src/db/site_view.rs b/server/src/db/site_view.rs index 40b1265f..674a7a6e 100644 --- a/server/src/db/site_view.rs +++ b/server/src/db/site_view.rs @@ -12,6 +12,7 @@ table! { open_registration -> Bool, enable_nsfw -> Bool, creator_name -> Varchar, + creator_avatar -> Nullable, number_of_users -> BigInt, number_of_posts -> BigInt, number_of_comments -> BigInt, @@ -34,6 +35,7 @@ pub struct SiteView { pub open_registration: bool, pub enable_nsfw: bool, pub creator_name: String, + pub creator_avatar: Option, pub number_of_users: i64, pub number_of_posts: i64, pub number_of_comments: i64, diff --git a/server/src/db/user.rs b/server/src/db/user.rs index c636f4e6..db4aa453 100644 --- a/server/src/db/user.rs +++ b/server/src/db/user.rs @@ -14,7 +14,7 @@ pub struct User_ { pub preferred_username: Option, pub password_encrypted: String, pub email: Option, - pub icon: Option>, + pub avatar: Option, pub admin: bool, pub banned: bool, pub published: chrono::NaiveDateTime, @@ -36,6 +36,7 @@ pub struct UserForm { pub admin: bool, pub banned: bool, pub email: Option, + pub avatar: Option, pub updated: Option, pub show_nsfw: bool, pub theme: String, @@ -99,6 +100,7 @@ pub struct Claims { pub default_sort_type: i16, pub default_listing_type: i16, pub lang: String, + pub avatar: Option, } impl Claims { @@ -123,6 +125,7 @@ impl User_ { default_sort_type: self.default_sort_type, default_listing_type: self.default_listing_type, lang: self.lang.to_owned(), + avatar: self.avatar.to_owned(), }; encode( &Header::default(), @@ -176,6 +179,7 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + avatar: None, admin: false, banned: false, updated: None, @@ -195,7 +199,7 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, - icon: None, + avatar: None, admin: false, banned: false, published: inserted_user.published, diff --git a/server/src/db/user_mention.rs b/server/src/db/user_mention.rs index 7eb4d486..5392c87a 100644 --- a/server/src/db/user_mention.rs +++ b/server/src/db/user_mention.rs @@ -68,6 +68,7 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + avatar: None, admin: false, banned: false, updated: None, @@ -86,6 +87,7 @@ mod tests { preferred_username: None, password_encrypted: "nope".into(), email: None, + avatar: None, admin: false, banned: false, updated: None, diff --git a/server/src/db/user_mention_view.rs b/server/src/db/user_mention_view.rs index 45541861..7a45d222 100644 --- a/server/src/db/user_mention_view.rs +++ b/server/src/db/user_mention_view.rs @@ -20,6 +20,7 @@ table! { banned -> Bool, banned_from_community -> Bool, creator_name -> Varchar, + creator_avatar -> Nullable, score -> BigInt, upvotes -> BigInt, downvotes -> BigInt, @@ -50,6 +51,7 @@ pub struct UserMentionView { pub banned: bool, pub banned_from_community: bool, pub creator_name: String, + pub creator_avatar: Option, pub score: i64, pub upvotes: i64, pub downvotes: i64, @@ -78,7 +80,7 @@ impl<'a> UserMentionQueryBuilder<'a> { UserMentionQueryBuilder { conn, query, - for_user_id: for_user_id, + for_user_id, sort: &SortType::New, unread_only: false, page: None, diff --git a/server/src/db/user_view.rs b/server/src/db/user_view.rs index 0ed95eef..616159de 100644 --- a/server/src/db/user_view.rs +++ b/server/src/db/user_view.rs @@ -6,6 +6,7 @@ table! { user_view (id) { id -> Int4, name -> Varchar, + avatar -> Nullable, fedi_name -> Varchar, admin -> Bool, banned -> Bool, @@ -24,6 +25,7 @@ table! { pub struct UserView { pub id: i32, pub name: String, + pub avatar: Option, pub fedi_name: String, pub admin: bool, pub banned: bool, diff --git a/server/src/schema.rs b/server/src/schema.rs index 118f5f4a..86834072 100644 --- a/server/src/schema.rs +++ b/server/src/schema.rs @@ -260,7 +260,7 @@ table! { preferred_username -> Nullable, password_encrypted -> Text, email -> Nullable, - icon -> Nullable, + avatar -> Nullable, admin -> Bool, banned -> Bool, published -> Timestamp, diff --git a/ui/.eslintignore b/ui/.eslintignore new file mode 100644 index 00000000..aa6ed178 --- /dev/null +++ b/ui/.eslintignore @@ -0,0 +1,2 @@ +fuse.js +translation_report.ts diff --git a/ui/fuse.js b/ui/fuse.js index 85eb75e2..3feab490 100644 --- a/ui/fuse.js +++ b/ui/fuse.js @@ -1,11 +1,11 @@ -const { +import { FuseBox, Sparky, EnvPlugin, CSSPlugin, WebIndexPlugin, - QuantumPlugin -} = require('fuse-box'); + QuantumPlugin, +} from 'fuse-box'; // const transformInferno = require('../../dist').default const transformInferno = require('ts-transform-inferno').default; const transformClasscat = require('ts-transform-classcat').default; @@ -25,22 +25,22 @@ Sparky.task('config', _ => { before: [transformClasscat(), transformInferno()], }, alias: { - 'locale': 'moment/locale' - }, + locale: 'moment/locale', + }, plugins: [ EnvPlugin({ NODE_ENV: isProduction ? 'production' : 'development' }), CSSPlugin(), WebIndexPlugin({ title: 'Inferno Typescript FuseBox Example', template: 'src/index.html', - path: isProduction ? "/static" : "/" + path: isProduction ? '/static' : '/', }), isProduction && - QuantumPlugin({ - bakeApiIntoBundle: 'app', - treeshake: true, - uglify: true, - }), + QuantumPlugin({ + bakeApiIntoBundle: 'app', + treeshake: true, + uglify: true, + }), ], }); app = fuse.bundle('app').instructions('>index.tsx'); @@ -48,7 +48,9 @@ Sparky.task('config', _ => { // Sparky.task('version', _ => setVersion()); Sparky.task('clean', _ => Sparky.src('dist/').clean('dist/')); Sparky.task('env', _ => (isProduction = true)); -Sparky.task('copy-assets', () => Sparky.src('assets/**/**.*').dest(isProduction ? 'dist/' : 'dist/static')); +Sparky.task('copy-assets', () => + Sparky.src('assets/**/**.*').dest(isProduction ? 'dist/' : 'dist/static') +); Sparky.task('dev', ['clean', 'config', 'copy-assets'], _ => { fuse.dev(); app.hmr().watch(); diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index f408ef27..34ec0dfb 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -17,7 +17,13 @@ import { BanType, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; -import { mdToHtml, getUnixTime, canMod, isMod } from '../utils'; +import { + mdToHtml, + getUnixTime, + canMod, + isMod, + pictshareAvatarThumbnail, +} from '../utils'; import * as moment from 'moment'; import { MomentTime } from './moment-time'; import { CommentForm } from './comment-form'; @@ -128,7 +134,15 @@ export class CommentNode extends Component { className="text-info" to={`/u/${node.comment.creator_name}`} > - {node.comment.creator_name} + {node.comment.creator_avatar && ( + + )} + {node.comment.creator_name} {this.isMod && ( diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index 0d6be91d..d1042f38 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -31,6 +31,7 @@ import { routeSortTypeToEnum, routeListingTypeToEnum, postRefetchSeconds, + pictshareAvatarThumbnail, } from '../utils'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -65,6 +66,9 @@ export class Main extends Component { number_of_posts: null, number_of_comments: null, number_of_communities: null, + enable_downvotes: null, + open_registration: null, + enable_nsfw: null, }, admins: [], banned: [], @@ -341,7 +345,15 @@ export class Main extends Component { {this.state.site.admins.map(admin => (
  • - {admin.name} + {admin.avatar && ( + + )} + {admin.name}
  • ))} diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx index 306dc74f..f1a98941 100644 --- a/ui/src/components/navbar.tsx +++ b/ui/src/components/navbar.tsx @@ -13,7 +13,7 @@ import { GetSiteResponse, Comment, } from '../interfaces'; -import { msgOp } from '../utils'; +import { msgOp, pictshareAvatarThumbnail } from '../utils'; import { version } from '../version'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -151,7 +151,19 @@ export class Navbar extends Component { class="nav-link" to={`/u/${UserService.Instance.user.username}`} > - {UserService.Instance.user.username} + + {UserService.Instance.user.avatar && ( + + )} + {UserService.Instance.user.username} + diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 61a4c865..1f3a74ac 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -25,6 +25,7 @@ import { isImage, isVideo, getUnixTime, + pictshareAvatarThumbnail, } from '../utils'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -248,7 +249,15 @@ export class PostListing extends Component {
  • {i18n.t('by')} - {post.creator_name} + {post.creator_avatar && ( + + )} + {post.creator_name} {this.isMod && ( diff --git a/ui/src/components/search.tsx b/ui/src/components/search.tsx index bba7c5ad..d92c06e1 100644 --- a/ui/src/components/search.tsx +++ b/ui/src/components/search.tsx @@ -19,6 +19,7 @@ import { fetchLimit, routeSearchTypeToEnum, routeSortTypeToEnum, + pictshareAvatarThumbnail, } from '../utils'; import { PostListing } from './post-listing'; import { SortSelect } from './sort-select'; @@ -286,7 +287,19 @@ export class Search extends Component { {`/u/${(i.data as UserView).name}`} + > + {(i.data as UserView).avatar && ( + + )} + {`/u/${(i.data as UserView).name}`} + {` - ${ (i.data as UserView).comment_score diff --git a/ui/src/components/sidebar.tsx b/ui/src/components/sidebar.tsx index 85f81a30..0e95666f 100644 --- a/ui/src/components/sidebar.tsx +++ b/ui/src/components/sidebar.tsx @@ -8,7 +8,7 @@ import { UserView, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; -import { mdToHtml, getUnixTime } from '../utils'; +import { mdToHtml, getUnixTime, pictshareAvatarThumbnail } from '../utils'; import { CommunityForm } from './community-form'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -194,7 +194,15 @@ export class Sidebar extends Component { {this.props.moderators.map(mod => (
  • - {mod.user_name} + {mod.avatar && ( + + )} + {mod.user_name}
  • ))} diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index 6d6a2e0c..e97b26f9 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -58,6 +58,7 @@ interface UserState { sort: SortType; page: number; loading: boolean; + avatarLoading: boolean; userSettingsForm: UserSettingsForm; userSettingsLoading: boolean; deleteAccountLoading: boolean; @@ -78,6 +79,7 @@ export class User extends Component { number_of_comments: null, comment_score: null, banned: null, + avatar: null, }, user_id: null, username: null, @@ -87,6 +89,7 @@ export class User extends Component { posts: [], admins: [], loading: true, + avatarLoading: false, view: this.getViewFromProps(this.props), sort: this.getSortTypeFromProps(this.props), page: this.getPageFromProps(this.props), @@ -96,6 +99,7 @@ export class User extends Component { default_sort_type: null, default_listing_type: null, lang: null, + avatar: null, auth: null, }, userSettingsLoading: null, @@ -203,7 +207,17 @@ export class User extends Component { ) : (
    -
    /u/{this.state.user.name}
    +
    + {this.state.user.avatar && ( + + )} + /u/{this.state.user.name} +
    {this.selects()} {this.state.view == View.Overview && this.overview()} {this.state.view == View.Comments && this.comments()} @@ -422,6 +436,39 @@ export class User extends Component { #
    +
    +
    + + + + + +
    +