diff --git a/README.md b/README.md index 7b645c14..79ecc4db 100644 --- a/README.md +++ b/README.md @@ -159,25 +159,26 @@ Lemmy is free, open-source software, meaning no advertising, monetizing, or vent ## Translations -If you'd like to add translations, take a look a look at the [English translation file](ui/src/translations/en.ts). +If you'd like to add translations, take a look at the [English translation file](ui/src/translations/en.ts). -- Languages supported: Catalan, (`ca`), English (`en`), Chinese (`zh`), Dutch (`nl`), Esperanto (`eo`), Finnish (`fi`), French (`fr`), Spanish (`es`), Swedish (`sv`), German (`de`), Russian (`ru`), Italian (`it`). +- Languages supported: Catalan, (`ca`), Farsi (`fa`), English (`en`), Chinese (`zh`), Dutch (`nl`), Esperanto (`eo`), Finnish (`fi`), French (`fr`), Spanish (`es`), Swedish (`sv`), German (`de`), Russian (`ru`), Italian (`it`). lang | done | missing ---- | ---- | ------- -ca | 100% | -de | 88% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,docs,message_sent,messages,old_password,matrix_user_id,private_message_disclaimer,send_notifications_to_email,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message -eo | 76% | number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,donate_to_lemmy,donate,from,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message -es | 100% | -fi | 100% | -fr | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message -it | 84% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message -nl | 93% | create_private_message,send_secure_message,send_message,message,message_sent,messages,matrix_user_id,private_message_disclaimer,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message -ru | 72% | cross_posts,cross_post,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message -sv | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message -zh | 70% | cross_posts,cross_post,users,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message +ca | 100% | old +de | 87% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,old,docs,message_sent,messages,old_password,matrix_user_id,private_message_disclaimer,send_notifications_to_email,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message +fa | 73% | cross_post,subscribed_to_communities,trending_communities,create_private_message,send_secure_message,send_message,message,mod,mods,moderates,remove_as_mod,appoint_as_mod,modlog,stickied,ban,ban_from_site,unban,unban_from_site,banned,number_of_subscribers,subscribers,both,saved,unsubscribe,subscribe,subscribed,old,api,docs,inbox,inbox_for,message_sent,notifications_error,messages,no_email_setup,matrix_user_id,private_message_disclaimer,url,body,copy_suggested_title,community,expand_here,subscribe_to_communities,theme,sponsor_message,general_sponsors,joined,by,to,from,landing_0,logged_in,community_moderator_already_exists,community_follower_already_exists,community_user_already_banned,no_slurs,admin_already_created,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message +eo | 75% | number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,donate_to_lemmy,donate,from,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message +es | 100% | old +fi | 100% | old +fr | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message +it | 84% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,old,docs,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message +nl | 92% | create_private_message,send_secure_message,send_message,message,old,message_sent,messages,matrix_user_id,private_message_disclaimer,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message +ru | 72% | cross_posts,cross_post,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message +sv | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message +zh | 70% | cross_posts,cross_post,users,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message If you'd like to update this report, run: diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 3d4cd949..646627bd 100644 --- a/docker/prod/docker-compose.yml +++ b/docker/prod/docker-compose.yml @@ -11,7 +11,7 @@ services: - lemmy_db:/var/lib/postgresql/data restart: always lemmy: - image: dessalines/lemmy:v0.6.5 + image: dessalines/lemmy:v0.6.7 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/server/migrations/2020-01-29-011901_create_reply_materialized_view/down.sql b/server/migrations/2020-01-29-011901_create_reply_materialized_view/down.sql new file mode 100644 index 00000000..06ec5971 --- /dev/null +++ b/server/migrations/2020-01-29-011901_create_reply_materialized_view/down.sql @@ -0,0 +1,25 @@ +-- Drop the materialized / built views +drop view reply_view; +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 +; diff --git a/server/migrations/2020-01-29-011901_create_reply_materialized_view/up.sql b/server/migrations/2020-01-29-011901_create_reply_materialized_view/up.sql new file mode 100644 index 00000000..ebbb1dff --- /dev/null +++ b/server/migrations/2020-01-29-011901_create_reply_materialized_view/up.sql @@ -0,0 +1,27 @@ +-- https://github.com/dessalines/lemmy/issues/197 +drop view reply_view; + +-- Do the reply_view referencing the comment_mview +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_mview cv, closereply +where closereply.id = cv.id +; diff --git a/server/migrations/2020-01-29-030825_create_user_mention_materialized_view/down.sql b/server/migrations/2020-01-29-030825_create_user_mention_materialized_view/down.sql new file mode 100644 index 00000000..d93ebc2e --- /dev/null +++ b/server/migrations/2020-01-29-030825_create_user_mention_materialized_view/down.sql @@ -0,0 +1 @@ +drop view user_mention_mview; diff --git a/server/migrations/2020-01-29-030825_create_user_mention_materialized_view/up.sql b/server/migrations/2020-01-29-030825_create_user_mention_materialized_view/up.sql new file mode 100644 index 00000000..b0ae4e9d --- /dev/null +++ b/server/migrations/2020-01-29-030825_create_user_mention_materialized_view/up.sql @@ -0,0 +1,67 @@ +create view user_mention_mview as +with all_comment as +( + select + ca.* + from comment_aggregates_mview ca +) + +select + ac.id, + um.id as user_mention_id, + ac.creator_id, + ac.post_id, + ac.parent_id, + ac.content, + ac.removed, + um.read, + ac.published, + ac.updated, + ac.deleted, + ac.community_id, + ac.banned, + ac.banned_from_community, + ac.creator_name, + ac.creator_avatar, + ac.score, + ac.upvotes, + ac.downvotes, + 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, + um.recipient_id +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 +left join user_mention um on um.comment_id = ac.id + +union all + +select + ac.id, + um.id as user_mention_id, + ac.creator_id, + ac.post_id, + ac.parent_id, + ac.content, + ac.removed, + um.read, + ac.published, + ac.updated, + ac.deleted, + ac.community_id, + ac.banned, + ac.banned_from_community, + ac.creator_name, + ac.creator_avatar, + ac.score, + ac.upvotes, + ac.downvotes, + null as user_id, + null as my_vote, + null as saved, + um.recipient_id +from all_comment ac +left join user_mention um on um.comment_id = ac.id +; + diff --git a/server/query_testing/apache_bench_report.sh b/server/query_testing/apache_bench_report.sh index c22af730..62b3e863 100755 --- a/server/query_testing/apache_bench_report.sh +++ b/server/query_testing/apache_bench_report.sh @@ -1,4 +1,5 @@ -#!/bin/sh +#!/bin/bash +set -e declare -a arr=( "https://mastodon.social/" diff --git a/server/query_testing/api_benchmark.sh b/server/query_testing/api_benchmark.sh new file mode 100755 index 00000000..8f8c65f1 --- /dev/null +++ b/server/query_testing/api_benchmark.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -e + +# By default, this script runs against `http://127.0.0.1:8536`, but you can pass a different Lemmy instance, +# eg `./api_benchmark.sh "https://example.com"`. +DOMAIN=${1:-"http://127.0.0.1:8536"} + +declare -a arr=( +"/api/v1/site" +"/api/v1/categories" +"/api/v1/modlog" +"/api/v1/search?q=test&type_=Posts&sort=Hot" +"/api/v1/community" +"/api/v1/community/list?sort=Hot" +"/api/v1/post/list?sort=Hot&type_=All" +) + +## now loop through the above array +for path in "${arr[@]}" +do + URL="$DOMAIN$path" + printf "\n\n\n" + echo "testing $URL" + curl --show-error --fail --silent "$URL" >/dev/null + ab -c 64 -t 10 "$URL" > out.abtest + grep "Server Hostname:" out.abtest + grep "Document Path:" out.abtest + grep "Requests per second" out.abtest + grep "(mean, across all concurrent requests)" out.abtest + grep "Transfer rate:" out.abtest + echo "---" +done + +rm *.abtest diff --git a/server/query_testing/generate_explain_reports.sh b/server/query_testing/generate_explain_reports.sh index 9ba91036..6ce7dc42 100755 --- a/server/query_testing/generate_explain_reports.sh +++ b/server/query_testing/generate_explain_reports.sh @@ -1,4 +1,5 @@ -#!/bin/sh +#!/bin/bash +set -e # Do the views first @@ -17,6 +18,15 @@ psql -qAt -U lemmy -f explain.sql > community_view.json echo "explain (analyze, format json) select * from site_view limit 1" > explain.sql psql -qAt -U lemmy -f explain.sql > site_view.json +echo "explain (analyze, format json) select * from reply_view where user_id = 34 and recipient_id = 34" > explain.sql +psql -qAt -U lemmy -f explain.sql > reply_view.json + +echo "explain (analyze, format json) select * from user_mention_view where user_id = 34 and recipient_id = 34" > explain.sql +psql -qAt -U lemmy -f explain.sql > user_mention_view.json + +echo "explain (analyze, format json) select * from user_mention_mview where user_id = 34 and recipient_id = 34" > explain.sql +psql -qAt -U lemmy -f explain.sql > user_mention_mview.json + grep "Execution Time" *.json rm explain.sql diff --git a/server/src/api/site.rs b/server/src/api/site.rs index a5faf34d..dfbd5ff0 100644 --- a/server/src/api/site.rs +++ b/server/src/api/site.rs @@ -3,7 +3,7 @@ use diesel::PgConnection; use std::str::FromStr; #[derive(Serialize, Deserialize)] -pub struct ListCategories; +pub struct ListCategories {} #[derive(Serialize, Deserialize)] pub struct ListCategoriesResponse { @@ -72,7 +72,7 @@ pub struct EditSite { } #[derive(Serialize, Deserialize)] -pub struct GetSite; +pub struct GetSite {} #[derive(Serialize, Deserialize)] pub struct SiteResponse { diff --git a/server/src/db/user_mention_view.rs b/server/src/db/user_mention_view.rs index 7a45d222..1cf43984 100644 --- a/server/src/db/user_mention_view.rs +++ b/server/src/db/user_mention_view.rs @@ -1,4 +1,3 @@ -use super::user_mention_view::user_mention_view::BoxedQuery; use super::*; use diesel::pg::Pg; @@ -31,6 +30,34 @@ table! { } } +table! { + user_mention_mview (id) { + id -> Int4, + user_mention_id -> Int4, + creator_id -> Int4, + post_id -> Int4, + parent_id -> Nullable, + content -> Text, + removed -> Bool, + read -> Bool, + published -> Timestamp, + updated -> Nullable, + deleted -> Bool, + community_id -> Int4, + banned -> Bool, + banned_from_community -> Bool, + creator_name -> Varchar, + creator_avatar -> Nullable, + score -> BigInt, + upvotes -> BigInt, + downvotes -> BigInt, + user_id -> Nullable, + my_vote -> Nullable, + saved -> Nullable, + recipient_id -> Int4, + } +} + #[derive( Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone, )] @@ -63,7 +90,7 @@ pub struct UserMentionView { pub struct UserMentionQueryBuilder<'a> { conn: &'a PgConnection, - query: BoxedQuery<'a, Pg>, + query: super::user_mention_view::user_mention_mview::BoxedQuery<'a, Pg>, for_user_id: i32, sort: &'a SortType, unread_only: bool, @@ -73,9 +100,9 @@ pub struct UserMentionQueryBuilder<'a> { impl<'a> UserMentionQueryBuilder<'a> { pub fn create(conn: &'a PgConnection, for_user_id: i32) -> Self { - use super::user_mention_view::user_mention_view::dsl::*; + use super::user_mention_view::user_mention_mview::dsl::*; - let query = user_mention_view.into_boxed(); + let query = user_mention_mview.into_boxed(); UserMentionQueryBuilder { conn, @@ -109,7 +136,7 @@ impl<'a> UserMentionQueryBuilder<'a> { } pub fn list(self) -> Result, Error> { - use super::user_mention_view::user_mention_view::dsl::*; + use super::user_mention_view::user_mention_mview::dsl::*; let mut query = self.query; diff --git a/server/src/version.rs b/server/src/version.rs index f2428229..c6b30c76 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.6.5"; +pub const VERSION: &str = "v0.6.7"; diff --git a/ui/package.json b/ui/package.json index d3085650..7e75052b 100644 --- a/ui/package.json +++ b/ui/package.json @@ -49,6 +49,7 @@ "fuse-box": "^3.1.3", "lint-staged": "^10.0.2", "sortpack": "^2.0.1", + "ts-node": "^8.6.2", "ts-transform-classcat": "^0.0.2", "ts-transform-inferno": "^4.0.2", "typescript": "^3.7.5" @@ -59,7 +60,7 @@ "engineStrict": true, "husky": { "hooks": { - "pre-commit": "ts-node translation_report.ts && git add ../README.md && cargo clippy --manifest-path ../server/Cargo.toml --all-targets --all-features -- -D warnings && lint-staged" + "pre-commit": "yarn run ts-node translation_report.ts && git add ../README.md && cargo clippy --manifest-path ../server/Cargo.toml --all-targets --all-features -- -D warnings && lint-staged" } }, "lint-staged": { diff --git a/ui/src/components/comment-form.tsx b/ui/src/components/comment-form.tsx index e4543d66..7eb30f50 100644 --- a/ui/src/components/comment-form.tsx +++ b/ui/src/components/comment-form.tsx @@ -96,6 +96,7 @@ export class CommentForm extends Component { className={`form-control ${this.state.previewMode && 'd-none'}`} value={this.state.commentForm.content} onInput={linkEvent(this, this.handleCommentContentChange)} + onPaste={linkEvent(this, this.handleImageUploadPaste)} required disabled={this.props.disabled} rows={2} @@ -208,9 +209,22 @@ export class CommentForm extends Component { i.props.onReplyCancel(); } + handleImageUploadPaste(i: CommentForm, event: any) { + let image = event.clipboardData.files[0]; + if (image) { + i.handleImageUpload(i, image); + } + } + handleImageUpload(i: CommentForm, event: any) { - event.preventDefault(); - let file = event.target.files[0]; + let file: any; + if (event.target) { + event.preventDefault(); + file = event.target.files[0]; + } else { + file = event; + } + const imageUploadUrl = `/pictshare/api/upload.php`; const formData = new FormData(); formData.append('file', file); diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index 221c9211..3c5f6890 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -11,6 +11,7 @@ import { SortType, Post, GetPostsForm, + GetCommunityForm, ListingType, GetPostsResponse, CreatePostLikeResponse, @@ -98,11 +99,11 @@ export class Community extends Component { () => console.log('complete') ); - if (this.state.communityId) { - WebSocketService.Instance.getCommunity(this.state.communityId); - } else if (this.state.communityName) { - WebSocketService.Instance.getCommunityByName(this.state.communityName); - } + let form: GetCommunityForm = { + id: this.state.communityId ? this.state.communityId : null, + name: this.state.communityName ? this.state.communityName : null, + }; + WebSocketService.Instance.getCommunity(form); } componentWillUnmount() { diff --git a/ui/src/components/inbox.tsx b/ui/src/components/inbox.tsx index ba5cc6ad..41c1ce60 100644 --- a/ui/src/components/inbox.tsx +++ b/ui/src/components/inbox.tsx @@ -38,6 +38,8 @@ enum UnreadType { Messages, } +type ReplyType = Comment | PrivateMessageI; + interface InboxState { unreadOrAll: UnreadOrAll; unreadType: UnreadType; @@ -186,7 +188,7 @@ export class Inbox extends Component { } all() { - let combined: Array = []; + let combined: Array = []; combined.push(...this.state.replies); combined.push(...this.state.mentions); diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx index 18ba98c9..1828fce9 100644 --- a/ui/src/components/navbar.tsx +++ b/ui/src/components/navbar.tsx @@ -138,7 +138,7 @@ export class Navbar extends Component {
  • diff --git a/ui/src/components/post-form.tsx b/ui/src/components/post-form.tsx index ebc7d7b7..57d9a964 100644 --- a/ui/src/components/post-form.tsx +++ b/ui/src/components/post-form.tsx @@ -160,6 +160,7 @@ export class PostForm extends Component { class="form-control" value={this.state.postForm.url} onInput={linkEvent(this, this.handlePostUrlChange)} + onPaste={linkEvent(this, this.handleImageUploadPaste)} /> {this.state.suggestedTitle && (
    { i.setState(i.state); } + handleImageUploadPaste(i: PostForm, event: any) { + let image = event.clipboardData.files[0]; + if (image) { + i.handleImageUpload(i, image); + } + } + handleImageUpload(i: PostForm, event: any) { - event.preventDefault(); - let file = event.target.files[0]; + let file: any; + if (event.target) { + event.preventDefault(); + file = event.target.files[0]; + } else { + file = event; + } + const imageUploadUrl = `/pictshare/api/upload.php`; const formData = new FormData(); formData.append('file', file); diff --git a/ui/src/components/post.tsx b/ui/src/components/post.tsx index f57d8913..36621248 100644 --- a/ui/src/components/post.tsx +++ b/ui/src/components/post.tsx @@ -23,6 +23,7 @@ import { SearchType, SortType, SearchForm, + GetPostForm, SearchResponse, GetSiteResponse, GetCommunityResponse, @@ -84,7 +85,10 @@ export class Post extends Component { () => console.log('complete') ); - WebSocketService.Instance.getPost(postId); + let form: GetPostForm = { + id: postId, + }; + WebSocketService.Instance.getPost(form); } componentWillUnmount() { @@ -231,6 +235,18 @@ export class Post extends Component { onChange={linkEvent(this, this.handleCommentSortChange)} /> +
    ); } @@ -313,6 +329,13 @@ export class Post extends Component { +a.comment.deleted - +b.comment.deleted || b.comment.published.localeCompare(a.comment.published) ); + } else if (this.state.commentSort == CommentSortType.Old) { + tree.sort( + (a, b) => + +a.comment.removed - +b.comment.removed || + +a.comment.deleted - +b.comment.deleted || + a.comment.published.localeCompare(b.comment.published) + ); } else if (this.state.commentSort == CommentSortType.Hot) { tree.sort( (a, b) => diff --git a/ui/src/i18next.ts b/ui/src/i18next.ts index 51e7e3a3..0d3ab177 100644 --- a/ui/src/i18next.ts +++ b/ui/src/i18next.ts @@ -12,6 +12,7 @@ import { nl } from './translations/nl'; import { it } from './translations/it'; import { fi } from './translations/fi'; import { ca } from './translations/ca'; +import { fa } from './translations/fa'; // https://github.com/nimbusec-oss/inferno-i18next/blob/master/tests/T.test.js#L66 const resources = { @@ -27,6 +28,7 @@ const resources = { it, fi, ca, + fa, }; function format(value: any, format: any, lng: any): any { diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts index cd3961b5..f83595d7 100644 --- a/ui/src/interfaces.ts +++ b/ui/src/interfaces.ts @@ -47,6 +47,7 @@ export enum CommentSortType { Hot, Top, New, + Old, } export enum ListingType { @@ -248,6 +249,10 @@ export interface FollowCommunityForm { auth?: string; } +export interface GetFollowedCommunitiesForm { + auth: string; +} + export interface GetFollowedCommunitiesResponse { communities: Array; } @@ -523,6 +528,12 @@ export interface CommunityForm { auth?: string; } +export interface GetCommunityForm { + id?: number; + name?: string; + auth?: string; +} + export interface GetCommunityResponse { community: Community; moderators: Array; @@ -572,6 +583,11 @@ export interface PostFormParams { community?: string; } +export interface GetPostForm { + id: number; + auth?: string; +} + export interface GetPostResponse { post: Post; comments: Array; @@ -759,6 +775,45 @@ export interface PrivateMessageResponse { message: PrivateMessage; } +export type MessageType = + | EditPrivateMessageForm + | LoginForm + | RegisterForm + | CommunityForm + | FollowCommunityForm + | ListCommunitiesForm + | GetFollowedCommunitiesForm + | PostForm + | GetPostForm + | GetPostsForm + | GetCommunityForm + | CommentForm + | CommentLikeForm + | SaveCommentForm + | CreatePostLikeForm + | BanFromCommunityForm + | AddAdminForm + | AddModToCommunityForm + | TransferCommunityForm + | TransferSiteForm + | SaveCommentForm + | BanUserForm + | AddAdminForm + | GetUserDetailsForm + | GetRepliesForm + | GetUserMentionsForm + | EditUserMentionForm + | GetModlogForm + | SiteForm + | SearchForm + | UserSettingsForm + | DeleteAccountForm + | PasswordResetForm + | PasswordChangeForm + | PrivateMessageForm + | EditPrivateMessageForm + | GetPrivateMessagesForm; + type ResponseType = | SiteResponse | GetFollowedCommunitiesResponse diff --git a/ui/src/services/WebSocketService.ts b/ui/src/services/WebSocketService.ts index e72a2871..a7e9b7e0 100644 --- a/ui/src/services/WebSocketService.ts +++ b/ui/src/services/WebSocketService.ts @@ -9,9 +9,12 @@ import { CommentForm, SaveCommentForm, CommentLikeForm, + GetPostForm, GetPostsForm, CreatePostLikeForm, + GetCommunityForm, FollowCommunityForm, + GetFollowedCommunitiesForm, GetUserDetailsForm, ListCommunitiesForm, GetModlogForm, @@ -35,6 +38,7 @@ import { PrivateMessageForm, EditPrivateMessageForm, GetPrivateMessagesForm, + MessageType, } from '../interfaces'; import { webSocket } from 'rxjs/webSocket'; import { Subject } from 'rxjs'; @@ -108,15 +112,15 @@ export class WebSocketService { } public getFollowedCommunities() { - let data = { auth: UserService.Instance.auth }; + let form: GetFollowedCommunitiesForm = { auth: UserService.Instance.auth }; this.subject.next( - this.wsSendWrapper(UserOperation.GetFollowedCommunities, data) + this.wsSendWrapper(UserOperation.GetFollowedCommunities, form) ); } public listCategories() { this.subject.next( - this.wsSendWrapper(UserOperation.ListCategories, undefined) + this.wsSendWrapper(UserOperation.ListCategories, {}) ); } @@ -125,19 +129,14 @@ export class WebSocketService { this.subject.next(this.wsSendWrapper(UserOperation.CreatePost, postForm)); } - public getPost(postId: number) { - let data = { id: postId, auth: UserService.Instance.auth }; - this.subject.next(this.wsSendWrapper(UserOperation.GetPost, data)); + public getPost(form: GetPostForm) { + this.setAuth(form, false); + this.subject.next(this.wsSendWrapper(UserOperation.GetPost, form)); } - public getCommunity(communityId: number) { - let data = { id: communityId, auth: UserService.Instance.auth }; - this.subject.next(this.wsSendWrapper(UserOperation.GetCommunity, data)); - } - - public getCommunityByName(name: string) { - let data = { name: name, auth: UserService.Instance.auth }; - this.subject.next(this.wsSendWrapper(UserOperation.GetCommunity, data)); + public getCommunity(form: GetCommunityForm) { + this.setAuth(form, false); + this.subject.next(this.wsSendWrapper(UserOperation.GetCommunity, form)); } public createComment(commentForm: CommentForm) { @@ -255,7 +254,7 @@ export class WebSocketService { } public getSite() { - this.subject.next(this.wsSendWrapper(UserOperation.GetSite, undefined)); + this.subject.next(this.wsSendWrapper(UserOperation.GetSite, {})); } public search(form: SearchForm) { @@ -310,7 +309,7 @@ export class WebSocketService { ); } - private wsSendWrapper(op: UserOperation, data: any) { + private wsSendWrapper(op: UserOperation, data: MessageType) { let send = { op: UserOperation[op], data: data }; console.log(send); return send; diff --git a/ui/src/translations/de.ts b/ui/src/translations/de.ts index 1ecaadaf..fafec579 100644 --- a/ui/src/translations/de.ts +++ b/ui/src/translations/de.ts @@ -15,9 +15,9 @@ export const de = { remove_comment: 'Kommentar löschen', communities: 'Communities', users: 'Benutzer', - create_a_community: 'Eine community anlegen', - create_community: 'Community anlegen', - remove_community: 'Community entfernen', + create_a_community: 'Eine Gemeinschaft anlegen', + create_community: 'Gemeinschaft anlegen', + remove_community: 'Gemeinschaft entfernen', subscribed_to_communities: 'Abonnierte <1>communities', trending_communities: 'Trending <1>communities', list_of_communities: 'Liste von communities', @@ -36,17 +36,17 @@ export const de = { unsticky: 'nicht haftend', link: 'link', archive_link: 'Archiv-Link', - mod: 'mod', - mods: 'mods', + mod: 'Moderator', + mods: 'Moderatoren', moderates: 'Moderiert', settings: 'Einstellungen', - remove_as_mod: 'Als mod entfernen', - appoint_as_mod: 'Zum mod ernennen', + remove_as_mod: 'Als Moderator entfernen', + appoint_as_mod: 'Zum Moderator ernennen', modlog: 'Modlog', - admin: 'admin', - admins: 'admins', - remove_as_admin: 'Als admin entfernen', - appoint_as_admin: 'Zum admin ernennen', + admin: 'Administrator', + admins: 'Administratoren', + remove_as_admin: 'Als Administrator entfernen', + appoint_as_admin: 'Zum Administrator ernennen', remove: 'entfernen', removed: 'entfernt', locked: 'gesperrt', @@ -66,11 +66,11 @@ export const de = { unban_from_site: 'Von der Seite entbannen', banned: 'gesperrt', save: 'speichern', - unsave: 'unsave', + unsave: 'nicht speichern', create: 'anlegen', creator: 'Ersteller', - username: 'Username', - email_or_username: 'Email oder Username', + username: 'Benutzername', + email_or_username: 'E-mail oder Username', number_of_users: '{{count}} Benutzer', number_of_subscribers: '{{count}} Abonnenten', number_of_points: '{{count}} Punkte', @@ -86,7 +86,7 @@ export const de = { subscribed: 'Abonniert', prev: 'Zurück', next: 'Weiter', - sidebar: 'Sidebar', + sidebar: 'Seitenleiste', sort_type: 'Sortieren nach', hot: 'Hot', new: 'Neu', @@ -122,22 +122,22 @@ export const de = { no_email_setup: "Dieser Server hat E-Mails nicht korrekt eingerichtet.", login: 'Einloggen', sign_up: 'Registrieren', - email: 'Email', - optional: 'Optional', + email: 'E-Mail', + optional: 'optional', expires: 'Ablaufdatum', language: 'Sprache', browser_default: 'Standard-Browser', url: 'URL', body: 'Text', copy_suggested_title: 'Vorgeschlagenen Titel übernehmen: {{title}}', - community: 'Community', - expand_here: 'Expand here', + community: 'Gemeinschaft', + expand_here: 'hier erweitern', subscribe_to_communities: 'Abonniere ein paar <1>communities.', chat: 'Chat', recent_comments: 'Neueste Kommentare', no_results: 'Keine Ergebnisse.', - setup: 'Setup', - lemmy_instance_setup: 'Lemmy Instanz Setup', + setup: 'Einrichten', + lemmy_instance_setup: 'Lemmy Instanz Einrichten', setup_admin: 'Seiten Administrator konfigurieren', your_site: 'deine Seite', modified: 'verändert', @@ -151,7 +151,7 @@ export const de = { support_on_patreon: 'Auf Patreon unterstützen', general_sponsors: 'Allgemeine Sponsoren sind die, die zwischen $10 und $39 zu Lemmy beitragen.', - crypto: 'Crypto', + crypto: 'Kryptowährung', bitcoin: 'Bitcoin', ethereum: 'Ethereum', monero: 'Monero', @@ -159,16 +159,16 @@ export const de = { joined: 'beigetreten', by: 'von', to: 'bis', - transfer_community: 'Transfer-Community', + transfer_community: 'Gemeinschaft übertragen', transfer_site: 'Transferseite', are_you_sure: 'Bist du sicher?', yes: 'Ja', no: 'Nein', powered_by: 'Bereitgestellt durch', landing_0: - 'Lemmy ist ein <1>Link Aggregator / Reddit Alternative im <2>Fediverse.<3>Es ist selbst-hostbar, hat live-updates von Kommentar-threads und ist winzig (<4>~80kB). Federation in das ActivityPub Netzwerk ist geplant. <5>Dies ist eine <6>sehr frühe Beta Version, und viele Features funktionieren zurzeit nicht richtig oder fehlen. <7>Schlage neue Features vor oder melde Bugs <8>hier.<9>Gebaut mit <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.', + 'Lemmy ist ein <1>Link-Aggregator / Reddit Alternative im <2>Fediverse.<3>Es ist selbst-hostbar, hat live-updates von Kommentar-threads und ist winzig (<4>~80kB). Federation in das ActivityPub Netzwerk ist geplant. <5>Dies ist eine <6>sehr frühe Beta Version, und viele Features funktionieren zurzeit nicht richtig oder fehlen. <7>Schlage neue Features vor oder melde Bugs <8>hier.<9>Gebaut mit <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.', not_logged_in: 'Nicht eingeloggt.', - community_ban: 'Du wurdest von dieser Community gebannt.', + community_ban: 'Du wurdest von dieser Gemeinschaft gebannt.', site_ban: 'Du wurdest von dieser Seite gebannt', couldnt_create_comment: 'Konnte Kommentar nicht anlegen.', couldnt_like_comment: 'Konnte nicht liken.', @@ -176,14 +176,14 @@ export const de = { couldnt_save_comment: 'Konnte Kommentar nicht speichern.', no_comment_edit_allowed: 'Keine Erlaubnis Kommentar zu editieren.', no_post_edit_allowed: 'Keine Erlaubnis Beitrag zu editieren.', - no_community_edit_allowed: 'Keine Erlaubnis Community zu editieren.', - couldnt_find_community: 'Konnte Community nicht finden.', - couldnt_update_community: 'Konnte Community nicht aktualisieren.', - community_already_exists: 'Community existiert bereits.', + no_community_edit_allowed: 'Keine Erlaubnis Gemeinschaft zu editieren.', + couldnt_find_community: 'Konnte Gemeinschaft nicht finden.', + couldnt_update_community: 'Konnte Gemeinschaft nicht aktualisieren.', + community_already_exists: 'Gemeinschaft existiert bereits.', community_moderator_already_exists: - 'Community Moderator existiert bereits.', - community_follower_already_exists: 'Community Follower existiert bereits.', - community_user_already_banned: 'Community Nutzer schon gebannt.', + 'Gemeinschaft Moderator existiert bereits.', + community_follower_already_exists: 'Gemeinschaft Follower existiert bereits.', + community_user_already_banned: 'Gemeinschaft Nutzer schon gebannt.', couldnt_create_post: 'Konnte Beitrag nicht anlegen.', couldnt_like_post: 'Konnte Beitrag nicht liken.', couldnt_find_post: 'Konnte Beitrag nicht finden.', diff --git a/ui/src/translations/en.ts b/ui/src/translations/en.ts index c932014f..90aff0b9 100644 --- a/ui/src/translations/en.ts +++ b/ui/src/translations/en.ts @@ -97,6 +97,7 @@ export const en = { sort_type: 'Sort type', hot: 'Hot', new: 'New', + old: 'Old', top_day: 'Top day', week: 'Week', month: 'Month', diff --git a/ui/src/translations/fa.ts b/ui/src/translations/fa.ts new file mode 100644 index 00000000..c06045c6 --- /dev/null +++ b/ui/src/translations/fa.ts @@ -0,0 +1,169 @@ +export const fa = { + translation: { + post: 'مطلب', + remove_post: 'حذف مطلب', + no_posts: 'بدون مطلب.', + create_a_post: 'ایجاد یک مطلب', + create_post: 'ایجاد مطلب', + number_of_posts: '{{count}} مطلب', + posts: 'مطالب', + related_posts: 'این مطالب ممکن است مرتبط باشند', + cross_posts: 'این پیوند در اینجا هم منتشر شده:', + comments: 'نظرات', + number_of_comments: '{{count}} نظر', + remove_comment: 'حذف نظر', + communities: 'جوامع', + users: 'کاربران', + create_a_community: 'ایجاد یک جامعه جدید', + create_community: 'ایجاد جامعه', + remove_community: 'حذف جامعه', + list_of_communities: 'فهرست جوامع', + number_of_communities: '{{count}} جامعه', + community_reqs: 'حروف کوچک, زیرخط, و بدون فاصله.', + edit: 'ویرایش', + reply: 'پاسخ', + cancel: 'لغو', + preview: 'پیش‌نمایش', + upload_image: 'بارگذاری تصویر', + avatar: 'آواتار', + upload_avatar: 'بارگذاری آواتار', + show_avatars: 'نمایش آواتارها', + formatting_help: 'راهنمای قالب‌بندی', + view_source: 'نمایش منبع', + unlock: 'بازکردن قفل', + lock: 'قفل کردن', + sticky: 'چسبان', + unsticky: 'غیرچسبان', + link: 'پیوند', + archive_link: 'بایگاهی پیوند', + settings: 'تنظیمات', + admin: 'مدیر', + admins: 'مدیران', + remove_as_admin: 'حذف به عنوان مدیر', + appoint_as_admin: 'انتصاب به عنوان مدیر', + remove: 'حذف', + removed: 'حذف شد', + locked: 'قفل شد', + reason: 'دلیل', + mark_as_read: 'علامت‌گذاری به عنوان خوانده شده', + mark_as_unread: 'علامت‌گذاری به عنوان خوانده نشده', + delete: 'پاک کردن', + deleted: 'پاک شد', + delete_account: 'پاک کردن حساب', + delete_account_confirm: + 'هشدار: این کنش، تمام اطلاعات شما را برای همیشه پاک می‌کند. برای تایید، گذرواژه خود را وارد کنید.', + restore: 'بازگردانی', + save: 'ذخیره', + unsave: 'عدم ذخیره', + create: 'ایجاد', + creator: 'سازنده', + username: 'نام‌کاربری', + email_or_username: 'رایانامه یا نام‌کاربری', + number_of_users: '{{count}} کاربر', + number_of_points: '{{count}} امتیاز', + number_online: '{{count}} کاربر برخط', + name: 'نام', + title: 'عنوان', + category: 'دسته‌بندی', + prev: 'پیش', + next: 'بعد', + sidebar: 'نوار کناری', + sort_type: 'نوع ترتیب', + hot: 'داغ', + new: 'تازه', + top_day: 'بهترین‌های روز', + week: 'هفته', + month: 'ماه', + year: 'سال', + all: 'همه', + top: 'بالاترین', + mark_all_as_read: 'علامت زدن همه به عنوان خوانده شده', + type: 'نوع', + unread: 'خوانده‌نشده', + replies: 'پاسخ‌ها', + mentions: 'اشاره‌ها', + reply_sent: 'پاسخ فرستاده شد', + search: 'جستجو', + overview: 'دید کلی', + view: 'نما', + logout: 'خروج', + login_sign_up: 'ورود / نام‌نویسی', + login: 'ورود', + sign_up: 'نام‌نویسی', + unread_messages: 'پیام‌های خوانده نشده', + password: 'گذرواژه', + verify_password: 'تایید گذرواژه', + old_password: 'پسورد پیشین', + forgot_password: 'گذرواژه را فراموش کرده‌ام', + reset_password_mail_sent: 'رایانامه‌ای برای بازنشانی گذرواژه فرستاده شد.', + password_change: 'تغییر گذرواژه', + new_password: 'گذرواژه جدید', + email: 'رایانامه', + send_notifications_to_email: 'فرستادن اعلانات به رایانامه', + optional: 'انتخابی', + expires: 'منقضی شود', + language: 'زبان', + browser_default: 'پیش‌فرض مرورگر', + downvotes_disabled: 'رای پایین غیرفعال است', + enable_downvotes: 'فعال‌سازی رای پایین', + open_registration: 'باز کردن نام‌نویسی', + registration_closed: 'نام‌نویسی بسته است', + enable_nsfw: 'فعال‌سازی NSFW', + chat: 'گپ', + recent_comments: 'نظرات اخیر', + no_results: 'بدون نتیجه.', + setup: 'نصب', + lemmy_instance_setup: 'نصب نمونهٔ لمی', + setup_admin: 'نصب مدیریت پایگاه', + your_site: 'پایگاه شما', + modified: 'تغییر یافت', + nsfw: 'NSFW', + show_nsfw: 'نمایش محتوای NSFW', + sponsors: 'حامیان', + sponsors_of_lemmy: 'حامیان لمی', + support_on_patreon: 'حمایت روی Patreon', + donate_to_lemmy: 'اعطای اعانه به لمی', + donate: 'اعانه', + crypto: 'رمزارز', + bitcoin: 'بیت‌کوین', + ethereum: 'اتریوم', + monero: 'مونرو', + code: 'کد', + transfer_community: 'انتقال جامعه', + transfer_site: 'انتقال پایگاه', + are_you_sure: 'مطمئنید؟', + yes: 'بله', + no: 'خیر', + powered_by: 'نیرو گرفته از', + not_logged_in: 'وارد نشده‌اید.', + community_ban: 'فعالیت شما در این جامعه ممنوع شده است.', + site_ban: 'فعالیت شما در این پایگاه ممنوع شده است', + couldnt_create_comment: 'ناتوانی در ایجاد نظر.', + couldnt_like_comment: 'ناتوانی در پسنیدن نظر.', + couldnt_update_comment: 'ناتوانی در به‌روزرسانی نظر.', + couldnt_save_comment: 'ناتوانی در ذخیره نظر.', + no_comment_edit_allowed: 'مجاز به ویرایش نظر نیستید.', + no_post_edit_allowed: 'مجاز به ویرایش مطلب نیستید.', + no_community_edit_allowed: 'مجاز به ویرایش جامعه نیستید.', + couldnt_find_community: 'ناتوانی در یافتن جامعه.', + couldnt_update_community: 'ناتوانی در به‌روزرسانی جامعه.', + community_already_exists: 'این جامعه از قبل وجود داشته است.', + couldnt_create_post: 'ناتوانی در ایجاد مطلب.', + couldnt_like_post: 'ناتوانی در پسندیدن مطلب.', + couldnt_find_post: 'ناتوانی در یافتن مطلب.', + couldnt_get_posts: 'ناتوانی در دریافت مطالب', + couldnt_update_post: 'ناتوای در به‌روزرسانی مطلب', + couldnt_save_post: 'ناتوانی در ذخیره مطلب.', + not_an_admin: 'مدیر نیستید.', + site_already_exists: 'این پایگاه از قبل وجود داشته است.', + couldnt_update_site: 'ناتوانی در به‌روزرسانی پایگاه.', + couldnt_find_that_username_or_email: + 'ناتوانی در یافتن این نام کاربری یا رایانامه.', + password_incorrect: 'گذرواژه نادرست.', + passwords_dont_match: 'گذرواژه‌ها با هم منطبق نیستند.', + user_already_exists: 'این کاربر از قبل وجود دارد.', + email_already_exists: 'این رایانامه از قبل وجود دارد.', + couldnt_update_user: 'ناتوانی در به‌روزرسانی کاربر.', + system_err_login: 'خطای سامانه. سعی کنید خارج شده و دوباره وارد شوید.', + }, +}; diff --git a/ui/src/translations/zh.ts b/ui/src/translations/zh.ts index 7beaf83e..52fdfabf 100644 --- a/ui/src/translations/zh.ts +++ b/ui/src/translations/zh.ts @@ -25,14 +25,14 @@ export const zh = { unlock: '解锁', lock: '加锁', link: '链接', - mod: 'mod', - mods: 'mods', - moderates: 'Moderates', - remove_as_mod: 'remove as mod', - appoint_as_mod: 'appoint as mod', - modlog: 'Modlog', - admin: 'admin', - admins: 'admins', + mod: '监管人', + mods: '监管人', + moderates: '监管', + remove_as_mod: '添加监管人', + appoint_as_mod: '移除监管人', + modlog: '监管记录', + admin: '管理权限', + admins: '管理权限', remove_as_admin: '移除管理权限', appoint_as_admin: '添加管理权限', remove: '移除', @@ -77,7 +77,7 @@ export const zh = { year: '年', all: '所有', top: '最热', - api: 'API', + api: '应用程式介面', inbox: '收件箱', inbox_for: '<1>{{user}} 收件箱', mark_all_as_read: '标记所有已读', @@ -98,7 +98,7 @@ export const zh = { email: '邮箱', optional: '选项', expires: '过期', - url: 'URL', + url: '网址', body: '内容', copy_suggested_title: '复制建议的标题: {{title}}', community: '节点', @@ -111,11 +111,11 @@ export const zh = { setup_admin: '设置管理员', your_site: '你的站点', modified: '修改', - sponsors: 'Sponsors', - sponsors_of_lemmy: 'Sponsors of Lemmy', + sponsors: '发起人', + sponsors_of_lemmy: 'Lemmy 的发起人', sponsor_message: 'Lemmy is free, <1>open-source software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project. Thank you to the following people:', - support_on_patreon: 'Support on Patreon', + support_on_patreon: '在 Patreon 赞助', general_sponsors: 'General Sponsors are those that pledged $10 to $39 to Lemmy.', crypto: '加密', @@ -139,8 +139,8 @@ export const zh = { couldnt_find_community: '不能找到节点.', couldnt_update_community: '不能更新节点.', community_already_exists: '节点已存在.', - community_moderator_already_exists: '节点 moderator 已存在.', - community_follower_already_exists: '节点 follower 已存在.', + community_moderator_already_exists: '节点监管人已存在.', + community_follower_already_exists: '节点追随者已存在.', community_user_already_banned: '节点用户已禁止.', couldnt_create_post: '不能创建帖子.', couldnt_like_post: '不能收藏帖子.', diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 0aec7624..0ec29114 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -9,6 +9,7 @@ import 'moment/locale/nl'; import 'moment/locale/it'; import 'moment/locale/fi'; import 'moment/locale/ca'; +import 'moment/locale/fa'; import { UserOperation, @@ -258,6 +259,7 @@ export const languages = [ { code: 'eo', name: 'Esperanto' }, { code: 'es', name: 'Español' }, { code: 'de', name: 'Deutsch' }, + { code: 'fa', name: 'فارسی' }, { code: 'zh', name: '中文' }, { code: 'fi', name: 'Suomi' }, { code: 'fr', name: 'Français' }, @@ -306,6 +308,8 @@ export function getMomentLanguage(): string { lang = 'fi'; } else if (lang.startsWith('ca')) { lang = 'ca'; + } else if (lang.startsWith('fa')) { + lang = 'fa'; } else { lang = 'en'; } diff --git a/ui/src/version.ts b/ui/src/version.ts index 2e0add0f..549688c7 100644 --- a/ui/src/version.ts +++ b/ui/src/version.ts @@ -1 +1 @@ -export const version: string = 'v0.6.5'; +export const version: string = 'v0.6.7'; diff --git a/ui/translation_report.ts b/ui/translation_report.ts index 56c09c96..1956b792 100644 --- a/ui/translation_report.ts +++ b/ui/translation_report.ts @@ -2,6 +2,7 @@ import { en } from './src/translations/en'; import { eo } from './src/translations/eo'; import { es } from './src/translations/es'; import { de } from './src/translations/de'; +import { fa } from './src/translations/fa'; import { zh } from './src/translations/zh'; import { fr } from './src/translations/fr'; import { sv } from './src/translations/sv'; @@ -15,6 +16,7 @@ import fs from 'fs'; const files = [ { t: ca, n: 'ca' }, { t: de, n: 'de' }, + { t: fa, n: 'fa' }, { t: eo, n: 'eo' }, { t: es, n: 'es' }, { t: fi, n: 'fi' }, diff --git a/ui/yarn.lock b/ui/yarn.lock index 6c83dd88..4d09380b 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -382,6 +382,11 @@ app-root-path@^2.0.1: resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a" integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA== +arg@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.2.tgz#e70c90579e02c63d80e3ad4e31d8bfdb8bd50064" + integrity sha512-+ytCkGcBtHZ3V2r2Z06AncYO8jz46UEamcspGoU8lHcEbpn6J77QK0vdWvChsclg/tM5XIJC5tnjmPp7Eq6Obg== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1036,6 +1041,11 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -2910,6 +2920,11 @@ loose-envify@^1.2.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +make-error@^1.1.1: + version "1.3.5" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" + integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== + map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -4123,7 +4138,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@~0.5.12: +source-map-support@^0.5.6, source-map-support@~0.5.12: version "0.5.16" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== @@ -4477,6 +4492,17 @@ tributejs@^4.1.1: resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-4.1.1.tgz#f169a4ad12e485241140ec1ab987b460950c974c" integrity sha512-jc+PcaiNzMjCn2LAQb3i4ic94EsSfLW8Jlk1sK2cb6hLcZFalU9ThcF8rxuKkTUKv1GIvTwN8XseLzCXLxB4lw== +ts-node@^8.6.2: + version "8.6.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35" + integrity sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg== + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.6" + yn "3.1.1" + ts-transform-classcat@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/ts-transform-classcat/-/ts-transform-classcat-0.0.2.tgz#2386c9418f3a7c1f03261ff51225b70d0a7664fb" @@ -4769,3 +4795,8 @@ yaml@^1.7.2: integrity sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw== dependencies: "@babel/runtime" "^7.6.3" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==